View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  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,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.core.filterchain;
21  
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.mina.core.buffer.IoBuffer;
29  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
30  import org.apache.mina.core.future.ConnectFuture;
31  import org.apache.mina.core.future.IoFuture;
32  import org.apache.mina.core.session.AbstractIoSession;
33  import org.apache.mina.core.session.AttributeKey;
34  import org.apache.mina.core.session.IdleStatus;
35  import org.apache.mina.core.session.IoSession;
36  import org.apache.mina.core.write.WriteRequest;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  /**
41   * A default implementation of {@link IoFilterChain} that provides
42   * all operations for developers who want to implement their own
43   * transport layer once used with {@link AbstractIoSession}.
44   *
45   * @author The Apache MINA Project (dev@mina.apache.org)
46   */
47  public class DefaultIoFilterChain implements IoFilterChain {
48      /**
49       * A session attribute that stores an {@link IoFuture} related with
50       * the {@link IoSession}.  {@link DefaultIoFilterChain} clears this
51       * attribute and notifies the future when {@link #fireSessionCreated()}
52       * or {@link #fireExceptionCaught(Throwable)} is invoked.
53       */
54      public static final AttributeKey SESSION_CREATED_FUTURE = new AttributeKey(
55              DefaultIoFilterChain.class, "connectFuture");
56  
57      /** The associated session */
58      private final AbstractIoSession session;
59  
60      private final Map<String, Entry> name2entry = new HashMap<String, Entry>();
61  
62      /** The chain head */
63      private final EntryImpl head;
64  
65      /** The chain tail */
66      private final EntryImpl tail;
67  
68      /** The logger for this class */
69      private final static Logger LOGGER = LoggerFactory.getLogger(DefaultIoFilterChain.class);
70  
71      
72      /**
73       * Create a new default chain, associated with a session. It will only contain a 
74       * HeadFilter and a TailFilter.
75       * 
76       * @param session The session associated with the created filter chain
77       */
78      public DefaultIoFilterChain(AbstractIoSession session) {
79          if (session == null) {
80              throw new NullPointerException("session");
81          }
82  
83          this.session = session;
84          head = new EntryImpl(null, null, "head", new HeadFilter());
85          tail = new EntryImpl(head, null, "tail", new TailFilter());
86          head.nextEntry = tail;
87      }
88  
89      public IoSession getSession() {
90          return session;
91      }
92  
93      public Entry getEntry(String name) {
94          Entry e = name2entry.get(name);
95          if (e == null) {
96              return null;
97          }
98          return e;
99      }
100 
101     public Entry getEntry(IoFilter filter) {
102         EntryImpl e = head.nextEntry;
103         while (e != tail) {
104             if (e.getFilter() == filter) {
105                 return e;
106             }
107             e = e.nextEntry;
108         }
109         return null;
110     }
111 
112     public Entry getEntry(Class<? extends IoFilter> filterType) {
113         EntryImpl e = head.nextEntry;
114         while (e != tail) {
115             if (filterType.isAssignableFrom(e.getFilter().getClass())) {
116                 return e;
117             }
118             e = e.nextEntry;
119         }
120         return null;
121     }
122 
123     public IoFilter get(String name) {
124         Entry e = getEntry(name);
125         if (e == null) {
126             return null;
127         }
128 
129         return e.getFilter();
130     }
131 
132     public IoFilter get(Class<? extends IoFilter> filterType) {
133         Entry e = getEntry(filterType);
134         if (e == null) {
135             return null;
136         }
137 
138         return e.getFilter();
139     }
140 
141     public NextFilter getNextFilter(String name) {
142         Entry e = getEntry(name);
143         if (e == null) {
144             return null;
145         }
146 
147         return e.getNextFilter();
148     }
149 
150     public NextFilter getNextFilter(IoFilter filter) {
151         Entry e = getEntry(filter);
152         if (e == null) {
153             return null;
154         }
155 
156         return e.getNextFilter();
157     }
158 
159     public NextFilter getNextFilter(Class<? extends IoFilter> filterType) {
160         Entry e = getEntry(filterType);
161         if (e == null) {
162             return null;
163         }
164 
165         return e.getNextFilter();
166     }
167 
168     public synchronized void addFirst(String name, IoFilter filter) {
169         checkAddable(name);
170         register(head, name, filter);
171     }
172 
173     public synchronized void addLast(String name, IoFilter filter) {
174         checkAddable(name);
175         register(tail.prevEntry, name, filter);
176     }
177 
178     public synchronized void addBefore(String baseName, String name,
179             IoFilter filter) {
180         EntryImpl baseEntry = checkOldName(baseName);
181         checkAddable(name);
182         register(baseEntry.prevEntry, name, filter);
183     }
184 
185     public synchronized void addAfter(String baseName, String name,
186             IoFilter filter) {
187         EntryImpl baseEntry = checkOldName(baseName);
188         checkAddable(name);
189         register(baseEntry, name, filter);
190     }
191 
192     public synchronized IoFilter remove(String name) {
193         EntryImpl entry = checkOldName(name);
194         deregister(entry);
195         return entry.getFilter();
196     }
197 
198     public synchronized void remove(IoFilter filter) {
199         EntryImpl e = head.nextEntry;
200         while (e != tail) {
201             if (e.getFilter() == filter) {
202                 deregister(e);
203                 return;
204             }
205             e = e.nextEntry;
206         }
207         throw new IllegalArgumentException("Filter not found: "
208                 + filter.getClass().getName());
209     }
210 
211     public synchronized IoFilter remove(Class<? extends IoFilter> filterType) {
212         EntryImpl e = head.nextEntry;
213         while (e != tail) {
214             if (filterType.isAssignableFrom(e.getFilter().getClass())) {
215                 IoFilter oldFilter = e.getFilter();
216                 deregister(e);
217                 return oldFilter;
218             }
219             e = e.nextEntry;
220         }
221         throw new IllegalArgumentException("Filter not found: "
222                 + filterType.getName());
223     }
224 
225     public synchronized IoFilter replace(String name, IoFilter newFilter) {
226         EntryImpl entry = checkOldName(name);
227         IoFilter oldFilter = entry.getFilter();
228         entry.setFilter(newFilter);
229         return oldFilter;
230     }
231 
232     public synchronized void replace(IoFilter oldFilter, IoFilter newFilter) {
233         EntryImpl e = head.nextEntry;
234         while (e != tail) {
235             if (e.getFilter() == oldFilter) {
236                 e.setFilter(newFilter);
237                 return;
238             }
239             e = e.nextEntry;
240         }
241         throw new IllegalArgumentException("Filter not found: "
242                 + oldFilter.getClass().getName());
243     }
244 
245     public synchronized IoFilter replace(
246             Class<? extends IoFilter> oldFilterType, IoFilter newFilter) {
247         EntryImpl e = head.nextEntry;
248         while (e != tail) {
249             if (oldFilterType.isAssignableFrom(e.getFilter().getClass())) {
250                 IoFilter oldFilter = e.getFilter();
251                 e.setFilter(newFilter);
252                 return oldFilter;
253             }
254             e = e.nextEntry;
255         }
256         throw new IllegalArgumentException("Filter not found: "
257                 + oldFilterType.getName());
258     }
259 
260     public synchronized void clear() throws Exception {
261         Iterator<String> it = new ArrayList<String>(name2entry.keySet())
262                 .iterator();
263         while (it.hasNext()) {
264             String name = it.next();
265             if (contains(name)) {
266                 remove(name);
267             }
268         }
269     }
270 
271     private void register(EntryImpl prevEntry, String name, IoFilter filter) {
272         EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry,
273                 name, filter);
274 
275         try {
276             filter.onPreAdd(this, name, newEntry.getNextFilter());
277         } catch (Exception e) {
278             throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':'
279                     + filter + " in " + getSession(), e);
280         }
281 
282         prevEntry.nextEntry.prevEntry = newEntry;
283         prevEntry.nextEntry = newEntry;
284         name2entry.put(name, newEntry);
285 
286         try {
287             filter.onPostAdd(this, name, newEntry.getNextFilter());
288         } catch (Exception e) {
289             deregister0(newEntry);
290             throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':'
291                     + filter + " in " + getSession(), e);
292         }
293     }
294 
295     private void deregister(EntryImpl entry) {
296         IoFilter filter = entry.getFilter();
297 
298         try {
299             filter.onPreRemove(this, entry.getName(), entry.getNextFilter());
300         } catch (Exception e) {
301             throw new IoFilterLifeCycleException("onPreRemove(): "
302                     + entry.getName() + ':' + filter + " in " + getSession(), e);
303         }
304 
305         deregister0(entry);
306 
307         try {
308             filter.onPostRemove(this, entry.getName(), entry.getNextFilter());
309         } catch (Exception e) {
310             throw new IoFilterLifeCycleException("onPostRemove(): "
311                     + entry.getName() + ':' + filter + " in " + getSession(), e);
312         }
313     }
314 
315     private void deregister0(EntryImpl entry) {
316         EntryImpl prevEntry = entry.prevEntry;
317         EntryImpl nextEntry = entry.nextEntry;
318         prevEntry.nextEntry = nextEntry;
319         nextEntry.prevEntry = prevEntry;
320 
321         name2entry.remove(entry.name);
322     }
323 
324     /**
325      * Throws an exception when the specified filter name is not registered in this chain.
326      *
327      * @return An filter entry with the specified name.
328      */
329     private EntryImpl checkOldName(String baseName) {
330         EntryImpl e = (EntryImpl) name2entry.get(baseName);
331         if (e == null) {
332             throw new IllegalArgumentException("Filter not found:" + baseName);
333         }
334         return e;
335     }
336 
337     /**
338      * Checks the specified filter name is already taken and throws an exception if already taken.
339      */
340     private void checkAddable(String name) {
341         if (name2entry.containsKey(name)) {
342             throw new IllegalArgumentException(
343                     "Other filter is using the same name '" + name + "'");
344         }
345     }
346 
347     public void fireSessionCreated() {
348         Entry head = this.head;
349         callNextSessionCreated(head, session);
350     }
351 
352     private void callNextSessionCreated(Entry entry, IoSession session) {
353         try {
354             IoFilter filter = entry.getFilter();
355             NextFilter nextFilter = entry.getNextFilter();
356             filter.sessionCreated(nextFilter, session);
357         } catch (Throwable e) {
358             fireExceptionCaught(e);
359         }
360     }
361 
362     public void fireSessionOpened() {
363         Entry head = this.head;
364         callNextSessionOpened(head, session);
365     }
366 
367     private void callNextSessionOpened(Entry entry, IoSession session) {
368         try {
369             IoFilter filter = entry.getFilter();
370             NextFilter nextFilter = entry.getNextFilter();
371             filter.sessionOpened(nextFilter, session);
372         } catch (Throwable e) {
373             fireExceptionCaught(e);
374         }
375     }
376 
377     public void fireSessionClosed() {
378         // Update future.
379         try {
380             session.getCloseFuture().setClosed();
381         } catch (Throwable t) {
382             fireExceptionCaught(t);
383         }
384 
385         // And start the chain.
386         Entry head = this.head;
387         callNextSessionClosed(head, session);
388     }
389 
390     private void callNextSessionClosed(Entry entry, IoSession session) {
391         try {
392             IoFilter filter = entry.getFilter();
393             NextFilter nextFilter = entry.getNextFilter();
394             filter.sessionClosed(nextFilter, session);
395         } catch (Throwable e) {
396             fireExceptionCaught(e);
397         }
398     }
399 
400     public void fireSessionIdle(IdleStatus status) {
401         session.increaseIdleCount(status, System.currentTimeMillis());
402         Entry head = this.head;
403         callNextSessionIdle(head, session, status);
404     }
405 
406     private void callNextSessionIdle(Entry entry, IoSession session,
407             IdleStatus status) {
408         try {
409             IoFilter filter = entry.getFilter();
410             NextFilter nextFilter = entry.getNextFilter();
411             filter.sessionIdle(nextFilter, session,
412                     status);
413         } catch (Throwable e) {
414             fireExceptionCaught(e);
415         }
416     }
417 
418     public void fireMessageReceived(Object message) {
419         if (message instanceof IoBuffer) {
420             session.increaseReadBytes(((IoBuffer) message).remaining(), System
421                     .currentTimeMillis());
422         }
423 
424         Entry head = this.head;
425         callNextMessageReceived(head, session, message);
426     }
427 
428     private void callNextMessageReceived(Entry entry, IoSession session,
429             Object message) {
430         try {
431             IoFilter filter = entry.getFilter();
432             NextFilter nextFilter = entry.getNextFilter();
433             filter.messageReceived(nextFilter, session,
434                     message);
435         } catch (Throwable e) {
436             fireExceptionCaught(e);
437         }
438     }
439 
440     public void fireMessageSent(WriteRequest request) {
441         session.increaseWrittenMessages(request, System.currentTimeMillis());
442 
443         try {
444             request.getFuture().setWritten();
445         } catch (Throwable t) {
446             fireExceptionCaught(t);
447         }
448 
449         Entry head = this.head;
450         callNextMessageSent(head, session, request);
451     }
452 
453     private void callNextMessageSent(Entry entry, IoSession session,
454             WriteRequest writeRequest) {
455         try {
456             IoFilter filter = entry.getFilter();
457             NextFilter nextFilter = entry.getNextFilter();
458             filter.messageSent(nextFilter, session,
459                     writeRequest);
460         } catch (Throwable e) {
461             fireExceptionCaught(e);
462         }
463     }
464 
465     public void fireExceptionCaught(Throwable cause) {
466         Entry head = this.head;
467         callNextExceptionCaught(head, session, cause);
468     }
469 
470     private void callNextExceptionCaught(Entry entry, IoSession session,
471             Throwable cause) {
472         // Notify the related future.
473         ConnectFuture future = (ConnectFuture) session
474                 .removeAttribute(SESSION_CREATED_FUTURE);
475         if (future == null) {
476             try {
477                 IoFilter filter = entry.getFilter();
478                 NextFilter nextFilter = entry.getNextFilter();
479                 filter.exceptionCaught(nextFilter,
480                         session, cause);
481             } catch (Throwable e) {
482                 LOGGER
483                         .warn(
484                                 "Unexpected exception from exceptionCaught handler.",
485                                 e);
486             }
487         } else {
488             // Please note that this place is not the only place that
489             // calls ConnectFuture.setException().
490             session.close(true);
491             future.setException(cause);
492         }
493     }
494 
495     public void fireFilterWrite(WriteRequest writeRequest) {
496         Entry tail = this.tail;
497         callPreviousFilterWrite(tail, session, writeRequest);
498     }
499 
500     private void callPreviousFilterWrite(Entry entry, IoSession session,
501             WriteRequest writeRequest) {
502         try {
503             IoFilter filter = entry.getFilter();
504             NextFilter nextFilter = entry.getNextFilter();
505             filter.filterWrite(nextFilter, session, writeRequest);
506         } catch (Throwable e) {
507             writeRequest.getFuture().setException(e);
508             fireExceptionCaught(e);
509         }
510     }
511 
512     public void fireFilterClose() {
513         Entry tail = this.tail;
514         callPreviousFilterClose(tail, session);
515     }
516 
517     private void callPreviousFilterClose(Entry entry, IoSession session) {
518         try {
519             IoFilter filter = entry.getFilter();
520             NextFilter nextFilter = entry.getNextFilter();
521             filter.filterClose(nextFilter, session);
522         } catch (Throwable e) {
523             fireExceptionCaught(e);
524         }
525     }
526 
527     public List<Entry> getAll() {
528         List<Entry> list = new ArrayList<Entry>();
529         EntryImpl e = head.nextEntry;
530         while (e != tail) {
531             list.add(e);
532             e = e.nextEntry;
533         }
534 
535         return list;
536     }
537 
538     public List<Entry> getAllReversed() {
539         List<Entry> list = new ArrayList<Entry>();
540         EntryImpl e = tail.prevEntry;
541         while (e != head) {
542             list.add(e);
543             e = e.prevEntry;
544         }
545         return list;
546     }
547 
548     public boolean contains(String name) {
549         return getEntry(name) != null;
550     }
551 
552     public boolean contains(IoFilter filter) {
553         return getEntry(filter) != null;
554     }
555 
556     public boolean contains(Class<? extends IoFilter> filterType) {
557         return getEntry(filterType) != null;
558     }
559 
560     @Override
561     public String toString() {
562         StringBuilder buf = new StringBuilder();
563         buf.append("{ ");
564 
565         boolean empty = true;
566 
567         EntryImpl e = head.nextEntry;
568         while (e != tail) {
569             if (!empty) {
570                 buf.append(", ");
571             } else {
572                 empty = false;
573             }
574 
575             buf.append('(');
576             buf.append(e.getName());
577             buf.append(':');
578             buf.append(e.getFilter());
579             buf.append(')');
580 
581             e = e.nextEntry;
582         }
583 
584         if (empty) {
585             buf.append("empty");
586         }
587 
588         buf.append(" }");
589 
590         return buf.toString();
591     }
592 
593     @Override
594     protected void finalize() throws Throwable {
595         try {
596             this.clear();
597         } finally {
598             super.finalize();
599         }
600     }
601 
602     private class HeadFilter extends IoFilterAdapter {
603         @SuppressWarnings("unchecked")
604         @Override
605         public void filterWrite(NextFilter nextFilter, IoSession session,
606                 WriteRequest writeRequest) throws Exception {
607 
608             AbstractIoSession s = (AbstractIoSession) session;
609 
610             // Maintain counters.
611             if (writeRequest.getMessage() instanceof IoBuffer) {
612                 IoBuffer buffer = (IoBuffer) writeRequest.getMessage();
613                 // I/O processor implementation will call buffer.reset()
614                 // it after the write operation is finished, because
615                 // the buffer will be specified with messageSent event.
616                 buffer.mark();
617                 int remaining = buffer.remaining();
618                 if (remaining == 0) {
619                     // Zero-sized buffer means the internal message
620                     // delimiter.
621                     s.increaseScheduledWriteMessages();
622                 } else {
623                     s.increaseScheduledWriteBytes(remaining);
624                 }
625             } else {
626                 s.increaseScheduledWriteMessages();
627             }
628 
629             s.getWriteRequestQueue().offer(s, writeRequest);
630             if (!s.isWriteSuspended()) {
631                 s.getProcessor().flush(s);
632             }
633         }
634 
635         @SuppressWarnings("unchecked")
636         @Override
637         public void filterClose(NextFilter nextFilter, IoSession session)
638                 throws Exception {
639             ((AbstractIoSession) session).getProcessor().remove(((AbstractIoSession) session));
640         }
641     }
642 
643     private static class TailFilter extends IoFilterAdapter {
644         @Override
645         public void sessionCreated(NextFilter nextFilter, IoSession session)
646                 throws Exception {
647             try {
648                 session.getHandler().sessionCreated(session);
649             } finally {
650                 // Notify the related future.
651                 ConnectFuture future = (ConnectFuture) session
652                         .removeAttribute(SESSION_CREATED_FUTURE);
653                 if (future != null) {
654                     future.setSession(session);
655                 }
656             }
657         }
658 
659         @Override
660         public void sessionOpened(NextFilter nextFilter, IoSession session)
661                 throws Exception {
662             session.getHandler().sessionOpened(session);
663         }
664 
665         @Override
666         public void sessionClosed(NextFilter nextFilter, IoSession session)
667                 throws Exception {
668             AbstractIoSession s = (AbstractIoSession) session;
669             try {
670                 s.getHandler().sessionClosed(session);
671             } finally {
672                 try {
673                     s.getWriteRequestQueue().dispose(session);
674                 } finally {
675                     try {
676                         s.getAttributeMap().dispose(session);
677                     } finally {
678                         try {
679                             // Remove all filters.
680                             session.getFilterChain().clear();
681                         } finally {
682                             if (s.getConfig().isUseReadOperation()) {
683                                 s.offerClosedReadFuture();
684                             }
685                         }
686                     }
687                 }
688             }
689         }
690 
691         @Override
692         public void sessionIdle(NextFilter nextFilter, IoSession session,
693                 IdleStatus status) throws Exception {
694             session.getHandler().sessionIdle(session, status);
695         }
696 
697         @Override
698         public void exceptionCaught(NextFilter nextFilter, IoSession session,
699                 Throwable cause) throws Exception {
700             AbstractIoSession s = (AbstractIoSession) session;
701             try {
702                 s.getHandler().exceptionCaught(s, cause);
703             } finally {
704                 if (s.getConfig().isUseReadOperation()) {
705                     s.offerFailedReadFuture(cause);
706                 }
707             }
708         }
709 
710         @Override
711         public void messageReceived(NextFilter nextFilter, IoSession session,
712                 Object message) throws Exception {
713             AbstractIoSession s = (AbstractIoSession) session;
714             if (!(message instanceof IoBuffer)) {
715                 s.increaseReadMessages(System.currentTimeMillis());
716             } else if (!((IoBuffer) message).hasRemaining()) {
717                 s.increaseReadMessages(System.currentTimeMillis());
718             }
719 
720             try {
721                 session.getHandler().messageReceived(s, message);
722             } finally {
723                 if (s.getConfig().isUseReadOperation()) {
724                     s.offerReadFuture(message);
725                 }
726             }
727         }
728 
729         @Override
730         public void messageSent(NextFilter nextFilter, IoSession session,
731                 WriteRequest writeRequest) throws Exception {
732             session.getHandler()
733                     .messageSent(session, writeRequest.getMessage());
734         }
735 
736         @Override
737         public void filterWrite(NextFilter nextFilter, IoSession session,
738                 WriteRequest writeRequest) throws Exception {
739             nextFilter.filterWrite(session, writeRequest);
740         }
741 
742         @Override
743         public void filterClose(NextFilter nextFilter, IoSession session)
744                 throws Exception {
745             nextFilter.filterClose(session);
746         }
747     }
748 
749     private class EntryImpl implements Entry {
750         private EntryImpl prevEntry;
751 
752         private EntryImpl nextEntry;
753 
754         private final String name;
755 
756         private IoFilter filter;
757 
758         private final NextFilter nextFilter;
759 
760         private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry,
761                 String name, IoFilter filter) {
762             if (filter == null) {
763                 throw new NullPointerException("filter");
764             }
765             if (name == null) {
766                 throw new NullPointerException("name");
767             }
768 
769             this.prevEntry = prevEntry;
770             this.nextEntry = nextEntry;
771             this.name = name;
772             this.filter = filter;
773             this.nextFilter = new NextFilter() {
774                 public void sessionCreated(IoSession session) {
775                     Entry nextEntry = EntryImpl.this.nextEntry;
776                     callNextSessionCreated(nextEntry, session);
777                 }
778 
779                 public void sessionOpened(IoSession session) {
780                     Entry nextEntry = EntryImpl.this.nextEntry;
781                     callNextSessionOpened(nextEntry, session);
782                 }
783 
784                 public void sessionClosed(IoSession session) {
785                     Entry nextEntry = EntryImpl.this.nextEntry;
786                     callNextSessionClosed(nextEntry, session);
787                 }
788 
789                 public void sessionIdle(IoSession session, IdleStatus status) {
790                     Entry nextEntry = EntryImpl.this.nextEntry;
791                     callNextSessionIdle(nextEntry, session, status);
792                 }
793 
794                 public void exceptionCaught(IoSession session, Throwable cause) {
795                     Entry nextEntry = EntryImpl.this.nextEntry;
796                     callNextExceptionCaught(nextEntry, session, cause);
797                 }
798 
799                 public void messageReceived(IoSession session, Object message) {
800                     Entry nextEntry = EntryImpl.this.nextEntry;
801                     callNextMessageReceived(nextEntry, session, message);
802                 }
803 
804                 public void messageSent(IoSession session,
805                         WriteRequest writeRequest) {
806                     Entry nextEntry = EntryImpl.this.nextEntry;
807                     callNextMessageSent(nextEntry, session, writeRequest);
808                 }
809 
810                 public void filterWrite(IoSession session,
811                         WriteRequest writeRequest) {
812                     Entry nextEntry = EntryImpl.this.prevEntry;
813                     callPreviousFilterWrite(nextEntry, session, writeRequest);
814                 }
815 
816                 public void filterClose(IoSession session) {
817                     Entry nextEntry = EntryImpl.this.prevEntry;
818                     callPreviousFilterClose(nextEntry, session);
819                 }
820                 
821                 public String toString() {
822                     return EntryImpl.this.nextEntry.name;
823                 }
824             };
825         }
826 
827         public String getName() {
828             return name;
829         }
830 
831         public IoFilter getFilter() {
832             return filter;
833         }
834 
835         private void setFilter(IoFilter filter) {
836             if (filter == null) {
837                 throw new NullPointerException("filter");
838             }
839 
840             this.filter = filter;
841         }
842 
843         public NextFilter getNextFilter() {
844             return nextFilter;
845         }
846 
847         @Override
848         public String toString() {
849             StringBuilder sb = new StringBuilder();
850             
851             // Add the current filter
852             sb.append("('").append(getName()).append('\'');
853             
854             // Add the previous filter 
855             sb.append(", prev: '");
856 
857             if (prevEntry != null) {
858                 sb.append(prevEntry.name);
859                 sb.append(':');
860                 sb.append(prevEntry.getFilter().getClass().getSimpleName());
861             } else {
862                 sb.append("null");
863             }
864             
865             // Add the next filter 
866             sb.append("', next: '");
867 
868             if (nextEntry != null) {
869                 sb.append(nextEntry.name);
870                 sb.append(':');
871                 sb.append(nextEntry.getFilter().getClass().getSimpleName());
872             } else {
873                 sb.append("null");
874             }
875             
876             sb.append("')");
877             return sb.toString();
878         }
879 
880         public void addAfter(String name, IoFilter filter) {
881             DefaultIoFilterChain.this.addAfter(getName(), name, filter);
882         }
883 
884         public void addBefore(String name, IoFilter filter) {
885             DefaultIoFilterChain.this.addBefore(getName(), name, filter);
886         }
887 
888         public void remove() {
889             DefaultIoFilterChain.this.remove(getName());
890         }
891 
892         public void replace(IoFilter newFilter) {
893             DefaultIoFilterChain.this.replace(getName(), newFilter);
894         }
895     }
896 }