View Javadoc

1   // ========================================================================
2   // Copyright 2006 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.cometd;
16  
17  import java.util.ArrayList;
18  import java.util.List;
19  import java.util.Queue;
20  
21  import org.cometd.Bayeux;
22  import org.cometd.Client;
23  import org.cometd.ClientListener;
24  import org.cometd.DeliverListener;
25  import org.cometd.Extension;
26  import org.cometd.Message;
27  import org.cometd.MessageListener;
28  import org.cometd.QueueListener;
29  import org.cometd.RemoveListener;
30  import org.mortbay.util.ArrayQueue;
31  import org.mortbay.util.LazyList;
32  import org.mortbay.util.ajax.JSON;
33  
34  
35  
36  /* ------------------------------------------------------------ */
37  /**
38   * 
39   * @author gregw
40   */
41  public class ClientImpl implements Client
42  {
43      private String _id;
44      private String _type;
45      private int _responsesPending;
46      private ChannelImpl[] _subscriptions=new ChannelImpl[0]; // copy on write
47      private RemoveListener[] _rListeners; // copy on write
48      private MessageListener[] _syncMListeners; // copy on write
49      private MessageListener[] _asyncMListeners; // copy on write
50      private QueueListener[] _qListeners; // copy on write
51      private DeliverListener[] _dListeners; // copy on write
52      protected AbstractBayeux _bayeux;
53      private String _browserId;
54      private JSON.Literal _advice;
55      private int _batch;
56      private int _maxQueue;
57      private ArrayQueue<Message> _queue=new ArrayQueue<Message>(8,16,this);
58      private long _timeout;
59      protected Extension[] _extensions;
60      
61      protected boolean _deliverViaMetaConnectOnly;
62      
63      // manipulated and synchronized by AbstractBayeux
64      int _adviseVersion;
65  
66      /* ------------------------------------------------------------ */
67      protected ClientImpl(AbstractBayeux bayeux)
68      {
69          _bayeux=bayeux;
70          _maxQueue=bayeux.getMaxClientQueue();
71          _bayeux.addClient(this,null);
72          if (_bayeux.isLogInfo())
73              _bayeux.logInfo("newClient: "+this);
74      }
75      
76      /* ------------------------------------------------------------ */
77      protected ClientImpl(AbstractBayeux bayeux, String idPrefix)
78      {
79          _bayeux=bayeux;
80          _maxQueue=0;
81          
82          _bayeux.addClient(this,idPrefix);
83          
84          if (_bayeux.isLogInfo())
85              _bayeux.logInfo("newClient: "+this);
86      }
87  
88      /* ------------------------------------------------------------ */
89      public void addExtension(Extension ext)
90      {
91          _extensions = (Extension[])LazyList.addToArray(_extensions,ext,Extension.class);
92      }
93  
94      /* ------------------------------------------------------------ */
95      Extension[] getExtensions()
96      {
97          return _extensions;
98      }
99      
100     /* ------------------------------------------------------------ */
101     public void deliver(Client from, String toChannel, Object data, String id)
102     {
103         MessageImpl message=_bayeux.newMessage();
104         message.put(Bayeux.CHANNEL_FIELD,toChannel);
105         message.put(Bayeux.DATA_FIELD,data);
106         if (id!=null)   
107             message.put(Bayeux.ID_FIELD,id);
108 
109         Message m=_bayeux.extendSendBayeux(from,message);
110         if (m!=null)
111             doDelivery(from,m);
112         if (m instanceof MessageImpl)
113             ((MessageImpl)m).decRef();
114     }
115     
116     /* ------------------------------------------------------------ */
117     public void deliverLazy(Client from, String toChannel, Object data, String id)
118     {
119         MessageImpl message=_bayeux.newMessage();
120         message.put(Bayeux.CHANNEL_FIELD,toChannel);
121         message.put(Bayeux.DATA_FIELD,data);
122         if (id!=null)   
123             message.put(Bayeux.ID_FIELD,id);
124         message.setLazy(true);
125         Message m=_bayeux.extendSendBayeux(from,message);
126         if (m!=null)
127             doDelivery(from,m);
128         if (m instanceof MessageImpl)
129             ((MessageImpl)m).decRef();
130     }
131     
132     /* ------------------------------------------------------------ */
133     protected void doDelivery(Client from, final Message msg)
134     {
135         final Message message=_bayeux.extendSendClient(from,this,msg);
136         if (message==null)
137             return;
138         
139         MessageListener[] alisteners=null;
140         synchronized(this)
141         {
142             if (_maxQueue<0)
143             {
144                 ((MessageImpl)message).incRef();
145                 _queue.addUnsafe(message);
146             }
147             else
148             {
149                 boolean add;
150                 if (_queue.size()>=_maxQueue)
151                 {
152                     if (_qListeners!=null && _qListeners.length>0)
153                     {
154                         add=true;
155                         for (QueueListener l : _qListeners)
156                             add &= l.queueMaxed(from,this,message);
157                     }
158                     else
159                         add=false;
160                 }
161                 else
162                     add=true;
163                 
164                 if (add)
165                 {
166                     ((MessageImpl)message).incRef();
167                     _queue.addUnsafe(message);
168                 }
169             }    
170 
171             // deliver synchronized
172             if (_syncMListeners!=null)
173                 for (MessageListener l:_syncMListeners)
174                     l.deliver(from,this,message);
175             alisteners=_asyncMListeners;
176              
177             if (_batch==0 &&  _responsesPending<1 && _queue.size()>0 && !((MessageImpl)message).isLazy())
178                 resume();
179         }
180         
181         // deliver unsynchronized
182         if (alisteners!=null)
183             for (MessageListener l:alisteners)
184                 l.deliver(from,this,message);
185     }
186 
187     /* ------------------------------------------------------------ */
188     public void doDeliverListeners()
189     {
190         synchronized (this)
191         {
192             if (_dListeners!=null)
193                 for (DeliverListener l:_dListeners)
194                     l.deliver(this,_queue);
195         }
196     }
197 
198     /* ------------------------------------------------------------ */
199     public void setMetaConnectDeliveryOnly(boolean deliverViaMetaConnectOnly)
200     {
201         _deliverViaMetaConnectOnly = deliverViaMetaConnectOnly;
202     }
203     
204     /* ------------------------------------------------------------ */
205     public boolean isMetaConnectDeliveryOnly()
206     {
207         return _deliverViaMetaConnectOnly;
208     }
209     
210     /* ------------------------------------------------------------ */
211     public void startBatch()
212     {
213         synchronized(this)
214         {
215             _batch++;
216         }
217     }
218     
219     /* ------------------------------------------------------------ */
220     public void endBatch()
221     {
222         synchronized(this)
223         {
224             if (--_batch==0 && _responsesPending<1)
225             {
226                 switch(_queue.size())
227                 {
228                     case 0:
229                         break;
230                     case 1:
231                         if (!((MessageImpl)_queue.get(0)).isLazy())
232                             resume();
233                         break;
234                     default:
235                         // TODO check entire queue for non-lazy messages!
236                         resume();
237                 }
238             }
239         }
240     }
241     
242     /* ------------------------------------------------------------ */
243     public String getConnectionType()
244     {
245         return _type;
246     }
247     
248     /* ------------------------------------------------------------ */
249     /* (non-Javadoc)
250      * @see org.mortbay.cometd.C#getId()
251      */
252     public String getId()
253     {
254         return _id;
255     }
256    
257     /* ------------------------------------------------------------ */
258     public boolean hasMessages()
259     {
260         return _queue.size()>0;
261     }
262 
263     /* ------------------------------------------------------------ */
264     public boolean isLocal()
265     {
266         return true;
267     }
268 
269     /* ------------------------------------------------------------ */
270     /* (non-Javadoc)
271      * @see org.cometd.Client#disconnect()
272      */
273     public void disconnect()
274     {
275         synchronized(this)
276         {
277             if (_bayeux.hasClient(_id))
278                 remove(false);
279         }
280     }
281 
282     /* ------------------------------------------------------------ */
283     /* (non-Javadoc)
284      * @see dojox.cometd.Client#remove(boolean)
285      */
286     public void remove(boolean timeout)
287     {
288         Client client=_bayeux.removeClient(_id); 
289         
290         if (client!=null && _bayeux.isLogInfo())
291             _bayeux.logInfo("Remove client "+client+" timeout="+timeout); 
292         
293         final String browser_id;
294         final RemoveListener[] listeners;
295         synchronized(this)
296         {  
297             browser_id=_browserId;
298             _browserId=null;
299             listeners=_rListeners;
300         }
301 
302         if (browser_id!=null)
303             _bayeux.clientOffBrowser(browser_id,_id);
304         if (listeners!=null)
305             for (RemoveListener l:listeners)
306                 l.removed(_id, timeout);
307         
308         resume();
309     }
310     
311     /* ------------------------------------------------------------ */
312     public int responded()
313     {
314         synchronized(this)
315         {
316             return _responsesPending--;
317         }
318     }
319 
320     /* ------------------------------------------------------------ */
321     public int responsePending()
322     {
323         synchronized(this)
324         {
325             return ++_responsesPending;
326         }
327     }
328     
329     /* ------------------------------------------------------------ */
330     /** Called by deliver to resume anything waiting on this client.
331      */
332     public void resume()
333     {
334     }
335     
336     /* ------------------------------------------------------------ */
337     /*
338      * @return the number of messages queued
339      */
340     public int getMessages()
341     {
342         return _queue.size();
343     }
344     
345     /* ------------------------------------------------------------ */
346     public List<Message> takeMessages()
347     {
348         synchronized(this)
349         {
350             ArrayList<Message> list = new ArrayList<Message>(_queue);
351             _queue.clear();
352             return list;
353         }
354     }
355     
356 
357     /* ------------------------------------------------------------ */
358     public void returnMessages(List<Message> messages)
359     {
360         synchronized(this)
361         {
362             _queue.addAll(0,messages);
363         }
364     }
365         
366     /* ------------------------------------------------------------ */
367     @Override
368     public String toString()
369     {
370         return _id;
371     }
372 
373     /* ------------------------------------------------------------ */
374     protected void addSubscription(ChannelImpl channel)
375     {
376         synchronized (this)
377         {
378             _subscriptions=(ChannelImpl[])LazyList.addToArray(_subscriptions,channel,null);
379         }
380     }
381 
382     /* ------------------------------------------------------------ */
383     protected void removeSubscription(ChannelImpl channel)
384     {
385         synchronized (this)
386         {
387             _subscriptions=(ChannelImpl[])LazyList.removeFromArray(_subscriptions,channel);
388         }
389     }
390 
391     /* ------------------------------------------------------------ */
392     protected void setConnectionType(String type)
393     {
394         synchronized (this)
395         {
396             _type=type;
397         }
398     }
399 
400     /* ------------------------------------------------------------ */
401     protected void setId(String _id)
402     {
403         synchronized (this)
404         {
405             this._id=_id;
406         }
407     }
408 
409     /* ------------------------------------------------------------ */
410     protected void unsubscribeAll()
411     {
412         ChannelImpl[] subscriptions;
413         synchronized(this)
414         {
415             _queue.clear();
416             subscriptions=_subscriptions;
417             _subscriptions=new ChannelImpl[0];
418         }
419         for (ChannelImpl channel : subscriptions)
420             channel.unsubscribe(this);
421         
422     }
423 
424     /* ------------------------------------------------------------ */
425     public void setBrowserId(String id)
426     {
427         if (_browserId!=null && !_browserId.equals(id))
428             _bayeux.clientOffBrowser(_browserId,_id);
429         _browserId=id;
430         if (_browserId!=null)
431             _bayeux.clientOnBrowser(_browserId,_id);
432     }
433 
434     /* ------------------------------------------------------------ */
435     public String getBrowserId()
436     {
437         return _browserId;
438     }
439 
440     /* ------------------------------------------------------------ */
441     @Override
442     public boolean equals(Object o)
443     {
444     	if (!(o instanceof Client))
445     		return false;
446     	return getId().equals(((Client)o).getId());
447     }
448 
449     /* ------------------------------------------------------------ */
450     /**
451      * Get the advice specific for this Client
452      * @return advice specific for this client or null
453      */
454     public JSON.Literal getAdvice()
455     {
456     	return _advice;
457     }
458 
459     /* ------------------------------------------------------------ */
460     /**
461      * @param advice specific for this client
462      */
463     public void setAdvice(JSON.Literal advice)
464     {
465     	_advice=advice;
466     }
467     
468 
469     /* ------------------------------------------------------------ */
470     public void addListener(ClientListener listener)
471     {
472         synchronized(this)
473         {
474             if (listener instanceof MessageListener)
475             {
476                 if (listener instanceof MessageListener.Synchronous)
477                     _syncMListeners=(MessageListener[])LazyList.addToArray(_syncMListeners,listener,MessageListener.class);
478                 else
479                     _asyncMListeners=(MessageListener[])LazyList.addToArray(_asyncMListeners,listener,MessageListener.class);
480             }
481 
482             if (listener instanceof RemoveListener)
483                 _rListeners=(RemoveListener[])LazyList.addToArray(_rListeners,listener,RemoveListener.class);
484 
485             if (listener instanceof QueueListener)
486                 _qListeners=(QueueListener[])LazyList.addToArray(_qListeners,listener,QueueListener.class);
487 
488             if (listener instanceof DeliverListener)
489                 _dListeners=(DeliverListener[])LazyList.addToArray(_dListeners,listener,DeliverListener.class);
490         }
491     }
492 
493     /* ------------------------------------------------------------ */
494     public void removeListener(ClientListener listener)
495     {
496         synchronized(this)
497         {
498             if (listener instanceof MessageListener)
499             {
500                 _syncMListeners=(MessageListener[])LazyList.removeFromArray(_syncMListeners,listener);
501                 _asyncMListeners=(MessageListener[])LazyList.removeFromArray(_asyncMListeners,listener);
502             }
503 
504             if (listener instanceof RemoveListener)
505                 _rListeners=(RemoveListener[])LazyList.removeFromArray(_rListeners,listener);
506 
507             if (listener instanceof QueueListener)
508                 _qListeners=(QueueListener[])LazyList.removeFromArray(_qListeners,listener);
509         }
510     }
511 
512     /* ------------------------------------------------------------ */
513     public long getTimeout() 
514     {
515     	return _timeout;
516     }
517 
518     /* ------------------------------------------------------------ */
519     public void setTimeout(long timeoutMS) 
520     {
521     	_timeout=timeoutMS;
522     }
523 
524     /* ------------------------------------------------------------ */
525     public void setMaxQueue(int maxQueue)
526     {
527         _maxQueue=maxQueue;
528     }
529     
530     /* ------------------------------------------------------------ */
531     public int getMaxQueue()
532     {
533         return _maxQueue;
534     }
535     
536     /* ------------------------------------------------------------ */
537     public Queue<Message> getQueue()
538     {
539         return _queue;
540     }
541 }