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                     s.increaseScheduledWriteBytes(remaining);
588                 }
589             } else {
590                 s.increaseScheduledWriteMessages();
591             }
592 
593             WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();
594 
595             if (!s.isWriteSuspended()) {
596                 if (writeRequestQueue.size() == 0) {
597                     // We can write directly the message
598                     s.getProcessor().write(s, writeRequest);
599                 } else {
600                     s.getWriteRequestQueue().offer(s, writeRequest);
601                     s.getProcessor().flush(s);
602                 }
603             } else {
604                 s.getWriteRequestQueue().offer(s, writeRequest);
605             }
606         }
607 
608         @SuppressWarnings("unchecked")
609         @Override
610         public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
611             ((AbstractIoSession) session).getProcessor().remove(session);
612         }
613     }
614 
615     private static class TailFilter extends IoFilterAdapter {
616         @Override
617         public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
618             try {
619                 session.getHandler().sessionCreated(session);
620             } finally {
621                 // Notify the related future.
622                 ConnectFuture future = (ConnectFuture) session.removeAttribute(SESSION_CREATED_FUTURE);
623                 if (future != null) {
624                     future.setSession(session);
625                 }
626             }
627         }
628 
629         @Override
630         public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
631             session.getHandler().sessionOpened(session);
632         }
633 
634         @Override
635         public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
636             AbstractIoSession s = (AbstractIoSession) session;
637             try {
638                 s.getHandler().sessionClosed(session);
639             } finally {
640                 try {
641                     s.getWriteRequestQueue().dispose(session);
642                 } finally {
643                     try {
644                         s.getAttributeMap().dispose(session);
645                     } finally {
646                         try {
647                             // Remove all filters.
648                             session.getFilterChain().clear();
649                         } finally {
650                             if (s.getConfig().isUseReadOperation()) {
651                                 s.offerClosedReadFuture();
652                             }
653                         }
654                     }
655                 }
656             }
657         }
658 
659         @Override
660         public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {
661             session.getHandler().sessionIdle(session, status);
662         }
663 
664         @Override
665         public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
666             AbstractIoSession s = (AbstractIoSession) session;
667             try {
668                 s.getHandler().exceptionCaught(s, cause);
669             } finally {
670                 if (s.getConfig().isUseReadOperation()) {
671                     s.offerFailedReadFuture(cause);
672                 }
673             }
674         }
675 
676         @Override
677         public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
678             AbstractIoSession s = (AbstractIoSession) session;
679             if (!(message instanceof IoBuffer)) {
680                 s.increaseReadMessages(System.currentTimeMillis());
681             } else if (!((IoBuffer) message).hasRemaining()) {
682                 s.increaseReadMessages(System.currentTimeMillis());
683             }
684 
685             try {
686                 session.getHandler().messageReceived(s, message);
687             } finally {
688                 if (s.getConfig().isUseReadOperation()) {
689                     s.offerReadFuture(message);
690                 }
691             }
692         }
693 
694         @Override
695         public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
696             session.getHandler().messageSent(session, writeRequest.getMessage());
697         }
698 
699         @Override
700         public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
701             nextFilter.filterWrite(session, writeRequest);
702         }
703 
704         @Override
705         public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
706             nextFilter.filterClose(session);
707         }
708     }
709 
710     private class EntryImpl implements Entry {
711         private EntryImpl prevEntry;
712 
713         private EntryImpl nextEntry;
714 
715         private final String name;
716 
717         private IoFilter filter;
718 
719         private final NextFilter nextFilter;
720 
721         private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) {
722             if (filter == null) {
723                 throw new IllegalArgumentException("filter");
724             }
725             if (name == null) {
726                 throw new IllegalArgumentException("name");
727             }
728 
729             this.prevEntry = prevEntry;
730             this.nextEntry = nextEntry;
731             this.name = name;
732             this.filter = filter;
733             this.nextFilter = new NextFilter() {
734                 public void sessionCreated(IoSession session) {
735                     Entry nextEntry = EntryImpl.this.nextEntry;
736                     callNextSessionCreated(nextEntry, session);
737                 }
738 
739                 public void sessionOpened(IoSession session) {
740                     Entry nextEntry = EntryImpl.this.nextEntry;
741                     callNextSessionOpened(nextEntry, session);
742                 }
743 
744                 public void sessionClosed(IoSession session) {
745                     Entry nextEntry = EntryImpl.this.nextEntry;
746                     callNextSessionClosed(nextEntry, session);
747                 }
748 
749                 public void sessionIdle(IoSession session, IdleStatus status) {
750                     Entry nextEntry = EntryImpl.this.nextEntry;
751                     callNextSessionIdle(nextEntry, session, status);
752                 }
753 
754                 public void exceptionCaught(IoSession session, Throwable cause) {
755                     Entry nextEntry = EntryImpl.this.nextEntry;
756                     callNextExceptionCaught(nextEntry, session, cause);
757                 }
758 
759                 public void messageReceived(IoSession session, Object message) {
760                     Entry nextEntry = EntryImpl.this.nextEntry;
761                     callNextMessageReceived(nextEntry, session, message);
762                 }
763 
764                 public void messageSent(IoSession session, WriteRequest writeRequest) {
765                     Entry nextEntry = EntryImpl.this.nextEntry;
766                     callNextMessageSent(nextEntry, session, writeRequest);
767                 }
768 
769                 public void filterWrite(IoSession session, WriteRequest writeRequest) {
770                     Entry nextEntry = EntryImpl.this.prevEntry;
771                     callPreviousFilterWrite(nextEntry, session, writeRequest);
772                 }
773 
774                 public void filterClose(IoSession session) {
775                     Entry nextEntry = EntryImpl.this.prevEntry;
776                     callPreviousFilterClose(nextEntry, session);
777                 }
778 
779                 public String toString() {
780                     return EntryImpl.this.nextEntry.name;
781                 }
782             };
783         }
784 
785         public String getName() {
786             return name;
787         }
788 
789         public IoFilter getFilter() {
790             return filter;
791         }
792 
793         private void setFilter(IoFilter filter) {
794             if (filter == null) {
795                 throw new IllegalArgumentException("filter");
796             }
797 
798             this.filter = filter;
799         }
800 
801         public NextFilter getNextFilter() {
802             return nextFilter;
803         }
804 
805         @Override
806         public String toString() {
807             StringBuilder sb = new StringBuilder();
808 
809             // Add the current filter
810             sb.append("('").append(getName()).append('\'');
811 
812             // Add the previous filter
813             sb.append(", prev: '");
814 
815             if (prevEntry != null) {
816                 sb.append(prevEntry.name);
817                 sb.append(':');
818                 sb.append(prevEntry.getFilter().getClass().getSimpleName());
819             } else {
820                 sb.append("null");
821             }
822 
823             // Add the next filter
824             sb.append("', next: '");
825 
826             if (nextEntry != null) {
827                 sb.append(nextEntry.name);
828                 sb.append(':');
829                 sb.append(nextEntry.getFilter().getClass().getSimpleName());
830             } else {
831                 sb.append("null");
832             }
833 
834             sb.append("')");
835             return sb.toString();
836         }
837 
838         public void addAfter(String name, IoFilter filter) {
839             DefaultIoFilterChain.this.addAfter(getName(), name, filter);
840         }
841 
842         public void addBefore(String name, IoFilter filter) {
843             DefaultIoFilterChain.this.addBefore(getName(), name, filter);
844         }
845 
846         public void remove() {
847             DefaultIoFilterChain.this.remove(getName());
848         }
849 
850         public void replace(IoFilter newFilter) {
851             DefaultIoFilterChain.this.replace(getName(), newFilter);
852         }
853     }
854 }