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             if (!session.isClosing()) {
598                 // Call the closeNow method only if needed
599                 session.closeNow();
600             }
601             
602             future.setException(cause);
603         }
604     }
605 
606     public void fireInputClosed() {
607         Entry head = this.head;
608         callNextInputClosed(head, session);
609     }
610 
611     private void callNextInputClosed(Entry entry, IoSession session) {
612         try {
613             IoFilter filter = entry.getFilter();
614             NextFilter nextFilter = entry.getNextFilter();
615             filter.inputClosed(nextFilter, session);
616         } catch (Throwable e) {
617             fireExceptionCaught(e);
618         }
619     }
620 
621     public void fireFilterWrite(WriteRequest writeRequest) {
622         callPreviousFilterWrite(tail, session, writeRequest);
623     }
624 
625     private void callPreviousFilterWrite(Entry entry, IoSession session, WriteRequest writeRequest) {
626         try {
627             IoFilter filter = entry.getFilter();
628             NextFilter nextFilter = entry.getNextFilter();
629             filter.filterWrite(nextFilter, session, writeRequest);
630         } catch (Exception e) {
631             writeRequest.getFuture().setException(e);
632             fireExceptionCaught(e);
633         } catch (Error e) {
634             writeRequest.getFuture().setException(e);
635             fireExceptionCaught(e);
636             throw e;
637         }
638     }
639 
640     public void fireFilterClose() {
641         callPreviousFilterClose(tail, session);
642     }
643 
644     private void callPreviousFilterClose(Entry entry, IoSession session) {
645         try {
646             IoFilter filter = entry.getFilter();
647             NextFilter nextFilter = entry.getNextFilter();
648             filter.filterClose(nextFilter, session);
649         } catch (Exception e) {
650             fireExceptionCaught(e);
651         } catch (Error e) {
652             fireExceptionCaught(e);
653             throw e;
654         }
655     }
656 
657     public List<Entry> getAll() {
658         List<Entry> list = new ArrayList<Entry>();
659         EntryImpl e = head.nextEntry;
660 
661         while (e != tail) {
662             list.add(e);
663             e = e.nextEntry;
664         }
665 
666         return list;
667     }
668 
669     public List<Entry> getAllReversed() {
670         List<Entry> list = new ArrayList<Entry>();
671         EntryImpl e = tail.prevEntry;
672 
673         while (e != head) {
674             list.add(e);
675             e = e.prevEntry;
676         }
677 
678         return list;
679     }
680 
681     public boolean contains(String name) {
682         return getEntry(name) != null;
683     }
684 
685     public boolean contains(IoFilter filter) {
686         return getEntry(filter) != null;
687     }
688 
689     public boolean contains(Class<? extends IoFilter> filterType) {
690         return getEntry(filterType) != null;
691     }
692 
693     @Override
694     public String toString() {
695         StringBuilder buf = new StringBuilder();
696         buf.append("{ ");
697 
698         boolean empty = true;
699 
700         EntryImpl e = head.nextEntry;
701 
702         while (e != tail) {
703             if (!empty) {
704                 buf.append(", ");
705             } else {
706                 empty = false;
707             }
708 
709             buf.append('(');
710             buf.append(e.getName());
711             buf.append(':');
712             buf.append(e.getFilter());
713             buf.append(')');
714 
715             e = e.nextEntry;
716         }
717 
718         if (empty) {
719             buf.append("empty");
720         }
721 
722         buf.append(" }");
723 
724         return buf.toString();
725     }
726 
727     private class HeadFilter extends IoFilterAdapter {
728         @SuppressWarnings("unchecked")
729         @Override
730         public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
731 
732             AbstractIoSession s = (AbstractIoSession) session;
733 
734             // Maintain counters.
735             if (writeRequest.getMessage() instanceof IoBuffer) {
736                 IoBuffer buffer = (IoBuffer) writeRequest.getMessage();
737                 // I/O processor implementation will call buffer.reset()
738                 // it after the write operation is finished, because
739                 // the buffer will be specified with messageSent event.
740                 buffer.mark();
741                 int remaining = buffer.remaining();
742 
743                 if (remaining > 0) {
744                     s.increaseScheduledWriteBytes(remaining);
745                 }
746             } else {
747                 s.increaseScheduledWriteMessages();
748             }
749 
750             WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();
751 
752             if (!s.isWriteSuspended()) {
753                 if (writeRequestQueue.isEmpty(session)) {
754                     // We can write directly the message
755                     s.getProcessor().write(s, writeRequest);
756                 } else {
757                     s.getWriteRequestQueue().offer(s, writeRequest);
758                     s.getProcessor().flush(s);
759                 }
760             } else {
761                 s.getWriteRequestQueue().offer(s, writeRequest);
762             }
763         }
764 
765         @SuppressWarnings("unchecked")
766         @Override
767         public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
768             ((AbstractIoSession) session).getProcessor().remove(session);
769         }
770     }
771 
772     private static class TailFilter extends IoFilterAdapter {
773         @Override
774         public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
775             try {
776                 session.getHandler().sessionCreated(session);
777             } finally {
778                 // Notify the related future.
779                 ConnectFuture future = (ConnectFuture) session.removeAttribute(SESSION_CREATED_FUTURE);
780 
781                 if (future != null) {
782                     future.setSession(session);
783                 }
784             }
785         }
786 
787         @Override
788         public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
789             session.getHandler().sessionOpened(session);
790         }
791 
792         @Override
793         public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
794             AbstractIoSession s = (AbstractIoSession) session;
795 
796             try {
797                 s.getHandler().sessionClosed(session);
798             } finally {
799                 try {
800                     s.getWriteRequestQueue().dispose(session);
801                 } finally {
802                     try {
803                         s.getAttributeMap().dispose(session);
804                     } finally {
805                         try {
806                             // Remove all filters.
807                             session.getFilterChain().clear();
808                         } finally {
809                             if (s.getConfig().isUseReadOperation()) {
810                                 s.offerClosedReadFuture();
811                             }
812                         }
813                     }
814                 }
815             }
816         }
817 
818         @Override
819         public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {
820             session.getHandler().sessionIdle(session, status);
821         }
822 
823         @Override
824         public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
825             AbstractIoSession s = (AbstractIoSession) session;
826 
827             try {
828                 s.getHandler().exceptionCaught(s, cause);
829             } finally {
830                 if (s.getConfig().isUseReadOperation()) {
831                     s.offerFailedReadFuture(cause);
832                 }
833             }
834         }
835 
836         @Override
837         public void inputClosed(NextFilter nextFilter, IoSession session) throws Exception {
838             session.getHandler().inputClosed(session);
839         }
840 
841         @Override
842         public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
843             AbstractIoSession s = (AbstractIoSession) session;
844 
845             if (!(message instanceof IoBuffer)) {
846                 s.increaseReadMessages(System.currentTimeMillis());
847             } else if (!((IoBuffer) message).hasRemaining()) {
848                 s.increaseReadMessages(System.currentTimeMillis());
849             }
850 
851             // Update the statistics
852             if (session.getService() instanceof AbstractIoService) {
853                 ((AbstractIoService) session.getService()).getStatistics().updateThroughput(System.currentTimeMillis());
854             }
855 
856             // Propagate the message
857             try {
858                 session.getHandler().messageReceived(s, message);
859             } finally {
860                 if (s.getConfig().isUseReadOperation()) {
861                     s.offerReadFuture(message);
862                 }
863             }
864         }
865 
866         @Override
867         public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
868             ((AbstractIoSession) session).increaseWrittenMessages(writeRequest, System.currentTimeMillis());
869 
870             // Update the statistics
871             if (session.getService() instanceof AbstractIoService) {
872                 ((AbstractIoService) session.getService()).getStatistics().updateThroughput(System.currentTimeMillis());
873             }
874 
875             // Propagate the message
876             session.getHandler().messageSent(session, writeRequest.getMessage());
877         }
878 
879         @Override
880         public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
881             nextFilter.filterWrite(session, writeRequest);
882         }
883 
884         @Override
885         public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
886             nextFilter.filterClose(session);
887         }
888     }
889 
890     private final class EntryImpl implements Entry {
891         private EntryImpl prevEntry;
892 
893         private EntryImpl nextEntry;
894 
895         private final String name;
896 
897         private IoFilter filter;
898 
899         private final NextFilter nextFilter;
900 
901         private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) {
902             if (filter == null) {
903                 throw new IllegalArgumentException("filter");
904             }
905 
906             if (name == null) {
907                 throw new IllegalArgumentException("name");
908             }
909 
910             this.prevEntry = prevEntry;
911             this.nextEntry = nextEntry;
912             this.name = name;
913             this.filter = filter;
914             this.nextFilter = new NextFilter() {
915                 public void sessionCreated(IoSession session) {
916                     Entry nextEntry = EntryImpl.this.nextEntry;
917                     callNextSessionCreated(nextEntry, session);
918                 }
919 
920                 public void sessionOpened(IoSession session) {
921                     Entry nextEntry = EntryImpl.this.nextEntry;
922                     callNextSessionOpened(nextEntry, session);
923                 }
924 
925                 public void sessionClosed(IoSession session) {
926                     Entry nextEntry = EntryImpl.this.nextEntry;
927                     callNextSessionClosed(nextEntry, session);
928                 }
929 
930                 public void sessionIdle(IoSession session, IdleStatus status) {
931                     Entry nextEntry = EntryImpl.this.nextEntry;
932                     callNextSessionIdle(nextEntry, session, status);
933                 }
934 
935                 public void exceptionCaught(IoSession session, Throwable cause) {
936                     Entry nextEntry = EntryImpl.this.nextEntry;
937                     callNextExceptionCaught(nextEntry, session, cause);
938                 }
939 
940                 public void inputClosed(IoSession session) {
941                     Entry nextEntry = EntryImpl.this.nextEntry;
942                     callNextInputClosed(nextEntry, session);
943                 }
944 
945                 public void messageReceived(IoSession session, Object message) {
946                     Entry nextEntry = EntryImpl.this.nextEntry;
947                     callNextMessageReceived(nextEntry, session, message);
948                 }
949 
950                 public void messageSent(IoSession session, WriteRequest writeRequest) {
951                     Entry nextEntry = EntryImpl.this.nextEntry;
952                     callNextMessageSent(nextEntry, session, writeRequest);
953                 }
954 
955                 public void filterWrite(IoSession session, WriteRequest writeRequest) {
956                     Entry nextEntry = EntryImpl.this.prevEntry;
957                     callPreviousFilterWrite(nextEntry, session, writeRequest);
958                 }
959 
960                 public void filterClose(IoSession session) {
961                     Entry nextEntry = EntryImpl.this.prevEntry;
962                     callPreviousFilterClose(nextEntry, session);
963                 }
964 
965                 public String toString() {
966                     return EntryImpl.this.nextEntry.name;
967                 }
968             };
969         }
970 
971         public String getName() {
972             return name;
973         }
974 
975         public IoFilter getFilter() {
976             return filter;
977         }
978 
979         private void setFilter(IoFilter filter) {
980             if (filter == null) {
981                 throw new IllegalArgumentException("filter");
982             }
983 
984             this.filter = filter;
985         }
986 
987         public NextFilter getNextFilter() {
988             return nextFilter;
989         }
990 
991         @Override
992         public String toString() {
993             StringBuilder sb = new StringBuilder();
994 
995             // Add the current filter
996             sb.append("('").append(getName()).append('\'');
997 
998             // Add the previous filter
999             sb.append(", prev: '");
1000 
1001             if (prevEntry != null) {
1002                 sb.append(prevEntry.name);
1003                 sb.append(':');
1004                 sb.append(prevEntry.getFilter().getClass().getSimpleName());
1005             } else {
1006                 sb.append("null");
1007             }
1008 
1009             // Add the next filter
1010             sb.append("', next: '");
1011 
1012             if (nextEntry != null) {
1013                 sb.append(nextEntry.name);
1014                 sb.append(':');
1015                 sb.append(nextEntry.getFilter().getClass().getSimpleName());
1016             } else {
1017                 sb.append("null");
1018             }
1019 
1020             sb.append("')");
1021 
1022             return sb.toString();
1023         }
1024 
1025         public void addAfter(String name, IoFilter filter) {
1026             DefaultIoFilterChain.this.addAfter(getName(), name, filter);
1027         }
1028 
1029         public void addBefore(String name, IoFilter filter) {
1030             DefaultIoFilterChain.this.addBefore(getName(), name, filter);
1031         }
1032 
1033         public void remove() {
1034             DefaultIoFilterChain.this.remove(getName());
1035         }
1036 
1037         public void replace(IoFilter newFilter) {
1038             DefaultIoFilterChain.this.replace(getName(), newFilter);
1039         }
1040     }
1041 }