<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 156,  comments - 601,  trackbacks - 0
                                Jetty cometd(Continuation)學(xué)習(xí)筆記

    關(guān)鍵字: Jetty, Cometd, Continuation, dojo

    前言:
    為了更容易的掌握J(rèn)etty cometd的使用方法,本筆記通過講解Jetty 6.0自帶的chat演示程序來輔助講解Jetty Cometd的實(shí)現(xiàn)。


    環(huán)境配置方法:
    服務(wù)器端:
        類庫清單:WEB-INF/lib
            jetty-6.1.9.jar
            jetty-util-6.1.9.jar
            servlet-api-2.5-6.1.9.jar
            (以上Jetty服務(wù)器自帶)
            cometd-api-0.9.20080221.jar
            cometd-bayeux-6.1.9.jar

        web.xml配置:
         
       <!-- 配置ContinuationCometdServlet, 這個(gè)是必須的。配置后就可以支持comted -->
      
    <servlet>
        
    <servlet-name>cometd</servlet-name>
        
    <servlet-class>org.mortbay.cometd.continuation.ContinuationCometdServlet</servlet-class>
        
    <!-- 對(duì)隊(duì)列的內(nèi)容進(jìn)行過濾 -->
        
    <init-param>
          
    <param-name>filters</param-name>
          
    <param-value>/WEB-INF/filters.json</param-value>
        
    </init-param>
        
    <!-- 超時(shí)設(shè)置The server side poll timeout in milliseconds (default 250000). This is how long the server will
    hold a reconnect request before responding. 
    -->
        
    <init-param>
          
    <param-name>timeout</param-name>
          
    <param-value>120000</param-value>
        
    </init-param>
        
    <!-- The client side poll timeout in milliseconds (default 0). How long a client will wait between
    reconnects 
    -->
        
    <init-param>
          
    <param-name>interval</param-name>
          
    <param-value>0</param-value>
        
    </init-param>
        
    <!-- the client side poll timeout if multiple connections are detected from the same browser
    (default 1500). 
    -->
        
    <init-param>
          
    <param-name>multiFrameInterval</param-name>
          
    <param-value>1500</param-value>
        
    </init-param>
        
    <!-- 0=none, 1=info, 2=debug -->
        
    <init-param>
          
    <param-name>logLevel</param-name>
          
    <param-value>0</param-value>
        
    </init-param>
        
    <!-- If "true" then the server will accept JSON wrapped in a comment and will generate JSON wrapped
    in a comment. This is a defence against Ajax Hijacking. 
    -->
        
    <init-param>
          
    <param-name>JSONCommented</param-name>
          
    <param-value>true</param-value>
        
    </init-param>

        
    <init-param>
          
    <param-name>alwaysResumePoll</param-name>
          
    <param-value>false</param-value> <!-- use true for x-site cometd -->
        
    </init-param>
        
    <load-on-startup>1</load-on-startup>
      
    </servlet>

      
    <servlet-mapping>
        
    <servlet-name>cometd</servlet-name>
        
    <url-pattern>/cometd/*</url-pattern>
      
    </servlet-mapping> 

    filters.json內(nèi)容如下:

    格式如下:
        {
            "channels": "/**", --要過濾的隊(duì)列(支持通配符)
            "filter":"org.mortbay.cometd.filter.NoMarkupFilter", --使用的過濾器,實(shí)現(xiàn)接口dojox.cometd.DataFilter
            "init"    : {} --初始化的值,調(diào)用 DataFilter.init方法傳入
        }

    示例內(nèi)容如下:

    [
      {
        
    "channels""/**",
        
    "filter"  : "org.mortbay.cometd.filter.NoMarkupFilter",
        
    "init"    : {}
      }
    ,

      {
        
    "channels""/chat/*",
        
    "filter"   : "org.mortbay.cometd.filter.RegexFilter",
        
    "init"    : [
                      [ "[fF].ck","dang" ],
                      [ 
    "teh ","the "]
                    ]
      },
     
      {
        
    "channels""/chat/**",
        
    "filter"   : "org.mortbay.cometd.filter.RegexFilter",
        
    "init"    : [
                      [ 
    "[Mm]icrosoft""Micro\\$oft" ],
                      [ 
    ".*tomcat.*", null ]
                    ]
      }
    ]



    這時(shí),服務(wù)器端的配置就已經(jīng)完成的,基本的cometd功能就可以使用了。
    客戶端通過dojox.cometd.init("http://127.0.0.2:8080/cometd");就可以進(jìn)行連接。

    代碼開發(fā):

    接下來,我們要準(zhǔn)備客戶端(使用dojo來實(shí)現(xiàn))

    一共三個(gè)文件
    index.html
    chat.js
    chat.css(不是必須)

    下面來看一下這兩個(gè)文件的內(nèi)容(加入注釋)
    index.html

    <html>
    <head>
        
    <title>Cometd chat</title>
        
    <script type="text/javascript" src="../dojo/dojo/dojo.js"></script><!-- dojo類庫 -->
        
    <script type="text/javascript" src="../dojo/dojox/cometd.js.uncompressed.js"></script><!-- dojo-cometd類庫 -->
        
    <script type="text/javascript" src="chat.js"></script><!-- chat js文件,控制cometd的連接,消息的發(fā)送與接收 -->
        
    <link rel="stylesheet" type="text/css" href="chat.css">
    </head>
    <body>
    <h1>Cometd Chat</h1>

    <div id="chatroom">
     
    <div id="chat"></div>
     
    <div id="input">
       
    <div id="join" ><!-- 未登錄時(shí),顯示的登錄名和登錄按鈕 -->
         Username:
    &nbsp;<input id="username" type="text"/>
    <
    input id="joinB" class="button" type="submit" name="join" value="Join"/>
       
    </div>
       
    <div id="joined" class="hidden"><!-- 登錄后,顯示的消息框和發(fā)送,退出按鈕(默認(rèn)為隱藏) -->
         Chat:
    &nbsp;<input id="phrase" type="text"></input>
         
    <input id="sendB" class="button" type="submit" name="join" value="Send"/>
         
    <input id="leaveB" class="button" type="submit" name="join" value="Leave"/>
       
    </div>
      
    </div>
     
    </div>

    </body>

    chat.js文件
      1 //引入所需要的類
      2 dojo.require("dojox.cometd");
      3 dojo.require("dojox.cometd.timestamp");
      4 
      5 //定義一個(gè)room類
      6 var room = {
      7     //定義屬性
      8     _last: ""//最后發(fā)送消息的人員(如果不是本人,則顯示為空) 
      9     _username: null//當(dāng)前的用戶名
     10     _connected: true//當(dāng)前的連接狀態(tài) true已經(jīng)連接, false表示未連接
     11     groupName: "whimsical"//組名(未知)
     12 
     13     //登錄操作
     14     join: function(name){
     15 
     16         if(name == null || name.length==0 ){
     17             alert('Please enter a username!');
     18         }else{
     19 
     20             dojox.cometd.init(
    new String(document.location).replace(/http:\/\/[^\/]*/,'').replace(/\/examples\/.*$/,'')+"/cometd");
     21             // dojox.cometd.init("http://127.0.0.2:8080/cometd");
     22             this._connected = true;
     23 
     24             this._username = name;
     25             dojo.byId('join').className='hidden';
     26             dojo.byId('joined').className='';
     27             dojo.byId('phrase').focus();
     28 
     29             // subscribe and join
     30             dojox.cometd.startBatch();
     31             dojox.cometd.subscribe("/chat/demo", room, "_chat", { groupName: this.groupName});
     32             dojox.cometd.publish("/chat/demo", { 
     33                 user: room._username,
     34                 join: true,
     35                 chat : room._username+" has joined"
     36             }, { groupName: this.groupName });
     37             dojox.cometd.endBatch();
     38 
     39             // handle cometd failures while in the room
     40             room._meta = dojo.subscribe("/cometd/meta"thisfunction(event){
     41                 console.debug(event);   
     42                 if(event.action == "handshake"){
     43                     room._chat({ data: {
     44                         join: true,
     45                         user:"SERVER",
     46                         chat:"reinitialized"
     47                     } });
     48                     dojox.cometd.subscribe("/chat/demo", room, "_chat", { groupName: this.groupName });
     49                 }else if(event.action == "connect"){
     50                     if(event.successful && !this._connected){
     51                         room._chat({ data: {
     52                             leave: true,
     53                             user: "SERVER",
     54                             chat: "reconnected!"
     55                         } });
     56                     }
     57                     if(!event.successful && this._connected){
     58                         room._chat({ data: {
     59                             leave: true,
     60                             user: "SERVER",
     61                             chat: "disconnected!"
     62                         } });
     63                     }
     64                     this._connected = event.successful;
     65                 }
     66             }, {groupName: this.groupName });
     67         }
     68     },
     69 
     70     //離開操作
     71     leave: function(){
     72         if(!room._username){
     73             return;
     74         }
     75 
     76         if(room._meta){
     77             dojo.unsubscribe(room._meta, nullnull, { groupName: this.groupName });
     78         }
     79         room._meta=null;
     80 
     81         dojox.cometd.startBatch();
     82         dojox.cometd.unsubscribe("/chat/demo", room, "_chat", { groupName: this.groupName });
     83         dojox.cometd.publish("/chat/demo", { 
     84             user: room._username,
     85             leave: true,
     86             chat : room._username+" has left"
     87         }, { groupName: this.groupName });
     88         dojox.cometd.endBatch();
     89 
     90         // switch the input form
     91         dojo.byId('join').className='';
     92         dojo.byId('joined').className='hidden';
     93         dojo.byId('username').focus();
     94         room._username = null;
     95         dojox.cometd.disconnect();
     96     },
     97 
     98     //發(fā)送消息
     99     chat: function(text){
    100         if(!text || !text.length){
    101             return false;
    102         }
    103         dojox.cometd.publish("/chat/demo", { user: room._username, chat: text}, { groupName: this.groupName });
    104     },
    105 
    106     //從服務(wù)器收到消息后,回調(diào)的方法
    107     _chat: function(message){
    108         var chat=dojo.byId('chat');
    109         if(!message.data){
    110             console.debug("bad message format "+message);
    111             return;
    112         }
    113         var from=message.data.user;
    114         var special=message.data.join || message.data.leave;
    115         var text=message.data.chat;
    116         if(!text){ return; }
    117 
    118         if!special && from == room._last ){
    119             from="";
    120         }else{
    121             room._last=from;
    122             from+=":";
    123         }
    124 
    125         if(special){
    126             chat.innerHTML += "<span class=\"alert\"><span class=\"from\">"+from+"&nbsp;
    </span><span class=\
    "text\">"+text+"</span></span><br/>";
    127             room._last="";
    128         }else{
    129             chat.innerHTML += "<span class=\"from\">"+from+"&nbsp;</span><span class=\"text\">"+text+"</span><br/>";
    130         } 
    131         chat.scrollTop = chat.scrollHeight - chat.clientHeight;    
    132     },
    133     
    134     //初始操作
    135     _init: function(){
    136         dojo.byId('join').className='';
    137         dojo.byId('joined').className='hidden';
    138         dojo.byId('username').focus();
    139 
    140         var element=dojo.byId('username');
    141         element.setAttribute("autocomplete","OFF"); 
    142         dojo.connect(element, "onkeyup"function(e){ //支持回車,登錄  
    143             if(e.keyCode == dojo.keys.ENTER){
    144                 room.join(dojo.byId('username').value);
    145                 return false;
    146             }
    147             return true;
    148         });
    149 
    150         dojo.connect(dojo.byId('joinB'), "onclick"function(e){ //綁定 room.join方法到 Join按扭
    151             room.join(dojo.byId('username').value);
    152             e.preventDefault();
    153         });
    154 
    155         element=dojo.byId('phrase');//取得消息框
    156         element.setAttribute("autocomplete","OFF");
    157         dojo.connect(element, "onkeyup"function(e){ //支持回車發(fā)送消息 
    158             if(e.keyCode == dojo.keys.ENTER){
    159                 room.chat(dojo.byId('phrase').value);
    160                 dojo.byId('phrase').value='';
    161                 e.preventDefault();
    162             }
    163         });
    164 
    165     dojo.connect(dojo.byId('sendB'), "onclick"function(e){  //綁定 room.chat方法到 sendB按扭 
    166             room.chat(dojo.byId('phrase').value);
    167             dojo.byId('phrase').value='';
    168     });
    169         dojo.connect(dojo.byId('leaveB'), "onclick", room, "leave"); //綁定 room.leave方法到 leaveB按扭 
    170     } 
    171 };
    172 
    173 //頁面裝載時(shí),調(diào)用room._init方法
    174 dojo.addOnLoad(room, "_init");
    175 //頁面關(guān)閉時(shí),調(diào)用 room.leave方法
    176 dojo.addOnUnload(room,"leave");
    177 
    178 //vim:ts=4:noet:

    補(bǔ)充:服務(wù)器端如何監(jiān)控消息隊(duì)列,以及進(jìn)行訂閱,發(fā)送消息操作

    要進(jìn)行 監(jiān)控消息隊(duì)列,以及進(jìn)行訂閱,發(fā)送消息操作的關(guān)鍵就是取得 Bayeux接口實(shí)現(xiàn)類 的實(shí)例

    可以通過 ServletContextAttributeListener 這個(gè)監(jiān)聽器接口,通過attributeAdded方式加入

    實(shí)現(xiàn)方法如下:

     1 public class BayeuxStartupListener implements ServletContextAttributeListener
     2 {
     3     public void initialize(Bayeux bayeux)
     4     {
     5         synchronized(bayeux)
     6         {
     7             if (!bayeux.hasChannel("/service/echo"))
     8             {
     9                                 //取得 bayeux實(shí)例               
    10             }
    11         }
    12     }
    13     
    14     public void attributeAdded(ServletContextAttributeEvent scab)
    15     {
    16         if (scab.getName().equals(Bayeux.DOJOX_COMETD_BAYEUX))
    17         {
    18             Bayeux bayeux=(Bayeux) scab.getValue();
    19             initialize(bayeux);
    20         }
    21     }
    22 
    23     public void attributeRemoved(ServletContextAttributeEvent scab)
    24     {
    25 
    26     }
    27 
    28     public void attributeReplaced(ServletContextAttributeEvent scab)
    29     {
    30 
    31     }
    32 }

    取到 Bayeux實(shí)例后,就可以借助BayeuxService類幫我們實(shí)現(xiàn)消息隊(duì)列的監(jiān)聽,訂閱消息以及發(fā)送消息
     1     public void initialize(Bayeux bayeux)
     2     {
     3         synchronized(bayeux)
     4         {
     5             if (!bayeux.hasChannel("/service/echo"))
     6             {
     7                                 //取得 bayeux實(shí)例  
     8                                 new ChatService(bayeux);             
     9             }
    10         }
    11     }

    具體方法請(qǐng)看下面這段代碼:
     1 //定義 ChatService類,繼承 BayeuxService
     2 public static class ChatService extends BayeuxService {
     3 
     4     ConcurrentMap<String,Set<String>> _members = new ConcurrentHashMap<String,Set<String>>();
     5     
     6     public ChatService(Bayeux bayeux)
     7     {
     8         super(bayeux, "chat");//必須,把 Bayeux傳入到 BayeuxService對(duì)象中
     9         subscribe("/chat/**""trackMembers"); //訂閱隊(duì)列,收到消息后,會(huì)回調(diào)trackMembers方法
    10         /*
    11             subscribe支持回調(diào)的方法如下:
    12             # myMethod(Client fromClient, Object data)
    13                     # myMethod(Client fromClient, Object data, String id)
    14                     # myMethod(Client fromClient, String channel, Object data,String id)
    15             # myMethod(Client fromClient, Message message)
    16             
    17                 參數(shù):
    18                     Client fromClient 發(fā)送消息的客戶端
    19                     Object data 消息內(nèi)容
    20                     id The id of the message 
    21                     channel 隊(duì)列名稱
    22                     Message message 消息對(duì)象。繼承于Map
    23         
    24         */
    25     }
    26     
    27     //發(fā)布消息到隊(duì)列
    28     public void sendMessage(String message) {
    29             Map<String,Object> mydata = new HashMap<String, Object>();
    30             mydata.put("chat", message);
    31             
    32             Client sender = getBayeux().newClient("server");
    33             
    34             getBayeux().getChannel("/chat/demo"false).publish(sender, mydata, "0"/*null*/);
    35 
    36     }
    37     
    38     //發(fā)送消息給指定的client(非廣播方式)
    39     public void sendMessageToClient(Client joiner, String message) {
    40             Map<String,Object> mydata = new HashMap<String, Object>();
    41             mydata.put("chat", message);
    42            
    43             send(joiner, "/chat/demo", mydata, "0"/*null*/);
    44     }    
    45     
    46     //訂閱消息回調(diào)方法
    47     public void trackMembers(Client joiner, String channel, Map<String,Object> data, String id)
    48     {
    49             //解釋消息內(nèi)容,如果消息內(nèi)容中 有 join這個(gè)字段且值為true
    50         if (Boolean.TRUE.equals(data.get("join")))
    51         {
    52                 //根據(jù)隊(duì)列,取得當(dāng)前登錄的人員
    53             Set<String> m = _members.get(channel);
    54             if (m==null)
    55             {
    56                     //如果為空,則創(chuàng)建一個(gè)新的Set實(shí)現(xiàn)
    57                 Set<String> new_list=new CopyOnWriteArraySet<String>();
    58                 m=_members.putIfAbsent(channel,new_list);
    59                 if (m==null)
    60                     m=new_list;
    61             }
    62             
    63             final Set<String> members=m;
    64             final String username=(String)data.get("user");
    65             
    66             members.add(username);
    67                         //為該client增加事件,Remove事件。當(dāng)用戶退出時(shí),觸發(fā)該方法。            
    68             joiner.addListener(new RemoveListener(){
    69                 public void removed(String clientId, boolean timeout)
    70                 {
    71                     members.remove(username);
    72                 }
    73             });
    74 
    75                         //為該client增加事件,消息的發(fā)送和接收事件。當(dāng)用戶退出時(shí),觸發(fā)該方法。
    76             joiner.addListener(new MessageListener() {
    77                                 public void deliver(Client fromClient, Client toClient, Message message) {
    78                                     System.out.println("message from " + fromClient.getId() + " to "
    79                                             + toClient.getId() + " message is " + message.getData());
    80                                 }      
    81             });
    82 
    83             Map<String,Object> mydata = new HashMap<String, Object>();
    84             mydata.put("chat""members=" + members);
    85             //把已經(jīng)登錄的人員信息列表,發(fā)送回給消息發(fā)送者
    86             send(joiner,channel,mydata,id);
    87          
    88         }
    89     }
    90 }
    91 

     附:部分使用到的API文檔

    dojox.cometd.subscribe

    dojo.require("dojox.cometd._base");
    defined in dojox/cometd/_base.js

    dojox.cometd.subscribe() handles all the hard work of telling the server that we want to be notified when events are published on a particular topic. subscribe accepts a function to handle messages and returns a dojo.Deferred object which has an extra property added to it which makes it suitable for passing to dojox.cometd.unsubscribe() as a “subscription handle” (much like the handle object that dojo.connect() produces and which dojo.disconnect() expects).

    Note that of a subscription is registered before a connection with the server is established, events sent before the connection is established will not be delivered to this client. The deferred object which subscribe returns will callback when the server successfuly acknolwedges receipt of our “subscribe” request.

    Usage

    var foo=dojox.cometd.subscribe(channel: String, objOrFunc: Object, funcName: String, props: Object?); (view source)
    parametertypedescription
    channel String  
    objOrFunc Object an object scope for funcName or the name or reference to a function to be called when messages are delivered to the
    funcName String the second half of the objOrFunc/funcName pair for identifying a callback function to notifiy upon channel message delivery
    props Object Optional.

    Examples

    Example 1

    Simple subscribe use-case

    dojox.cometd.init("http://myserver.com:8080/cometd");
    // log out all incoming messages on /foo/bar
    dojox.cometd.subscribe("/foo/bar", console, "debug");

    Example 2

    Subscribe before connection is initialized

    dojox.cometd.subscribe("/foo/bar", console, "debug");
    dojox.cometd.init("http://myserver.com:8080/cometd");

    Example 3

    Subscribe an unsubscribe

    dojox.cometd.init("http://myserver.com:8080/cometd");
    var h = dojox.cometd.subscribe("/foo/bar", console, "debug");
    dojox.cometd.unsubscribe(h);

    Example 4

    Listen for successful subscription:

    dojox.cometd.init("http://myserver.com:8080/cometd");
    var h = dojox.cometd.subscribe("/foo/bar", console, "debug");
    h.addCallback(function(){
    console.debug("subscription to /foo/bar established");
    });

    dojox.cometd.publish

    dojo.require("dojox.cometd._base");
    defined in dojox/cometd/_base.js
    publishes the passed message to the cometd server for delivery on the specified topic

    Usage

    var foo=dojox.cometd.publish(channel: String, data: Object, props: Object?); (view source)
    parametertypedescription
    channel String the destination channel for the message
    data Object a JSON object containing the message "payload" properties: Optional. Other meta-data to be mixed into the top-level of the message
    props Object Optional.





    Good Luck!
    Yours Matthew!
    posted on 2008-11-20 20:04 x.matthew 閱讀(13938) 評(píng)論(10)  編輯  收藏 所屬分類: Rest

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 日本无卡码免费一区二区三区| 亚洲人成人网站在线观看| 亚洲大尺度无码专区尤物| 亚洲日韩中文字幕一区| 免费国产午夜高清在线视频 | 性感美女视频免费网站午夜| 中文字幕在线亚洲精品| 亚洲1区2区3区精华液| 日本视频一区在线观看免费| 亚洲精品乱码久久久久久按摩| 午夜亚洲福利在线老司机| 亚洲欧洲精品国产区| 久久精品网站免费观看| 亚洲国产精品高清久久久| 亚洲日本一线产区和二线| 中文无码成人免费视频在线观看 | 亚洲美女视频一区二区三区| 成全视频在线观看免费| 亚洲熟妇无码AV在线播放| 无码 免费 国产在线观看91| 全亚洲最新黄色特级网站| 国产成人综合久久精品亚洲| 国产精品色午夜视频免费看 | 国产精品成人免费一区二区| 亚洲人成网站18禁止久久影院| 永久免费视频网站在线观看| 精品日韩亚洲AV无码一区二区三区| 免费人成毛片动漫在线播放 | 亚洲乱亚洲乱妇无码| 最近中文字幕无吗高清免费视频| 亚洲天堂男人影院| 无码永久免费AV网站| 亚洲精品人成网在线播放影院| 久久精品网站免费观看| 亚洲日韩一区二区三区| 黄a大片av永久免费| 精品久久久久久亚洲综合网| 人人狠狠综合久久亚洲高清| 国产激情久久久久影院老熟女免费| 亚洲国产小视频精品久久久三级 | 最近2022中文字幕免费视频|