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.Arrays;
18  import java.util.Collection;
19  import java.util.List;
20  import java.util.Queue;
21  import java.util.concurrent.ConcurrentHashMap;
22  import java.util.concurrent.ConcurrentMap;
23  
24  import org.cometd.Channel;
25  import org.cometd.ChannelListener;
26  import org.cometd.Client;
27  import org.cometd.DataFilter;
28  import org.cometd.Message;
29  import org.cometd.SubscriptionListener;
30  import org.mortbay.log.Log;
31  import org.mortbay.util.ArrayQueue;
32  import org.mortbay.util.LazyList;
33  
34  
35  /* ------------------------------------------------------------ */
36  /** A Bayuex Channel
37   * 
38   * @author gregw
39   *
40   */
41  public class ChannelImpl implements Channel
42  {
43      protected AbstractBayeux _bayeux;
44      private volatile ClientImpl[] _subscribers=new ClientImpl[0]; // copy on write
45      private volatile DataFilter[] _dataFilters=new DataFilter[0]; // copy on write
46      private volatile SubscriptionListener[] _subscriptionListeners=new SubscriptionListener[0]; // copy on write
47      private ChannelId _id;
48      private ConcurrentMap<String,ChannelImpl> _children = new ConcurrentHashMap<String, ChannelImpl>();
49      private ChannelImpl _wild;
50      private ChannelImpl _wildWild;
51      private boolean _persistent;
52      private int _split;
53      private boolean _lazy;
54      
55      /* ------------------------------------------------------------ */
56      ChannelImpl(String id,AbstractBayeux bayeux)
57      {
58          _id=new ChannelId(id);
59          _bayeux=bayeux;
60      }
61  
62      /* ------------------------------------------------------------ */
63      /**
64       * A Lazy channel marks published messages as lazy.
65       * Lazy messages are queued but do not wake up
66       * waiting clients.
67       * @return true if message is lazy
68       */
69      public boolean isLazy()
70      {
71          return _lazy;
72      }
73  
74      /* ------------------------------------------------------------ */
75      /**
76       * A Lazy channel marks published messages as lazy.
77       * Lazy messages are queued but do not wake up
78       * waiting clients.
79       * @param lazy true if message is lazy
80       */
81      public void setLazy(boolean lazy)
82      {
83          _lazy = lazy;
84      }
85      
86      /* ------------------------------------------------------------ */
87      public void addChild(ChannelImpl channel)
88      {
89          ChannelId child=channel.getChannelId();
90          if (!_id.isParentOf(child))
91          {
92              throw new IllegalArgumentException(_id+" not parent of "+child);
93          }
94          
95          String next = child.getSegment(_id.depth());
96  
97          if ((child.depth()-_id.depth())==1)
98          {
99              // add the channel to this channels
100             ChannelImpl old = _children.putIfAbsent(next,channel);
101             if (old!=null)
102                 throw new IllegalArgumentException("Already Exists");
103 
104             if (ChannelId.WILD.equals(next))
105                 _wild=channel;
106             else if (ChannelId.WILDWILD.equals(next))
107                 _wildWild=channel;
108             _bayeux.addChannel(channel);
109         }
110         else
111         {
112             ChannelImpl branch=_children.get(next);
113             branch=(ChannelImpl)_bayeux.getChannel((_id.depth()==0?"/":(_id.toString()+"/"))+next,true);
114             branch.addChild(channel);
115         }
116     }
117     
118     /* ------------------------------------------------------------ */
119     /**
120      * @param filter
121      */
122     public void addDataFilter(DataFilter filter)
123     {
124         synchronized(this)
125         {
126             _dataFilters=(DataFilter[])LazyList.addToArray(_dataFilters,filter,null);
127         }
128     }
129 
130     /* ------------------------------------------------------------ */
131     /* ------------------------------------------------------------ */
132     /**
133      * @return
134      */
135     public ChannelId getChannelId()
136     {
137         return _id;
138     }
139     
140     /* ------------------------------------------------------------ */
141     public ChannelImpl getChild(ChannelId id)
142     {
143         String next=id.getSegment(_id.depth());
144         if (next==null)
145             return null;
146         
147         ChannelImpl channel = _children.get(next);
148         
149         if (channel==null || channel.getChannelId().depth()==id.depth())
150         {
151             return channel;
152         }
153         return channel.getChild(id);
154     }
155 
156     /* ------------------------------------------------------------ */
157      public void getChannels(List<Channel> list)
158      {
159          list.add(this);
160          for (ChannelImpl channel: _children.values())
161              channel.getChannels(list);
162      }
163 
164      /* ------------------------------------------------------------ */
165      public int getChannelCount()
166      {
167          int count = 1;
168          
169          for(ChannelImpl channel: _children.values())
170              count += channel.getChannelCount();
171          
172          return count;
173      }
174      
175     /* ------------------------------------------------------------ */
176     /**
177      * @return
178      */
179     public String getId()
180     {
181         return _id.toString();
182     }
183 
184     
185     /* ------------------------------------------------------------ */
186     public boolean isPersistent()
187     {
188         return _persistent;
189     }
190 
191     /* ------------------------------------------------------------ */
192     public void publish(Client fromClient, Object data, String msgId)
193     {
194         _bayeux.doPublish(getChannelId(),fromClient,data,msgId,false);   
195     }
196 
197     /* ------------------------------------------------------------ */
198     public void publishLazy(Client fromClient, Object data, String msgId)
199     {
200         _bayeux.doPublish(getChannelId(),fromClient,data,msgId,true);   
201     }
202     
203     /* ------------------------------------------------------------ */
204     public boolean remove()
205     {
206         return _bayeux.removeChannel(this);
207     }
208     
209     /* ------------------------------------------------------------ */
210     public boolean doRemove(ChannelImpl channel)
211     {
212         ChannelId channelId = channel.getChannelId();
213         String key = channelId.getSegment(channelId.depth()-1);
214         if (_children.containsKey(key))
215         {
216             ChannelImpl child = _children.get(key);
217             
218             synchronized (this)
219             {
220                 synchronized (child)
221                 {
222                     if (!child.isPersistent() && child.getSubscriberCount()==0 && child.getChannelCount()==1)
223                     {
224                         _children.remove(key);
225                         return true;
226                     }
227                     else
228                         return false;
229                 }       
230             }
231         }
232         else
233         {
234             for (ChannelImpl child : _children.values())
235             {
236                 if (child.doRemove(channel))
237                     return true;
238             }
239         }
240         return false;
241     }
242     
243     /* ------------------------------------------------------------ */
244     /* ------------------------------------------------------------ */
245     /**
246      * @param filter
247      */
248     public DataFilter removeDataFilter(DataFilter filter)
249     {
250         synchronized(this)
251         {
252             _dataFilters=(DataFilter[])LazyList.removeFromArray(_dataFilters,filter);
253             return filter;
254         }
255     }
256 
257     /* ------------------------------------------------------------ */
258     public void setPersistent(boolean persistent)
259     {
260         _persistent=persistent;
261     }
262 
263     /* ------------------------------------------------------------ */
264     /**
265      * @param client
266      */
267     public void subscribe(Client client)
268     {
269         if (!(client instanceof ClientImpl))
270             throw new IllegalArgumentException("Client instance not obtained from Bayeux.newClient()");
271         
272         synchronized (this)
273         {
274             for (ClientImpl c : _subscribers)
275             {
276                 if (client.equals(c))
277                     return;
278             }
279             _subscribers=(ClientImpl[])LazyList.addToArray(_subscribers,client,null);
280             
281             for (SubscriptionListener l : _subscriptionListeners)
282                 l.subscribed(client, this);
283         }
284         
285         ((ClientImpl)client).addSubscription(this);
286     }
287 
288     /* ------------------------------------------------------------ */
289     @Override
290     public String toString()
291     {
292         return _id.toString();
293     }
294 
295     /* ------------------------------------------------------------ */
296     /**
297      * @param client
298      */
299     public void unsubscribe(Client client)
300     {
301         if (!(client instanceof ClientImpl))
302             throw new IllegalArgumentException("Client instance not obtained from Bayeux.newClient()");
303         ((ClientImpl)client).removeSubscription(this);
304         synchronized(this)
305         {
306             _subscribers=(ClientImpl[])LazyList.removeFromArray(_subscribers,client);
307             
308             for (SubscriptionListener l : _subscriptionListeners)
309                 l.unsubscribed(client,this);
310             
311             if (!_persistent && _subscribers.length==0 && _children.size()==0)
312                 remove();
313         }
314     }
315 
316     /* ------------------------------------------------------------ */
317     protected void doDelivery(ChannelId to, Client from, Message msg)
318     {   
319         int tail = to.depth()-_id.depth();
320         
321         Object data = msg.getData();
322         Object old = data;
323         
324         try
325         {
326             switch(tail)
327             {
328                 case 0:      
329                 {
330                     final DataFilter[] filters=_dataFilters;
331                     for (DataFilter filter: filters)
332                     {
333                         data=filter.filter(from,this,data);
334                         if (data==null)
335                             return;
336                     }
337                 }
338                 break;
339 
340                 case 1:
341                     if (_wild!=null)  
342                     {
343                         final DataFilter[] filters=_wild._dataFilters;
344                         for (DataFilter filter: filters)
345                         {
346                             data=filter.filter(from,this,data);
347                             if (data==null)
348                                 return;
349                         }
350                     }
351 
352                 default:
353                     if (_wildWild!=null)  
354                     {
355                         final DataFilter[] filters=_wildWild._dataFilters;
356                         for (DataFilter filter: filters)
357                         {
358                             data=filter.filter(from,this,data);
359                             if (data==null)
360                                 return;
361                         }
362                     }
363             }
364         }
365         catch (IllegalStateException e)
366         {
367             Log.debug(e);
368             return;
369         }
370 
371         // TODO this may not be correct if the message is reused.
372         // probably should close message ?
373         if (data!=old)
374             msg.put(AbstractBayeux.DATA_FIELD,data);
375         
376 
377         switch(tail)
378         {
379             case 0:
380             {
381                 if (_lazy && msg instanceof MessageImpl)
382                     ((MessageImpl)msg).setLazy(true);
383                 
384                 final ClientImpl[] subscribers=_subscribers;
385                 if (subscribers.length>0)
386                 {
387                     // fair delivery 
388                     int split=_split++%_subscribers.length;
389                     for (int i=split;i<subscribers.length;i++)
390                         subscribers[i].doDelivery(from,msg);
391                     for (int i=0;i<split;i++)
392                         subscribers[i].doDelivery(from,msg);
393                 }                
394                 break;
395             }
396 
397             case 1:
398                 if (_wild!=null)
399                 {
400                     if (_wild._lazy && msg instanceof MessageImpl)
401                         ((MessageImpl)msg).setLazy(true);
402                     final ClientImpl[] subscribers=_wild._subscribers;
403                     for (ClientImpl client: subscribers)
404                         client.doDelivery(from,msg);
405                 }
406 
407             default:
408             {
409                 if (_wildWild!=null)
410                 {
411                     if (_wildWild._lazy && msg instanceof MessageImpl)
412                         ((MessageImpl)msg).setLazy(true);
413                     final ClientImpl[] subscribers=_wildWild._subscribers;
414                     for (ClientImpl client: subscribers)
415                         client.doDelivery(from,msg);
416                 }
417                 String next = to.getSegment(_id.depth());
418                 ChannelImpl channel = _children.get(next);
419                 if (channel!=null)
420                     channel.doDelivery(to,from,msg);
421             }
422         }
423     }
424     
425     /* ------------------------------------------------------------ */
426     public Collection<Client> getSubscribers()
427     {
428         synchronized(this)
429         {
430             return Arrays.asList((Client[])_subscribers);
431         }
432     }
433 
434     /* ------------------------------------------------------------ */
435     public int getSubscriberCount()
436     {
437         synchronized(this)
438         {
439             return _subscribers.length;
440         }
441     }
442 
443 
444     /* ------------------------------------------------------------ */
445     /* (non-Javadoc)
446      * @see dojox.cometd.Channel#getFilters()
447      */
448     public Collection<DataFilter> getDataFilters()
449     {
450         synchronized(this)
451         {
452             return Arrays.asList(_dataFilters);
453         }
454     }
455 
456     /* ------------------------------------------------------------ */
457     public void addListener(ChannelListener listener)
458     {
459         synchronized(this)
460         {
461             if (listener instanceof SubscriptionListener)
462                 _subscriptionListeners=(SubscriptionListener[])LazyList.addToArray(_subscriptionListeners,listener,null);
463         }
464     }
465     
466 }