View Javadoc

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