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