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