View Javadoc

1   /*
2    *   @(#) $Id: AbstractIoFilterChain.java 169321 2005-05-09 15:00:25Z 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: 169321 $, $Date: 2005-05-10 00:00:25 +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                 handler.dataRead( session, buf );
151                 buf.release();
152             }
153 
154             public void dataWritten( NextFilter nextFilter, IoSession session,
155                                     Object marker ) throws Exception
156             {
157                 session.getHandler().dataWritten( session, marker );
158             }
159 
160             public void filterWrite( NextFilter nextFilter,
161                                      IoSession session, ByteBuffer buf, Object marker ) throws Exception
162             {
163                 nextFilter.filterWrite( session, buf, marker );
164             }
165         };
166     }
167     
168     public IoFilter getChild( String name )
169     {
170         Entry e = ( Entry ) name2entry.get( name );
171         if ( e == null )
172         {
173             return null;
174         }
175         return e.filter;
176     }
177     
178     /***
179      * Adds the specified interceptor with the specified name at the beginning of this chain.
180      */
181     public synchronized void addFirst( String name,
182                                        IoFilter filter )
183     {
184         checkAddable( name );
185         register( head, name, filter );
186     }
187 
188 
189     /***
190      * Adds the specified interceptor with the specified name at the end of this chain.
191      */
192     public synchronized void addLast( String name,
193                                       IoFilter filter )
194     {
195         checkAddable( name );
196         register( tail.prevEntry, name, filter );
197     }
198 
199 
200     /***
201      * Adds the specified interceptor with the specified name just before the interceptor whose name is
202      * <code>baseName</code> in this chain.
203      */
204     public synchronized void addBefore( String baseName,
205                                         String name,
206                                         IoFilter filter )
207     {
208         Entry baseEntry = checkOldName( baseName );
209         checkAddable( name );
210         register( baseEntry, name, filter );
211     }
212 
213 
214     /***
215      * Adds the specified interceptor with the specified name just after the interceptor whose name is
216      * <code>baseName</code> in this chain.
217      */
218     public synchronized void addAfter( String baseName,
219                                        String name,
220                                        IoFilter filter )
221     {
222         Entry baseEntry = checkOldName( baseName );
223         checkAddable( name );
224         register( baseEntry.prevEntry, name, filter );
225     }
226 
227 
228     /***
229      * Removes the interceptor with the specified name from this chain.
230      */
231     public synchronized void remove( String name )
232     {
233         Entry entry = checkOldName( name );
234         Entry prevEntry = entry.prevEntry;
235         Entry nextEntry = entry.nextEntry;
236         prevEntry.nextEntry = nextEntry;
237         nextEntry.prevEntry = prevEntry;
238 
239         name2entry.remove( name );
240         IoFilter filter = entry.filter;
241         filter2entry.remove( filter );
242     }
243 
244 
245     /***
246      * Removes all interceptors added to this chain.
247      */
248     public synchronized void clear()
249     {
250         Iterator it = new ArrayList( name2entry.keySet() ).iterator();
251         while ( it.hasNext() )
252         {
253             this.remove( ( String ) it.next() );
254         }
255     }
256 
257     private void register( Entry prevEntry, String name, IoFilter filter )
258     {
259         Entry newEntry = new Entry( prevEntry, prevEntry.nextEntry, name, filter );
260         prevEntry.nextEntry.prevEntry = newEntry;
261         prevEntry.nextEntry = newEntry;
262         name2entry.put( name, newEntry );
263         filter2entry.put( filter, newEntry );
264     }
265 
266     /***
267      * Throws an exception when the specified interceptor name is not registered in this chain.
268      *
269      * @return An interceptor entry with the specified name.
270      */
271     private Entry checkOldName( String baseName )
272     {
273         Entry e = ( Entry ) name2entry.get( baseName );
274         if ( e == null )
275         {
276             throw new IllegalArgumentException( "Unknown interceptor name:" +
277                     baseName );
278         }
279         return e;
280     }
281 
282 
283     /***
284      * Checks the specified interceptor name is already taken and throws an exception if already taken.
285      */
286     private void checkAddable( String name )
287     {
288         if ( name2entry.containsKey( name ) )
289         {
290             throw new IllegalArgumentException( "Other interceptor is using name '" + name + "'" );
291         }
292     }
293 
294     public void sessionOpened( IoSession session )
295     {
296         Entry head = this.head;
297         callNextSessionOpened(head, session);
298     }
299 
300     private void callNextSessionOpened( Entry entry,
301                                         IoSession session)
302     {
303         try
304         {
305             entry.filter.sessionOpened( entry.nextFilter, session );
306         }
307         catch( Throwable e )
308         {
309             exceptionCaught( session, e );
310         }
311     }
312 
313     public void sessionClosed( IoSession session )
314     {
315         Entry head = this.head;
316         callNextSessionClosed(head, session);
317     }
318 
319     private void callNextSessionClosed( Entry entry,
320                                         IoSession session )
321     {
322         try
323         {
324             entry.filter.sessionClosed( entry.nextFilter, session );
325         }
326         catch( Throwable e )
327         {
328             exceptionCaught( session, e );
329         }
330     }
331 
332     public void sessionIdle( IoSession session, IdleStatus status )
333     {
334         Entry head = this.head;
335         callNextSessionIdle(head, session, status);
336     }
337 
338     private void callNextSessionIdle( Entry entry,
339                                       IoSession session,
340                                       IdleStatus status )
341     {
342         try
343         {
344             entry.filter.sessionIdle( entry.nextFilter, session, status );
345         }
346         catch( Throwable e )
347         {
348             exceptionCaught( session, e );
349         }
350     }
351 
352     public void dataRead( IoSession session, ByteBuffer buf )
353     {
354         Entry head = this.head;
355         callNextDataRead(head, session, buf);
356     }
357 
358     private void callNextDataRead( Entry entry,
359                                    IoSession session,
360                                    ByteBuffer buf )
361     {
362         try
363         {
364             entry.filter.dataRead( entry.nextFilter, session, buf );
365         }
366         catch( Throwable e )
367         {
368             exceptionCaught( session, e );
369         }
370     }
371 
372     public void dataWritten( IoSession session, Object marker )
373     {
374         Entry head = this.head;
375         callNextDataWritten(head, session, marker);
376     }
377 
378     private void callNextDataWritten( Entry entry,
379                                       IoSession session,
380                                       Object marker ) 
381     {
382         try
383         {
384             entry.filter.dataWritten( entry.nextFilter, session, marker );
385         }
386         catch( Throwable e )
387         {
388             exceptionCaught( session, e );
389         }
390     }
391 
392     public void exceptionCaught( IoSession session, Throwable cause )
393     {
394         Entry head = this.head;
395         callNextExceptionCaught(head, session, cause);
396     }
397 
398     private void callNextExceptionCaught( Entry entry,
399                                           IoSession session,
400                                           Throwable cause )
401     {
402         try
403         {
404             entry.filter.exceptionCaught( entry.nextFilter, session, cause );
405         }
406         catch( Throwable e )
407         {
408             e.printStackTrace();
409         }
410     }
411     
412     public void filterWrite( IoSession session, ByteBuffer buf, Object marker )
413     {
414         Entry tail = this.tail;
415         callPreviousFilterWrite( tail, session, buf, marker );
416     }
417 
418     private void callPreviousFilterWrite( Entry entry,
419                                           IoSession session,
420                                           ByteBuffer buf, Object marker )
421     {
422         if( buf == null )
423         {
424             return;
425         }
426         
427         try
428         {
429             entry.filter.filterWrite( entry.nextFilter, session, buf, marker );
430         }
431         catch( Throwable e )
432         {
433             exceptionCaught( session, e );
434         }
435     }
436 
437     public List getChildren()
438     {
439         List list = new ArrayList();
440         Entry e = head.nextEntry;
441         while( e != tail )
442         {
443             list.add( e.filter );
444             e = e.nextEntry;
445         }
446 
447         return list;
448     }
449 
450     public List getChildrenReversed()
451     {
452         List list = new ArrayList();
453         Entry e = tail.prevEntry;
454         while( e != head )
455         {
456             list.add( e.filter );
457             e = e.prevEntry;
458         }
459         return list;
460     }
461     
462     protected abstract void doWrite( IoSession session, ByteBuffer buffer, Object marker );
463 
464     private class Entry
465     {
466         private Entry prevEntry;
467 
468         private Entry nextEntry;
469 
470         private final String name;
471         
472         private final IoFilter filter;
473 
474         private final NextFilter nextFilter;
475         
476         private Entry( Entry prevEntry, Entry nextEntry,
477                        String name, IoFilter filter )
478         {
479             if( filter == null )
480             {
481                 throw new NullPointerException( "filter" );
482             }
483             if( name == null )
484             {
485                 throw new NullPointerException( "name" );
486             }
487             
488             this.prevEntry = prevEntry;
489             this.nextEntry = nextEntry;
490             this.name = name;
491             this.filter = filter;
492             this.nextFilter = new NextFilter()
493             {
494 
495                 public void sessionOpened( IoSession session )
496                 {
497                     Entry nextEntry = Entry.this.nextEntry;
498                     callNextSessionOpened( nextEntry, session );
499                 }
500 
501                 public void sessionClosed( IoSession session )
502                 {
503                     Entry nextEntry = Entry.this.nextEntry;
504                     callNextSessionClosed( nextEntry, session );
505                 }
506 
507                 public void sessionIdle( IoSession session, IdleStatus status )
508                 {
509                     Entry nextEntry = Entry.this.nextEntry;
510                     callNextSessionIdle( nextEntry, session, status );
511                 }
512 
513                 public void exceptionCaught( IoSession session,
514                                             Throwable cause )
515                 {
516                     Entry nextEntry = Entry.this.nextEntry;
517                     callNextExceptionCaught( nextEntry, session, cause );
518                 }
519 
520                 public void dataRead( IoSession session, ByteBuffer buf )
521                 {
522                     Entry nextEntry = Entry.this.nextEntry;
523                     callNextDataRead( nextEntry, session, buf );
524                 }
525 
526                 public void dataWritten( IoSession session, Object marker )
527                 {
528                     Entry nextEntry = Entry.this.nextEntry;
529                     callNextDataWritten( nextEntry, session, marker );
530                 }
531                 
532                 public void filterWrite( IoSession session, ByteBuffer buf, Object marker )
533                 {
534                     Entry nextEntry = Entry.this.prevEntry;
535                     callPreviousFilterWrite( nextEntry, session, buf, marker );
536                 }
537             };
538         }
539         
540         public String getName()
541         {
542             return name;
543         }
544         
545         public IoFilter getFilter()
546         {
547             return filter;
548         }
549     }
550 }