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         // TODO recycle maps
104         Message message=_bayeux.newMessage();
105         message.put(Bayeux.CHANNEL_FIELD,toChannel);
106         message.put(Bayeux.DATA_FIELD,data);
107         if (id!=null)   
108             message.put(Bayeux.ID_FIELD,id);
109 
110         message=_bayeux.extendSendBayeux(from,message);
111         if (message!=null)
112             doDelivery(from,message);
113         
114         ((MessageImpl)message).decRef();
115     }
116     
117     /* ------------------------------------------------------------ */
118     protected void doDelivery(Client from, Message message)
119     {
120         message=_bayeux.extendSendClient(from,this,message);
121         if (message==null)
122             return;
123         
124         MessageListener[] alisteners=null;
125         synchronized(this)
126         {
127             ((MessageImpl)message).incRef();
128             
129             if (_maxQueue<0)
130             {
131                 _queue.addUnsafe(message);
132             }
133             else
134             {
135                 boolean add;
136                 if (_queue.size()>=_maxQueue)
137                 {
138                     if (_qListeners!=null && _qListeners.length>0)
139                     {
140                         add=true;
141                         for (QueueListener l : _qListeners)
142                             add &= l.queueMaxed(from,this,message);
143                     }
144                     else
145                         add=false;
146                 }
147                 else
148                     add=true;
149                 
150                 if (add)
151                     _queue.addUnsafe(message);
152             }    
153 
154             // deliver unsynchronized
155             if (_syncMListeners!=null)
156                 for (MessageListener l:_syncMListeners)
157                     l.deliver(from,this,message);
158             alisteners=_asyncMListeners;
159              
160             if (_batch==0 &&  _responsesPending<1 && _queue.size()>0)
161                 resume();
162         }
163         
164         // deliver unsynchronized
165         if (alisteners!=null)
166             for (MessageListener l:alisteners)
167                 l.deliver(from,this,message);
168     }
169 
170     /* ------------------------------------------------------------ */
171     public void doDeliverListeners()
172     {
173         synchronized (this)
174         {
175             if (_dListeners!=null)
176                 for (DeliverListener l:_dListeners)
177                     l.deliver(this,_queue);
178         }
179     }
180 
181     /* ------------------------------------------------------------ */
182     public void setDeliverViaMetaConnectOnly(boolean deliverViaMetaConnectOnly)
183     {
184         _deliverViaMetaConnectOnly = deliverViaMetaConnectOnly;
185     }
186     
187     /* ------------------------------------------------------------ */
188     public boolean isDeliverViaMetaConnectOnly()
189     {
190         return _deliverViaMetaConnectOnly;
191     }
192     
193     /* ------------------------------------------------------------ */
194     public void startBatch()
195     {
196         synchronized(this)
197         {
198             _batch++;
199         }
200     }
201     
202     /* ------------------------------------------------------------ */
203     public void endBatch()
204     {
205         synchronized(this)
206         {
207             if (--_batch==0 && _queue.size()>0 && _responsesPending<1)
208                 resume();
209         }
210     }
211     
212     /* ------------------------------------------------------------ */
213     public String getConnectionType()
214     {
215         return _type;
216     }
217     
218     /* ------------------------------------------------------------ */
219     /* (non-Javadoc)
220      * @see org.mortbay.cometd.C#getId()
221      */
222     public String getId()
223     {
224         return _id;
225     }
226    
227     /* ------------------------------------------------------------ */
228     public boolean hasMessages()
229     {
230         return _queue.size()>0;
231     }
232 
233     /* ------------------------------------------------------------ */
234     public boolean isLocal()
235     {
236         return true;
237     }
238 
239     /* ------------------------------------------------------------ */
240     /* (non-Javadoc)
241      * @see org.cometd.Client#disconnect()
242      */
243     public void disconnect()
244     {
245         synchronized(this)
246         {
247             if (_bayeux.hasClient(_id))
248                 remove(false);
249         }
250     }
251 
252     /* ------------------------------------------------------------ */
253     /* (non-Javadoc)
254      * @see dojox.cometd.Client#remove(boolean)
255      */
256     public void remove(boolean timeout)
257     {
258         synchronized(this)
259         {
260             Client client=_bayeux.removeClient(_id);   
261             if (_bayeux.isLogInfo())
262                 _bayeux.logInfo("Remove client "+client+" timeout="+timeout); 
263             if (_browserId!=null)
264                 _bayeux.clientOffBrowser(getBrowserId(),_id);
265             _browserId=null;
266             
267             if (_rListeners!=null)
268                 for (RemoveListener l:_rListeners)
269                     l.removed(_id, timeout);
270         }
271         resume();
272     }
273     
274     /* ------------------------------------------------------------ */
275     public int responded()
276     {
277         synchronized(this)
278         {
279             return _responsesPending--;
280         }
281     }
282 
283     /* ------------------------------------------------------------ */
284     public int responsePending()
285     {
286         synchronized(this)
287         {
288             return ++_responsesPending;
289         }
290     }
291     
292     /* ------------------------------------------------------------ */
293     /** Called by deliver to resume anything waiting on this client.
294      */
295     public void resume()
296     {
297     }
298     
299     /* ------------------------------------------------------------ */
300     /*
301      * @return the number of messages queued
302      */
303     public int getMessages()
304     {
305         return _queue.size();
306     }
307     
308     /* ------------------------------------------------------------ */
309     public List<Message> takeMessages()
310     {
311         synchronized(this)
312         {
313             ArrayList<Message> list = new ArrayList<Message>(_queue);
314             _queue.clear();
315             return list;
316         }
317     }
318     
319 
320     /* ------------------------------------------------------------ */
321     public void returnMessages(List<Message> messages)
322     {
323         synchronized(this)
324         {
325             _queue.addAll(0,messages);
326         }
327     }
328         
329     /* ------------------------------------------------------------ */
330     @Override
331     public String toString()
332     {
333         return _id;
334     }
335 
336     /* ------------------------------------------------------------ */
337     protected void addSubscription(ChannelImpl channel)
338     {
339         synchronized (this)
340         {
341             _subscriptions=(ChannelImpl[])LazyList.addToArray(_subscriptions,channel,null);
342         }
343     }
344 
345     /* ------------------------------------------------------------ */
346     protected void removeSubscription(ChannelImpl channel)
347     {
348         synchronized (this)
349         {
350             _subscriptions=(ChannelImpl[])LazyList.removeFromArray(_subscriptions,channel);
351         }
352     }
353 
354     /* ------------------------------------------------------------ */
355     protected void setConnectionType(String type)
356     {
357         synchronized (this)
358         {
359             _type=type;
360         }
361     }
362 
363     /* ------------------------------------------------------------ */
364     protected void setId(String _id)
365     {
366         synchronized (this)
367         {
368             this._id=_id;
369         }
370     }
371 
372     /* ------------------------------------------------------------ */
373     protected void unsubscribeAll()
374     {
375         ChannelImpl[] subscriptions;
376         synchronized(this)
377         {
378             _queue.clear();
379             subscriptions=_subscriptions;
380             _subscriptions=new ChannelImpl[0];
381         }
382         for (ChannelImpl channel : subscriptions)
383             channel.unsubscribe(this);
384         
385     }
386 
387     /* ------------------------------------------------------------ */
388     public void setBrowserId(String id)
389     {
390         if (_browserId!=null && !_browserId.equals(id))
391             _bayeux.clientOffBrowser(_browserId,_id);
392         _browserId=id;
393         if (_browserId!=null)
394             _bayeux.clientOnBrowser(_browserId,_id);
395     }
396 
397     /* ------------------------------------------------------------ */
398     public String getBrowserId()
399     {
400         return _browserId;
401     }
402 
403     /* ------------------------------------------------------------ */
404     @Override
405     public boolean equals(Object o)
406     {
407     	if (!(o instanceof Client))
408     		return false;
409     	return getId().equals(((Client)o).getId());
410     }
411 
412     /* ------------------------------------------------------------ */
413     /**
414      * Get the advice specific for this Client
415      * @return advice specific for this client or null
416      */
417     public JSON.Literal getAdvice()
418     {
419     	return _advice;
420     }
421 
422     /* ------------------------------------------------------------ */
423     /**
424      * @param advice specific for this client
425      */
426     public void setAdvice(JSON.Literal advice)
427     {
428     	_advice=advice;
429     }
430     
431     
432     /* ------------------------------------------------------------ */
433     public void addListener(ClientListener listener)
434     {
435     	synchronized(this)
436     	{
437     		if (listener instanceof MessageListener)
438     		{
439     			if (listener instanceof MessageListener.Synchronous)
440     				_syncMListeners=(MessageListener[])LazyList.addToArray(_syncMListeners,listener,MessageListener.class);
441     			else
442     				_asyncMListeners=(MessageListener[])LazyList.addToArray(_asyncMListeners,listener,MessageListener.class);
443     		}
444 
445     		if (listener instanceof RemoveListener)
446     			_rListeners=(RemoveListener[])LazyList.addToArray(_rListeners,listener,RemoveListener.class);
447     		
448     		if (listener instanceof QueueListener)
449     		    _qListeners=(QueueListener[])LazyList.addToArray(_qListeners,listener,QueueListener.class);
450                 
451                 if (listener instanceof DeliverListener)
452                     _dListeners=(DeliverListener[])LazyList.addToArray(_dListeners,listener,DeliverListener.class);
453     	}
454     }
455 
456     /* ------------------------------------------------------------ */
457     public void removeListener(ClientListener listener)
458     {
459     	synchronized(this)
460     	{
461     		if (listener instanceof MessageListener)
462     		{
463     			_syncMListeners=(MessageListener[])LazyList.removeFromArray(_syncMListeners,listener);
464     			_asyncMListeners=(MessageListener[])LazyList.removeFromArray(_asyncMListeners,listener);
465     		}
466 
467     		if (listener instanceof RemoveListener)
468     			_rListeners=(RemoveListener[])LazyList.removeFromArray(_rListeners,listener);
469     		
470     		if (listener instanceof QueueListener)
471     		    _qListeners=(QueueListener[])LazyList.removeFromArray(_qListeners,listener);
472     	}
473     }
474 
475     /* ------------------------------------------------------------ */
476     public long getTimeout() 
477     {
478     	return _timeout;
479     }
480 
481     /* ------------------------------------------------------------ */
482     public void setTimeout(long timeoutMS) 
483     {
484     	_timeout=timeoutMS;
485     }
486 
487     /* ------------------------------------------------------------ */
488     public void setMaxQueue(int maxQueue)
489     {
490         _maxQueue=maxQueue;
491     }
492     
493     /* ------------------------------------------------------------ */
494     public int getMaxQueue()
495     {
496         return _maxQueue;
497     }
498     
499     /* ------------------------------------------------------------ */
500     public Queue<Message> getQueue()
501     {
502         return _queue;
503     }
504 }