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