View Javadoc

1   /*
2    *   @(#) $Id: AbstractIoFilterChain.java 327113 2005-10-21 06:59:15Z trustin $
3    *
4    *   Copyright 2004 The Apache Software Foundation
5    *
6    *   Licensed under the Apache License, Version 2.0 (the "License");
7    *   you may not use this file except in compliance with the License.
8    *   You may obtain a copy of the License at
9    *
10   *       http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing, software
13   *   distributed under the License is distributed on an "AS IS" BASIS,
14   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *   See the License for the specific language governing permissions and
16   *   limitations under the License.
17   *
18   */
19  package org.apache.mina.io;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.IdentityHashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.mina.common.ByteBuffer;
29  import org.apache.mina.common.IdleStatus;
30  import org.apache.mina.io.IoFilter.NextFilter;
31  
32  /***
33   * An abstract implementation of {@link IoFilterChain} that provides
34   * common operations for developers to support specific transport types.
35   * <p>
36   * All methods has been implemented.  The list of filters is maintained
37   * as a doublely linked list.  You can fire any MINA events which is filtered
38   * by this chain using these public methods:
39   * <ul>
40   *   <li></li>
41   * </ul>
42   * 
43   * The only method a developer should implement is {@link #doWrite(IoSession, ByteBuffer, Object)}.
44   * This method is invoked when filter chain is evaluated for
45   * {@link IoFilter#filterWrite(NextFilter, IoSession, ByteBuffer, Object)} and 
46   * finally to be written to the underlying transport layer (e.g. socket)
47   * 
48   * @author The Apache Directory Project
49   * @version $Rev: 327113 $, $Date: 2005-10-21 15:59:15 +0900 $
50   */
51  public abstract class AbstractIoFilterChain implements IoFilterChain
52  {
53      private final Map name2entry = new HashMap();
54  
55      private final Map filter2entry = new IdentityHashMap();
56  
57      private final Entry head;
58      
59      private final Entry tail;
60  
61      protected AbstractIoFilterChain()
62      {
63          head = new Entry( null, null, "head", createHeadFilter() );
64          tail = new Entry( head, null, "tail", createTailFilter() );
65          head.nextEntry = tail;
66      }
67      
68      /***
69       * Override this method to create custom head of this filter chain.
70       */
71      protected IoFilter createHeadFilter()
72      {
73          return new IoFilter()
74          {
75              public void sessionOpened( NextFilter nextFilter, IoSession session ) throws Exception
76              {
77                  nextFilter.sessionOpened( session );
78              }
79  
80              public void sessionClosed( NextFilter nextFilter, IoSession session ) throws Exception
81              {
82                  nextFilter.sessionClosed( session );
83              }
84  
85              public void sessionIdle( NextFilter nextFilter, IoSession session,
86                                      IdleStatus status ) throws Exception
87              {
88                  nextFilter.sessionIdle( session, status );
89              }
90  
91              public void exceptionCaught( NextFilter nextFilter,
92                                          IoSession session, Throwable cause ) throws Exception
93              {
94                  nextFilter.exceptionCaught( session, cause );
95              }
96  
97              public void dataRead( NextFilter nextFilter, IoSession session,
98                                   ByteBuffer buf ) throws Exception
99              {
100                 nextFilter.dataRead( session, buf );
101             }
102 
103             public void dataWritten( NextFilter nextFilter, IoSession session,
104                                     Object marker ) throws Exception
105             {
106                 nextFilter.dataWritten( session, marker );
107             }
108             
109             public void filterWrite( NextFilter nextFilter, IoSession session,
110                                      ByteBuffer buf, Object marker ) throws Exception
111             {
112                 doWrite( session, buf, marker );
113             }
114         };
115     }
116     
117     /***
118      * Override this method to create custom tail of this filter chain.
119      */
120     protected IoFilter createTailFilter()
121     {
122         return new IoFilter()
123         {
124             public void sessionOpened( NextFilter nextFilter, IoSession session ) throws Exception
125             {
126                 session.getHandler().sessionOpened( session );
127             }
128 
129             public void sessionClosed( NextFilter nextFilter, IoSession session ) throws Exception
130             {
131                 session.getHandler().sessionClosed( session );
132             }
133 
134             public void sessionIdle( NextFilter nextFilter, IoSession session,
135                                     IdleStatus status ) throws Exception
136             {
137                 session.getHandler().sessionIdle( session, status );
138             }
139 
140             public void exceptionCaught( NextFilter nextFilter,
141                                         IoSession session, Throwable cause ) throws Exception
142             {
143                 session.getHandler().exceptionCaught( session, cause );
144             }
145 
146             public void dataRead( NextFilter nextFilter, IoSession session,
147                                  ByteBuffer buf ) throws Exception
148             {
149                 IoHandler handler = session.getHandler();
150                 try
151                 {
152                     handler.dataRead( session, buf );
153                 }
154                 finally
155                 {
156                     buf.release();
157                 }
158             }
159 
160             public void dataWritten( NextFilter nextFilter, IoSession session,
161                                     Object marker ) throws Exception
162             {
163                 session.getHandler().dataWritten( session, marker );
164             }
165 
166             public void filterWrite( NextFilter nextFilter,
167                                      IoSession session, ByteBuffer buf, Object marker ) throws Exception
168             {
169                 nextFilter.filterWrite( session, buf, marker );
170             }
171         };
172     }
173     
174     public IoFilter getChild( String name )
175     {
176         Entry e = ( Entry ) name2entry.get( name );
177         if ( e == null )
178         {
179             return null;
180         }
181         return e.filter;
182     }
183     
184     /***
185      * Adds the specified interceptor with the specified name at the beginning of this chain.
186      */
187     public synchronized void addFirst( String name,
188                                        IoFilter filter )
189     {
190         checkAddable( name );
191         register( head, name, filter );
192     }
193 
194 
195     /***
196      * Adds the specified interceptor with the specified name at the end of this chain.
197      */
198     public synchronized void addLast( String name,
199                                       IoFilter filter )
200     {
201         checkAddable( name );
202         register( tail.prevEntry, name, filter );
203     }
204 
205 
206     /***
207      * Adds the specified interceptor with the specified name just before the interceptor whose name is
208      * <code>baseName</code> in this chain.
209      */
210     public synchronized void addBefore( String baseName,
211                                         String name,
212                                         IoFilter filter )
213     {
214         Entry baseEntry = checkOldName( baseName );
215         checkAddable( name );
216         register( baseEntry, name, filter );
217     }
218 
219 
220     /***
221      * Adds the specified interceptor with the specified name just after the interceptor whose name is
222      * <code>baseName</code> in this chain.
223      */
224     public synchronized void addAfter( String baseName,
225                                        String name,
226                                        IoFilter filter )
227     {
228         Entry baseEntry = checkOldName( baseName );
229         checkAddable( name );
230         register( baseEntry.prevEntry, name, filter );
231     }
232 
233 
234     /***
235      * Removes the interceptor with the specified name from this chain.
236      */
237     public synchronized IoFilter remove( String name )
238     {
239         Entry entry = checkOldName( name );
240         Entry prevEntry = entry.prevEntry;
241         Entry nextEntry = entry.nextEntry;
242         prevEntry.nextEntry = nextEntry;
243         nextEntry.prevEntry = prevEntry;
244 
245         name2entry.remove( name );
246         IoFilter filter = entry.filter;
247         filter2entry.remove( filter );
248         
249         return filter;
250     }
251 
252 
253     /***
254      * Removes all interceptors added to this chain.
255      */
256     public synchronized void clear()
257     {
258         Iterator it = new ArrayList( name2entry.keySet() ).iterator();
259         while ( it.hasNext() )
260         {
261             this.remove( ( String ) it.next() );
262         }
263     }
264 
265     private void register( Entry prevEntry, String name, IoFilter filter )
266     {
267         Entry newEntry = new Entry( prevEntry, prevEntry.nextEntry, name, filter );
268         prevEntry.nextEntry.prevEntry = newEntry;
269         prevEntry.nextEntry = newEntry;
270         name2entry.put( name, newEntry );
271         filter2entry.put( filter, newEntry );
272     }
273 
274     /***
275      * Throws an exception when the specified interceptor name is not registered in this chain.
276      *
277      * @return An interceptor entry with the specified name.
278      */
279     private Entry checkOldName( String baseName )
280     {
281         Entry e = ( Entry ) name2entry.get( baseName );
282         if ( e == null )
283         {
284             throw new IllegalArgumentException( "Unknown interceptor name:" +
285                     baseName );
286         }
287         return e;
288     }
289 
290 
291     /***
292      * Checks the specified interceptor name is already taken and throws an exception if already taken.
293      */
294     private void checkAddable( String name )
295     {
296         if ( name2entry.containsKey( name ) )
297         {
298             throw new IllegalArgumentException( "Other interceptor is using name '" + name + "'" );
299         }
300     }
301 
302     public void sessionOpened( IoSession session )
303     {
304         Entry head = this.head;
305         callNextSessionOpened(head, session);
306     }
307 
308     private void callNextSessionOpened( Entry entry,
309                                         IoSession session)
310     {
311         try
312         {
313             entry.filter.sessionOpened( entry.nextFilter, session );
314         }
315         catch( Throwable e )
316         {
317             exceptionCaught( session, e );
318         }
319     }
320 
321     public void sessionClosed( IoSession session )
322     {
323         Entry head = this.head;
324         callNextSessionClosed(head, session);
325     }
326 
327     private void callNextSessionClosed( Entry entry,
328                                         IoSession session )
329     {
330         try
331         {
332             entry.filter.sessionClosed( entry.nextFilter, session );
333         }
334         catch( Throwable e )
335         {
336             exceptionCaught( session, e );
337         }
338     }
339 
340     public void sessionIdle( IoSession session, IdleStatus status )
341     {
342         Entry head = this.head;
343         callNextSessionIdle(head, session, status);
344     }
345 
346     private void callNextSessionIdle( Entry entry,
347                                       IoSession session,
348                                       IdleStatus status )
349     {
350         try
351         {
352             entry.filter.sessionIdle( entry.nextFilter, session, status );
353         }
354         catch( Throwable e )
355         {
356             exceptionCaught( session, e );
357         }
358     }
359 
360     public void dataRead( IoSession session, ByteBuffer buf )
361     {
362         Entry head = this.head;
363         callNextDataRead(head, session, buf);
364     }
365 
366     private void callNextDataRead( Entry entry,
367                                    IoSession session,
368                                    ByteBuffer buf )
369     {
370         try
371         {
372             entry.filter.dataRead( entry.nextFilter, session, buf );
373         }
374         catch( Throwable e )
375         {
376             exceptionCaught( session, e );
377         }
378     }
379 
380     public void dataWritten( IoSession session, Object marker )
381     {
382         Entry head = this.head;
383         callNextDataWritten(head, session, marker);
384     }
385 
386     private void callNextDataWritten( Entry entry,
387                                       IoSession session,
388                                       Object marker ) 
389     {
390         try
391         {
392             entry.filter.dataWritten( entry.nextFilter, session, marker );
393         }
394         catch( Throwable e )
395         {
396             exceptionCaught( session, e );
397         }
398     }
399 
400     public void exceptionCaught( IoSession session, Throwable cause )
401     {
402         Entry head = this.head;
403         callNextExceptionCaught(head, session, cause);
404     }
405 
406     private void callNextExceptionCaught( Entry entry,
407                                           IoSession session,
408                                           Throwable cause )
409     {
410         try
411         {
412             entry.filter.exceptionCaught( entry.nextFilter, session, cause );
413         }
414         catch( Throwable e )
415         {
416             e.printStackTrace();
417         }
418     }
419     
420     public void filterWrite( IoSession session, ByteBuffer buf, Object marker )
421     {
422         Entry tail = this.tail;
423         callPreviousFilterWrite( tail, session, buf, marker );
424     }
425 
426     private void callPreviousFilterWrite( Entry entry,
427                                           IoSession session,
428                                           ByteBuffer buf, Object marker )
429     {
430         if( buf == null )
431         {
432             return;
433         }
434         
435         try
436         {
437             entry.filter.filterWrite( entry.nextFilter, session, buf, marker );
438         }
439         catch( Throwable e )
440         {
441             exceptionCaught( session, e );
442         }
443     }
444 
445     public List getChildren()
446     {
447         List list = new ArrayList();
448         Entry e = head.nextEntry;
449         while( e != tail )
450         {
451             list.add( e.filter );
452             e = e.nextEntry;
453         }
454 
455         return list;
456     }
457 
458     public List getChildrenReversed()
459     {
460         List list = new ArrayList();
461         Entry e = tail.prevEntry;
462         while( e != head )
463         {
464             list.add( e.filter );
465             e = e.prevEntry;
466         }
467         return list;
468     }
469     
470     protected abstract void doWrite( IoSession session, ByteBuffer buffer, Object marker );
471 
472     private class Entry
473     {
474         private Entry prevEntry;
475 
476         private Entry nextEntry;
477 
478         private final String name;
479         
480         private final IoFilter filter;
481 
482         private final NextFilter nextFilter;
483         
484         private Entry( Entry prevEntry, Entry nextEntry,
485                        String name, IoFilter filter )
486         {
487             if( filter == null )
488             {
489                 throw new NullPointerException( "filter" );
490             }
491             if( name == null )
492             {
493                 throw new NullPointerException( "name" );
494             }
495             
496             this.prevEntry = prevEntry;
497             this.nextEntry = nextEntry;
498             this.name = name;
499             this.filter = filter;
500             this.nextFilter = new NextFilter()
501             {
502 
503                 public void sessionOpened( IoSession session )
504                 {
505                     Entry nextEntry = Entry.this.nextEntry;
506                     callNextSessionOpened( nextEntry, session );
507                 }
508 
509                 public void sessionClosed( IoSession session )
510                 {
511                     Entry nextEntry = Entry.this.nextEntry;
512                     callNextSessionClosed( nextEntry, session );
513                 }
514 
515                 public void sessionIdle( IoSession session, IdleStatus status )
516                 {
517                     Entry nextEntry = Entry.this.nextEntry;
518                     callNextSessionIdle( nextEntry, session, status );
519                 }
520 
521                 public void exceptionCaught( IoSession session,
522                                             Throwable cause )
523                 {
524                     Entry nextEntry = Entry.this.nextEntry;
525                     callNextExceptionCaught( nextEntry, session, cause );
526                 }
527 
528                 public void dataRead( IoSession session, ByteBuffer buf )
529                 {
530                     Entry nextEntry = Entry.this.nextEntry;
531                     callNextDataRead( nextEntry, session, buf );
532                 }
533 
534                 public void dataWritten( IoSession session, Object marker )
535                 {
536                     Entry nextEntry = Entry.this.nextEntry;
537                     callNextDataWritten( nextEntry, session, marker );
538                 }
539                 
540                 public void filterWrite( IoSession session, ByteBuffer buf, Object marker )
541                 {
542                     Entry nextEntry = Entry.this.prevEntry;
543                     callPreviousFilterWrite( nextEntry, session, buf, marker );
544                 }
545             };
546         }
547         
548         public String getName()
549         {
550             return name;
551         }
552         
553         public IoFilter getFilter()
554         {
555             return filter;
556         }
557     }
558 }