View Javadoc

1   package org.mortbay.cometd.ext;
2   
3   import java.util.HashMap;
4   import java.util.Iterator;
5   import java.util.Map;
6   import java.util.Queue;
7   
8   import javax.servlet.UnavailableException;
9   
10  import org.cometd.Bayeux;
11  import org.cometd.Client;
12  import org.cometd.Extension;
13  import org.cometd.Message;
14  import org.mortbay.cometd.MessageImpl;
15  import org.mortbay.util.ArrayQueue;
16  
17  /**
18   * Acknowledged Message Client extension.
19   * 
20   * Tracks the batch id of messages sent to a client.
21   *
22   */
23  public class AcknowledgedMessagesClientExtension implements Extension
24  {
25      private final Client _client;
26      private final ArrayIdQueue<Message> _unackedQueue;
27      
28      public AcknowledgedMessagesClientExtension(Client client)
29      {
30          _client=client;
31          _unackedQueue=new ArrayIdQueue<Message>(8,16,client);
32          _unackedQueue.setCurrentId(1);
33      }
34      
35      public Message rcv(Client from, Message message)
36      {
37          return message;
38      }
39  
40      /**
41       * Handle received meta messages.
42       * Looks for meta/connect messages with ext/ack fields.
43       * If present, delete all messages that have been acked and requeue messages that
44       * have not been acked.
45       */
46      public Message rcvMeta(Client from, Message message)
47      {      
48          if (message.getChannel().equals(Bayeux.META_CONNECT))
49          {  
50              synchronized (_client)
51              {
52                  Map<String, Object> ext=message.getExt(false);
53                  if (ext != null)
54                  {
55                      Long acked=(Long) ext.get("ack");
56                      if (acked != null)
57                      {
58                          // We have received an ack ID, so delete the acked messages.
59                          final int s=_unackedQueue.size();
60                          if (s>0)
61                          {
62                              if (_unackedQueue.getAssociatedIdUnsafe(s-1)<=acked)
63                              {
64                                  // we can just clear the queue
65                                  for (int i=0;i<s;i++)
66                                  {
67                                      final Message q = _unackedQueue.getUnsafe(i);
68                                      if (q instanceof MessageImpl)
69                                          ((MessageImpl)q).decRef();
70                                  }
71                                  _unackedQueue.clear();
72                              }
73                              else
74                              {
75                                  // we need to remove elements until we see unacked
76                                  for (int i=0;i<s;i++)
77                                  {
78                                      if (_unackedQueue.getAssociatedIdUnsafe(0)<=acked)
79                                      {
80                                          final Message q = _unackedQueue.remove();
81                                          if (q instanceof MessageImpl)
82                                              ((MessageImpl)q).decRef();
83                                          continue;
84                                      }
85                                      break;
86                                  }
87                              }
88                          }
89                      }
90                  }
91  
92                  // requeue all unacked messages.
93                  final ArrayQueue<Message> messages=(ArrayQueue)from.getQueue();
94                  final int cid=_unackedQueue.getCurrentId();
95                  final int s=_unackedQueue.size();
96                  for (int i=0;i<s;i++)
97                  {
98                      if (_unackedQueue.getAssociatedIdUnsafe(0)<cid)
99                          messages.add(i,_unackedQueue.remove());
100                     else
101                         break;
102                 }
103             }
104         }
105 
106         return message;
107     }
108 
109     public Message send(Client from, Message message)
110     {
111         synchronized (_client)
112         {
113             _unackedQueue.add(message);
114             // prevent the message from being erased
115             ((MessageImpl) message).incRef();
116         }
117         return message;
118     }
119 
120     public Message sendMeta(Client from, Message message)
121     {
122         if ( message.getChannel().equals(Bayeux.META_CONNECT) )
123         {
124             synchronized (_client)
125             {
126                 Map<String, Object> ext= message.getExt(true);
127                 ext.put("ack",_unackedQueue.getCurrentId());
128                 _unackedQueue.incrementCurrentId();
129             }
130         }
131         return message;
132     }
133 }