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.Collections;
19  import java.util.List;
20  import java.util.Queue;
21  import java.util.concurrent.CopyOnWriteArrayList;
22  
23  import org.cometd.Bayeux;
24  import org.cometd.Client;
25  import org.cometd.DeliverListener;
26  import org.cometd.Extension;
27  import org.cometd.ClientListener;
28  import org.cometd.Message;
29  import org.cometd.MessageListener;
30  import org.cometd.QueueListener;
31  import org.cometd.RemoveListener;
32  import org.mortbay.util.ArrayQueue;
33  import org.mortbay.util.LazyList;
34  import org.mortbay.util.ajax.JSON;
35  
36  
37  
38  /* ------------------------------------------------------------ */
39  /**
40   * 
41   * @author gregw
42   */
43  public class ClientImpl implements Client
44  {
45      private String _id;
46      private String _type;
47      private int _responsesPending;
48      private ChannelImpl[] _subscriptions=new ChannelImpl[0]; // copy on write
49      private RemoveListener[] _rListeners; // copy on write
50      private MessageListener[] _syncMListeners; // copy on write
51      private MessageListener[] _asyncMListeners; // copy on write
52      private QueueListener[] _qListeners; // copy on write
53      private DeliverListener[] _dListeners; // copy on write
54      protected AbstractBayeux _bayeux;
55      private String _browserId;
56      private JSON.Literal _advice;
57      private int _batch;
58      private int _maxQueue;
59      private ArrayQueue<Message> _queue=new ArrayQueue<Message>(8,16,this);
60      private long _timeout;
61      protected Extension[] _extensions;
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     /* ------------------------------------------------------------ */
183     public void startBatch()
184     {
185         synchronized(this)
186         {
187             _batch++;
188         }
189     }
190     
191     /* ------------------------------------------------------------ */
192     public void endBatch()
193     {
194         synchronized(this)
195         {
196             if (--_batch==0 && _queue.size()>0 && _responsesPending<1)
197                 resume();
198         }
199     }
200     
201     /* ------------------------------------------------------------ */
202     public String getConnectionType()
203     {
204         return _type;
205     }
206     
207     /* ------------------------------------------------------------ */
208     /* (non-Javadoc)
209      * @see org.mortbay.cometd.C#getId()
210      */
211     public String getId()
212     {
213         return _id;
214     }
215    
216     /* ------------------------------------------------------------ */
217     public boolean hasMessages()
218     {
219         return _queue.size()>0;
220     }
221 
222     /* ------------------------------------------------------------ */
223     public boolean isLocal()
224     {
225         return true;
226     }
227 
228     /* ------------------------------------------------------------ */
229     /* (non-Javadoc)
230      * @see org.cometd.Client#disconnect()
231      */
232     public void disconnect()
233     {
234         synchronized(this)
235         {
236             if (_bayeux.hasClient(_id))
237                 remove(false);
238         }
239     }
240 
241     /* ------------------------------------------------------------ */
242     /* (non-Javadoc)
243      * @see dojox.cometd.Client#remove(boolean)
244      */
245     public void remove(boolean timeout)
246     {
247         synchronized(this)
248         {
249             Client client=_bayeux.removeClient(_id);   
250             if (_bayeux.isLogInfo())
251                 _bayeux.logInfo("Remove client "+client+" timeout="+timeout); 
252             if (_browserId!=null)
253                 _bayeux.clientOffBrowser(getBrowserId(),_id);
254             _browserId=null;
255             
256             if (_rListeners!=null)
257                 for (RemoveListener l:_rListeners)
258                     l.removed(_id, timeout);
259         }
260         resume();
261     }
262     
263     /* ------------------------------------------------------------ */
264     public int responded()
265     {
266         synchronized(this)
267         {
268             return _responsesPending--;
269         }
270     }
271 
272     /* ------------------------------------------------------------ */
273     public int responsePending()
274     {
275         synchronized(this)
276         {
277             return ++_responsesPending;
278         }
279     }
280     
281     /* ------------------------------------------------------------ */
282     /** Called by deliver to resume anything waiting on this client.
283      */
284     public void resume()
285     {
286     }
287     
288     /* ------------------------------------------------------------ */
289     /*
290      * @return the number of messages queued
291      */
292     public int getMessages()
293     {
294         return _queue.size();
295     }
296     
297     /* ------------------------------------------------------------ */
298     public List<Message> takeMessages()
299     {
300         synchronized(this)
301         {
302             ArrayList<Message> list = new ArrayList<Message>(_queue);
303             _queue.clear();
304             return list;
305         }
306     }
307     
308 
309     /* ------------------------------------------------------------ */
310     public void returnMessages(List<Message> messages)
311     {
312         synchronized(this)
313         {
314             _queue.addAll(0,messages);
315         }
316     }
317         
318     /* ------------------------------------------------------------ */
319     @Override
320     public String toString()
321     {
322         return _id;
323     }
324 
325     /* ------------------------------------------------------------ */
326     protected void addSubscription(ChannelImpl channel)
327     {
328         synchronized (this)
329         {
330             _subscriptions=(ChannelImpl[])LazyList.addToArray(_subscriptions,channel,null);
331         }
332     }
333 
334     /* ------------------------------------------------------------ */
335     protected void removeSubscription(ChannelImpl channel)
336     {
337         synchronized (this)
338         {
339             _subscriptions=(ChannelImpl[])LazyList.removeFromArray(_subscriptions,channel);
340         }
341     }
342 
343     /* ------------------------------------------------------------ */
344     protected void setConnectionType(String type)
345     {
346         synchronized (this)
347         {
348             _type=type;
349         }
350     }
351 
352     /* ------------------------------------------------------------ */
353     protected void setId(String _id)
354     {
355         synchronized (this)
356         {
357             this._id=_id;
358         }
359     }
360 
361     /* ------------------------------------------------------------ */
362     protected void unsubscribeAll()
363     {
364         ChannelImpl[] subscriptions;
365         synchronized(this)
366         {
367             _queue.clear();
368             subscriptions=_subscriptions;
369             _subscriptions=new ChannelImpl[0];
370         }
371         for (ChannelImpl channel : subscriptions)
372             channel.unsubscribe(this);
373         
374     }
375 
376     /* ------------------------------------------------------------ */
377     public void setBrowserId(String id)
378     {
379         if (_browserId!=null && !_browserId.equals(id))
380             _bayeux.clientOffBrowser(_browserId,_id);
381         _browserId=id;
382         if (_browserId!=null)
383             _bayeux.clientOnBrowser(_browserId,_id);
384     }
385 
386     /* ------------------------------------------------------------ */
387     public String getBrowserId()
388     {
389         return _browserId;
390     }
391 
392     /* ------------------------------------------------------------ */
393     @Override
394     public boolean equals(Object o)
395     {
396     	if (!(o instanceof Client))
397     		return false;
398     	return getId().equals(((Client)o).getId());
399     }
400 
401     /* ------------------------------------------------------------ */
402     /**
403      * Get the advice specific for this Client
404      * @return advice specific for this client or null
405      */
406     public JSON.Literal getAdvice()
407     {
408     	return _advice;
409     }
410 
411     /* ------------------------------------------------------------ */
412     /**
413      * @param advice specific for this client
414      */
415     public void setAdvice(JSON.Literal advice)
416     {
417     	_advice=advice;
418     }
419     
420     
421     /* ------------------------------------------------------------ */
422     public void addListener(ClientListener listener)
423     {
424     	synchronized(this)
425     	{
426     		if (listener instanceof MessageListener)
427     		{
428     			if (listener instanceof MessageListener.Synchronous)
429     				_syncMListeners=(MessageListener[])LazyList.addToArray(_syncMListeners,listener,MessageListener.class);
430     			else
431     				_asyncMListeners=(MessageListener[])LazyList.addToArray(_asyncMListeners,listener,MessageListener.class);
432     		}
433 
434     		if (listener instanceof RemoveListener)
435     			_rListeners=(RemoveListener[])LazyList.addToArray(_rListeners,listener,RemoveListener.class);
436     		
437     		if (listener instanceof QueueListener)
438     		    _qListeners=(QueueListener[])LazyList.addToArray(_qListeners,listener,QueueListener.class);
439                 
440                 if (listener instanceof DeliverListener)
441                     _dListeners=(DeliverListener[])LazyList.addToArray(_dListeners,listener,DeliverListener.class);
442     	}
443     }
444 
445     /* ------------------------------------------------------------ */
446     public void removeListener(ClientListener listener)
447     {
448     	synchronized(this)
449     	{
450     		if (listener instanceof MessageListener)
451     		{
452     			_syncMListeners=(MessageListener[])LazyList.removeFromArray(_syncMListeners,listener);
453     			_asyncMListeners=(MessageListener[])LazyList.removeFromArray(_asyncMListeners,listener);
454     		}
455 
456     		if (listener instanceof RemoveListener)
457     			_rListeners=(RemoveListener[])LazyList.removeFromArray(_rListeners,listener);
458     		
459     		if (listener instanceof QueueListener)
460     		    _qListeners=(QueueListener[])LazyList.removeFromArray(_qListeners,listener);
461     	}
462     }
463 
464     /* ------------------------------------------------------------ */
465     public long getTimeout() 
466     {
467     	return _timeout;
468     }
469 
470     /* ------------------------------------------------------------ */
471     public void setTimeout(long timeoutMS) 
472     {
473     	_timeout=timeoutMS;
474     }
475 
476     /* ------------------------------------------------------------ */
477     public void setMaxQueue(int maxQueue)
478     {
479         _maxQueue=maxQueue;
480     }
481     
482     /* ------------------------------------------------------------ */
483     public int getMaxQueue()
484     {
485         return _maxQueue;
486     }
487     
488     /* ------------------------------------------------------------ */
489     public Queue<Message> getQueue()
490     {
491         return _queue;
492     }
493 }