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