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