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