View Javadoc

1   /*
2    *   @(#) $Id: AbstractIoFilterChain.java 210062 2005-07-11 03:52:38Z 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: 210062 $, $Date: 2005-07-11 12:52:38 +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 IoFilter 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         return filter;
244     }
245 
246 
247     /***
248      * Removes all interceptors added to this chain.
249      */
250     public synchronized void clear()
251     {
252         Iterator it = new ArrayList( name2entry.keySet() ).iterator();
253         while ( it.hasNext() )
254         {
255             this.remove( ( String ) it.next() );
256         }
257     }
258 
259     private void register( Entry prevEntry, String name, IoFilter filter )
260     {
261         Entry newEntry = new Entry( prevEntry, prevEntry.nextEntry, name, filter );
262         prevEntry.nextEntry.prevEntry = newEntry;
263         prevEntry.nextEntry = newEntry;
264         name2entry.put( name, newEntry );
265         filter2entry.put( filter, newEntry );
266     }
267 
268     /***
269      * Throws an exception when the specified interceptor name is not registered in this chain.
270      *
271      * @return An interceptor entry with the specified name.
272      */
273     private Entry checkOldName( String baseName )
274     {
275         Entry e = ( Entry ) name2entry.get( baseName );
276         if ( e == null )
277         {
278             throw new IllegalArgumentException( "Unknown interceptor name:" +
279                     baseName );
280         }
281         return e;
282     }
283 
284 
285     /***
286      * Checks the specified interceptor name is already taken and throws an exception if already taken.
287      */
288     private void checkAddable( String name )
289     {
290         if ( name2entry.containsKey( name ) )
291         {
292             throw new IllegalArgumentException( "Other interceptor is using name '" + name + "'" );
293         }
294     }
295 
296     public void sessionOpened( IoSession session )
297     {
298         Entry head = this.head;
299         callNextSessionOpened(head, session);
300     }
301 
302     private void callNextSessionOpened( Entry entry,
303                                         IoSession session)
304     {
305         try
306         {
307             entry.filter.sessionOpened( entry.nextFilter, session );
308         }
309         catch( Throwable e )
310         {
311             exceptionCaught( session, e );
312         }
313     }
314 
315     public void sessionClosed( IoSession session )
316     {
317         Entry head = this.head;
318         callNextSessionClosed(head, session);
319     }
320 
321     private void callNextSessionClosed( Entry entry,
322                                         IoSession session )
323     {
324         try
325         {
326             entry.filter.sessionClosed( entry.nextFilter, session );
327         }
328         catch( Throwable e )
329         {
330             exceptionCaught( session, e );
331         }
332     }
333 
334     public void sessionIdle( IoSession session, IdleStatus status )
335     {
336         Entry head = this.head;
337         callNextSessionIdle(head, session, status);
338     }
339 
340     private void callNextSessionIdle( Entry entry,
341                                       IoSession session,
342                                       IdleStatus status )
343     {
344         try
345         {
346             entry.filter.sessionIdle( entry.nextFilter, session, status );
347         }
348         catch( Throwable e )
349         {
350             exceptionCaught( session, e );
351         }
352     }
353 
354     public void dataRead( IoSession session, ByteBuffer buf )
355     {
356         Entry head = this.head;
357         callNextDataRead(head, session, buf);
358     }
359 
360     private void callNextDataRead( Entry entry,
361                                    IoSession session,
362                                    ByteBuffer buf )
363     {
364         try
365         {
366             entry.filter.dataRead( entry.nextFilter, session, buf );
367         }
368         catch( Throwable e )
369         {
370             exceptionCaught( session, e );
371         }
372     }
373 
374     public void dataWritten( IoSession session, Object marker )
375     {
376         Entry head = this.head;
377         callNextDataWritten(head, session, marker);
378     }
379 
380     private void callNextDataWritten( Entry entry,
381                                       IoSession session,
382                                       Object marker ) 
383     {
384         try
385         {
386             entry.filter.dataWritten( entry.nextFilter, session, marker );
387         }
388         catch( Throwable e )
389         {
390             exceptionCaught( session, e );
391         }
392     }
393 
394     public void exceptionCaught( IoSession session, Throwable cause )
395     {
396         Entry head = this.head;
397         callNextExceptionCaught(head, session, cause);
398     }
399 
400     private void callNextExceptionCaught( Entry entry,
401                                           IoSession session,
402                                           Throwable cause )
403     {
404         try
405         {
406             entry.filter.exceptionCaught( entry.nextFilter, session, cause );
407         }
408         catch( Throwable e )
409         {
410             e.printStackTrace();
411         }
412     }
413     
414     public void filterWrite( IoSession session, ByteBuffer buf, Object marker )
415     {
416         Entry tail = this.tail;
417         callPreviousFilterWrite( tail, session, buf, marker );
418     }
419 
420     private void callPreviousFilterWrite( Entry entry,
421                                           IoSession session,
422                                           ByteBuffer buf, Object marker )
423     {
424         if( buf == null )
425         {
426             return;
427         }
428         
429         try
430         {
431             entry.filter.filterWrite( entry.nextFilter, session, buf, marker );
432         }
433         catch( Throwable e )
434         {
435             exceptionCaught( session, e );
436         }
437     }
438 
439     public List getChildren()
440     {
441         List list = new ArrayList();
442         Entry e = head.nextEntry;
443         while( e != tail )
444         {
445             list.add( e.filter );
446             e = e.nextEntry;
447         }
448 
449         return list;
450     }
451 
452     public List getChildrenReversed()
453     {
454         List list = new ArrayList();
455         Entry e = tail.prevEntry;
456         while( e != head )
457         {
458             list.add( e.filter );
459             e = e.prevEntry;
460         }
461         return list;
462     }
463     
464     protected abstract void doWrite( IoSession session, ByteBuffer buffer, Object marker );
465 
466     private class Entry
467     {
468         private Entry prevEntry;
469 
470         private Entry nextEntry;
471 
472         private final String name;
473         
474         private final IoFilter filter;
475 
476         private final NextFilter nextFilter;
477         
478         private Entry( Entry prevEntry, Entry nextEntry,
479                        String name, IoFilter filter )
480         {
481             if( filter == null )
482             {
483                 throw new NullPointerException( "filter" );
484             }
485             if( name == null )
486             {
487                 throw new NullPointerException( "name" );
488             }
489             
490             this.prevEntry = prevEntry;
491             this.nextEntry = nextEntry;
492             this.name = name;
493             this.filter = filter;
494             this.nextFilter = new NextFilter()
495             {
496 
497                 public void sessionOpened( IoSession session )
498                 {
499                     Entry nextEntry = Entry.this.nextEntry;
500                     callNextSessionOpened( nextEntry, session );
501                 }
502 
503                 public void sessionClosed( IoSession session )
504                 {
505                     Entry nextEntry = Entry.this.nextEntry;
506                     callNextSessionClosed( nextEntry, session );
507                 }
508 
509                 public void sessionIdle( IoSession session, IdleStatus status )
510                 {
511                     Entry nextEntry = Entry.this.nextEntry;
512                     callNextSessionIdle( nextEntry, session, status );
513                 }
514 
515                 public void exceptionCaught( IoSession session,
516                                             Throwable cause )
517                 {
518                     Entry nextEntry = Entry.this.nextEntry;
519                     callNextExceptionCaught( nextEntry, session, cause );
520                 }
521 
522                 public void dataRead( IoSession session, ByteBuffer buf )
523                 {
524                     Entry nextEntry = Entry.this.nextEntry;
525                     callNextDataRead( nextEntry, session, buf );
526                 }
527 
528                 public void dataWritten( IoSession session, Object marker )
529                 {
530                     Entry nextEntry = Entry.this.nextEntry;
531                     callNextDataWritten( nextEntry, session, marker );
532                 }
533                 
534                 public void filterWrite( IoSession session, ByteBuffer buf, Object marker )
535                 {
536                     Entry nextEntry = Entry.this.prevEntry;
537                     callPreviousFilterWrite( nextEntry, session, buf, marker );
538                 }
539             };
540         }
541         
542         public String getName()
543         {
544             return name;
545         }
546         
547         public IoFilter getFilter()
548         {
549             return filter;
550         }
551     }
552 }