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<>();
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 static final 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      /**
91       * {@inheritDoc}
92       */
93      @Override
94      public IoSession getSession() {
95          return session;
96      }
97  
98      /**
99       * {@inheritDoc}
100      */
101     @Override
102     public Entry getEntry(String name) {
103         Entry e = name2entry.get(name);
104 
105         if (e == null) {
106             return null;
107         }
108 
109         return e;
110     }
111 
112     /**
113      * {@inheritDoc}
114      */
115     @Override
116     public Entry getEntry(IoFilter filter) {
117         EntryImpl e = head.nextEntry;
118 
119         while (e != tail) {
120             if (e.getFilter() == filter) {
121                 return e;
122             }
123 
124             e = e.nextEntry;
125         }
126 
127         return null;
128     }
129 
130     /**
131      * {@inheritDoc}
132      */
133     @Override
134     public Entry getEntry(Class<? extends IoFilter> filterType) {
135         EntryImpl e = head.nextEntry;
136 
137         while (e != tail) {
138             if (filterType.isAssignableFrom(e.getFilter().getClass())) {
139                 return e;
140             }
141 
142             e = e.nextEntry;
143         }
144 
145         return null;
146     }
147 
148     /**
149      * {@inheritDoc}
150      */
151     @Override
152     public IoFilter get(String name) {
153         Entry e = getEntry(name);
154 
155         if (e == null) {
156             return null;
157         }
158 
159         return e.getFilter();
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     @Override
166     public IoFilter get(Class<? extends IoFilter> filterType) {
167         Entry e = getEntry(filterType);
168 
169         if (e == null) {
170             return null;
171         }
172 
173         return e.getFilter();
174     }
175 
176     /**
177      * {@inheritDoc}
178      */
179     @Override
180     public NextFilter getNextFilter(String name) {
181         Entry e = getEntry(name);
182 
183         if (e == null) {
184             return null;
185         }
186 
187         return e.getNextFilter();
188     }
189 
190     /**
191      * {@inheritDoc}
192      */
193     @Override
194     public NextFilter getNextFilter(IoFilter filter) {
195         Entry e = getEntry(filter);
196 
197         if (e == null) {
198             return null;
199         }
200 
201         return e.getNextFilter();
202     }
203 
204     /**
205      * {@inheritDoc}
206      */
207     @Override
208     public NextFilter getNextFilter(Class<? extends IoFilter> filterType) {
209         Entry e = getEntry(filterType);
210 
211         if (e == null) {
212             return null;
213         }
214 
215         return e.getNextFilter();
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     @Override
222     public synchronized void addFirst(String name, IoFilter filter) {
223         checkAddable(name);
224         register(head, name, filter);
225     }
226 
227     /**
228      * {@inheritDoc}
229      */
230     @Override
231     public synchronized void addLast(String name, IoFilter filter) {
232         checkAddable(name);
233         register(tail.prevEntry, name, filter);
234     }
235 
236     /**
237      * {@inheritDoc}
238      */
239     @Override
240     public synchronized void addBefore(String baseName, String name, IoFilter filter) {
241         EntryImpl baseEntry = checkOldName(baseName);
242         checkAddable(name);
243         register(baseEntry.prevEntry, name, filter);
244     }
245 
246     /**
247      * {@inheritDoc}
248      */
249     @Override
250     public synchronized void addAfter(String baseName, String name, IoFilter filter) {
251         EntryImpl baseEntry = checkOldName(baseName);
252         checkAddable(name);
253         register(baseEntry, name, filter);
254     }
255 
256     /**
257      * {@inheritDoc}
258      */
259     @Override
260     public synchronized IoFilter remove(String name) {
261         EntryImpl entry = checkOldName(name);
262         deregister(entry);
263         
264         return entry.getFilter();
265     }
266 
267     /**
268      * {@inheritDoc}
269      */
270     @Override
271     public synchronized void remove(IoFilter filter) {
272         EntryImpl e = head.nextEntry;
273 
274         while (e != tail) {
275             if (e.getFilter() == filter) {
276                 deregister(e);
277 
278                 return;
279             }
280 
281             e = e.nextEntry;
282         }
283 
284         throw new IllegalArgumentException("Filter not found: " + filter.getClass().getName());
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     @Override
291     public synchronized IoFilter remove(Class<? extends IoFilter> filterType) {
292         EntryImpl e = head.nextEntry;
293 
294         while (e != tail) {
295             if (filterType.isAssignableFrom(e.getFilter().getClass())) {
296                 IoFilter oldFilter = e.getFilter();
297                 deregister(e);
298 
299                 return oldFilter;
300             }
301 
302             e = e.nextEntry;
303         }
304 
305         throw new IllegalArgumentException("Filter not found: " + filterType.getName());
306     }
307 
308     /**
309      * {@inheritDoc}
310      */
311     @Override
312     public synchronized IoFilter replace(String name, IoFilter newFilter) {
313         EntryImpl entry = checkOldName(name);
314         IoFilter oldFilter = entry.getFilter();
315 
316         // Call the preAdd method of the new filter
317         try {
318             newFilter.onPreAdd(this, name, entry.getNextFilter());
319         } catch (Exception e) {
320             throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':' + newFilter + " in " + getSession(), e);
321         }
322 
323         // Now, register the new Filter replacing the old one.
324         entry.setFilter(newFilter);
325 
326         // Call the postAdd method of the new filter
327         try {
328             newFilter.onPostAdd(this, name, entry.getNextFilter());
329         } catch (Exception e) {
330             entry.setFilter(oldFilter);
331             throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':' + newFilter + " in " + getSession(), e);
332         }
333 
334         return oldFilter;
335     }
336 
337     /**
338      * {@inheritDoc}
339      */
340     @Override
341     public synchronized void replace(IoFilter oldFilter, IoFilter newFilter) {
342         EntryImpl entry = head.nextEntry;
343 
344         // Search for the filter to replace
345         while (entry != tail) {
346             if (entry.getFilter() == oldFilter) {
347                 String oldFilterName = null;
348 
349                 // Get the old filter name. It's not really efficient...
350                 for (Map.Entry<String, Entry> mapping : name2entry.entrySet()) {
351                     if (entry == mapping.getValue() ) {
352                         oldFilterName = mapping.getKey();
353 
354                         break;
355                     }
356                 }
357 
358                 // Call the preAdd method of the new filter
359                 try {
360                     newFilter.onPreAdd(this, oldFilterName, entry.getNextFilter());
361                 } catch (Exception e) {
362                     throw new IoFilterLifeCycleException("onPreAdd(): " + oldFilterName + ':' + newFilter + " in "
363                             + getSession(), e);
364                 }
365 
366                 // Now, register the new Filter replacing the old one.
367                 entry.setFilter(newFilter);
368 
369                 // Call the postAdd method of the new filter
370                 try {
371                     newFilter.onPostAdd(this, oldFilterName, entry.getNextFilter());
372                 } catch (Exception e) {
373                     entry.setFilter(oldFilter);
374                     throw new IoFilterLifeCycleException("onPostAdd(): " + oldFilterName + ':' + newFilter + " in "
375                             + getSession(), e);
376                 }
377 
378                 return;
379             }
380 
381             entry = entry.nextEntry;
382         }
383 
384         throw new IllegalArgumentException("Filter not found: " + oldFilter.getClass().getName());
385     }
386 
387     /**
388      * {@inheritDoc}
389      */
390     @Override
391     public synchronized IoFilter replace(Class<? extends IoFilter> oldFilterType, IoFilter newFilter) {
392         EntryImpl entry = head.nextEntry;
393 
394         while (entry != tail) {
395             if (oldFilterType.isAssignableFrom(entry.getFilter().getClass())) {
396                 IoFilter oldFilter = entry.getFilter();
397 
398                 String oldFilterName = null;
399 
400                 // Get the old filter name. It's not really efficient...
401                 for (Map.Entry<String, Entry> mapping : name2entry.entrySet()) {
402                     if (entry == mapping.getValue() ) {
403                         oldFilterName = mapping.getKey();
404 
405                         break;
406                     }
407                 }
408 
409                 // Call the preAdd method of the new filter
410                 try {
411                     newFilter.onPreAdd(this, oldFilterName, entry.getNextFilter());
412                 } catch (Exception e) {
413                     throw new IoFilterLifeCycleException("onPreAdd(): " + oldFilterName + ':' + newFilter + " in "
414                             + getSession(), e);
415                 }
416 
417                 entry.setFilter(newFilter);
418 
419                 // Call the postAdd method of the new filter
420                 try {
421                     newFilter.onPostAdd(this, oldFilterName, entry.getNextFilter());
422                 } catch (Exception e) {
423                     entry.setFilter(oldFilter);
424                     throw new IoFilterLifeCycleException("onPostAdd(): " + oldFilterName + ':' + newFilter + " in "
425                             + getSession(), e);
426                 }
427 
428                 return oldFilter;
429             }
430 
431             entry = entry.nextEntry;
432         }
433 
434         throw new IllegalArgumentException("Filter not found: " + oldFilterType.getName());
435     }
436 
437     /**
438      * {@inheritDoc}
439      */
440     @Override
441     public synchronized void clear() throws Exception {
442         List<IoFilterChain.Entry> l = new ArrayList<>(name2entry.values());
443 
444         for (IoFilterChain.Entry entry : l) {
445             try {
446                 deregister((EntryImpl) entry);
447             } catch (Exception e) {
448                 throw new IoFilterLifeCycleException("clear(): " + entry.getName() + " in " + getSession(), e);
449             }
450         }
451     }
452 
453     /**
454      * Register the newly added filter, inserting it between the previous and
455      * the next filter in the filter's chain. We also call the preAdd and
456      * postAdd methods.
457      */
458     private void register(EntryImpl prevEntry, String name, IoFilter filter) {
459         EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry, name, filter);
460 
461         try {
462             filter.onPreAdd(this, name, newEntry.getNextFilter());
463         } catch (Exception e) {
464             throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':' + filter + " in " + getSession(), e);
465         }
466 
467         prevEntry.nextEntry.prevEntry = newEntry;
468         prevEntry.nextEntry = newEntry;
469         name2entry.put(name, newEntry);
470 
471         try {
472             filter.onPostAdd(this, name, newEntry.getNextFilter());
473         } catch (Exception e) {
474             deregister0(newEntry);
475             throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':' + filter + " in " + getSession(), e);
476         }
477     }
478 
479     private void deregister(EntryImpl entry) {
480         IoFilter filter = entry.getFilter();
481 
482         try {
483             filter.onPreRemove(this, entry.getName(), entry.getNextFilter());
484         } catch (Exception e) {
485             throw new IoFilterLifeCycleException("onPreRemove(): " + entry.getName() + ':' + filter + " in "
486                     + getSession(), e);
487         }
488 
489         deregister0(entry);
490 
491         try {
492             filter.onPostRemove(this, entry.getName(), entry.getNextFilter());
493         } catch (Exception e) {
494             throw new IoFilterLifeCycleException("onPostRemove(): " + entry.getName() + ':' + filter + " in "
495                     + getSession(), e);
496         }
497     }
498 
499     private void deregister0(EntryImpl entry) {
500         EntryImpl prevEntry = entry.prevEntry;
501         EntryImpl nextEntry = entry.nextEntry;
502         prevEntry.nextEntry = nextEntry;
503         nextEntry.prevEntry = prevEntry;
504 
505         name2entry.remove(entry.name);
506     }
507 
508     /**
509      * Throws an exception when the specified filter name is not registered in this chain.
510      *
511      * @return An filter entry with the specified name.
512      */
513     private EntryImpl checkOldName(String baseName) {
514         EntryImpl e = (EntryImpl) name2entry.get(baseName);
515 
516         if (e == null) {
517             throw new IllegalArgumentException("Filter not found:" + baseName);
518         }
519 
520         return e;
521     }
522 
523     /**
524      * Checks the specified filter name is already taken and throws an exception if already taken.
525      */
526     private void checkAddable(String name) {
527         if (name2entry.containsKey(name)) {
528             throw new IllegalArgumentException("Other filter is using the same name '" + name + "'");
529         }
530     }
531 
532     /**
533      * {@inheritDoc}
534      */
535     @Override
536     public void fireSessionCreated() {
537         callNextSessionCreated(head, session);
538     }
539 
540     private void callNextSessionCreated(Entry entry, IoSession session) {
541         try {
542             IoFilter filter = entry.getFilter();
543             NextFilter nextFilter = entry.getNextFilter();
544             filter.sessionCreated(nextFilter, session);
545         } catch (Exception e) {
546             fireExceptionCaught(e);
547         } catch (Error e) {
548             fireExceptionCaught(e);
549             throw e;
550         }
551     }
552 
553     /**
554      * {@inheritDoc}
555      */
556     @Override
557     public void fireSessionOpened() {
558         callNextSessionOpened(head, session);
559     }
560 
561     private void callNextSessionOpened(Entry entry, IoSession session) {
562         try {
563             IoFilter filter = entry.getFilter();
564             NextFilter nextFilter = entry.getNextFilter();
565             filter.sessionOpened(nextFilter, session);
566         } catch (Exception e) {
567             fireExceptionCaught(e);
568         } catch (Error e) {
569             fireExceptionCaught(e);
570             throw e;
571         }
572     }
573 
574     /**
575      * {@inheritDoc}
576      */
577     @Override
578     public void fireSessionClosed() {
579         // Update future.
580         try {
581             session.getCloseFuture().setClosed();
582         } catch (Exception e) {
583             fireExceptionCaught(e);
584         } catch (Error e) {
585             fireExceptionCaught(e);
586             throw e;
587         }
588 
589         // And start the chain.
590         callNextSessionClosed(head, session);
591     }
592 
593     private void callNextSessionClosed(Entry entry, IoSession session) {
594         try {
595             IoFilter filter = entry.getFilter();
596             NextFilter nextFilter = entry.getNextFilter();
597             filter.sessionClosed(nextFilter, session);
598         } catch (Exception | Error e) {
599             fireExceptionCaught(e);
600         }
601     }
602 
603     /**
604      * {@inheritDoc}
605      */
606     @Override
607     public void fireSessionIdle(IdleStatus status) {
608         session.increaseIdleCount(status, System.currentTimeMillis());
609         callNextSessionIdle(head, session, status);
610     }
611 
612     private void callNextSessionIdle(Entry entry, IoSession session, IdleStatus status) {
613         try {
614             IoFilter filter = entry.getFilter();
615             NextFilter nextFilter = entry.getNextFilter();
616             filter.sessionIdle(nextFilter, session, status);
617         } catch (Exception e) {
618             fireExceptionCaught(e);
619         } catch (Error e) {
620             fireExceptionCaught(e);
621             throw e;
622         }
623     }
624 
625     /**
626      * {@inheritDoc}
627      */
628     @Override
629     public void fireMessageReceived(Object message) {
630         if (message instanceof IoBuffer) {
631             session.increaseReadBytes(((IoBuffer) message).remaining(), System.currentTimeMillis());
632         }
633 
634         callNextMessageReceived(head, session, message);
635     }
636 
637     private void callNextMessageReceived(Entry entry, IoSession session, Object message) {
638         try {
639             IoFilter filter = entry.getFilter();
640             NextFilter nextFilter = entry.getNextFilter();
641             filter.messageReceived(nextFilter, session, message);
642         } catch (Exception e) {
643             fireExceptionCaught(e);
644         } catch (Error e) {
645             fireExceptionCaught(e);
646             throw e;
647         }
648     }
649 
650     /**
651      * {@inheritDoc}
652      */
653     @Override
654     public void fireMessageSent(WriteRequest request) {
655         try {
656             request.getFuture().setWritten();
657         } catch (Exception e) {
658             fireExceptionCaught(e);
659         } catch (Error e) {
660             fireExceptionCaught(e);
661             throw e;
662         }
663 
664         if (!request.isEncoded()) {
665             callNextMessageSent(head, session, request);
666         }
667     }
668 
669     private void callNextMessageSent(Entry entry, IoSession session, WriteRequest writeRequest) {
670         try {
671             IoFilter filter = entry.getFilter();
672             NextFilter nextFilter = entry.getNextFilter();
673             filter.messageSent(nextFilter, session, writeRequest);
674         } catch (Exception e) {
675             fireExceptionCaught(e);
676         } catch (Error e) {
677             fireExceptionCaught(e);
678             throw e;
679         }
680     }
681 
682     /**
683      * {@inheritDoc}
684      */
685     @Override
686     public void fireExceptionCaught(Throwable cause) {
687         callNextExceptionCaught(head, session, cause);
688     }
689 
690     private void callNextExceptionCaught(Entry entry, IoSession session, Throwable cause) {
691         // Notify the related future.
692         ConnectFuture future = (ConnectFuture) session.removeAttribute(SESSION_CREATED_FUTURE);
693         if (future == null) {
694             try {
695                 IoFilter filter = entry.getFilter();
696                 NextFilter nextFilter = entry.getNextFilter();
697                 filter.exceptionCaught(nextFilter, session, cause);
698             } catch (Throwable e) {
699                 LOGGER.warn("Unexpected exception from exceptionCaught handler.", e);
700             }
701         } else {
702             // Please note that this place is not the only place that
703             // calls ConnectFuture.setException().
704             if (!session.isClosing()) {
705                 // Call the closeNow method only if needed
706                 session.closeNow();
707             }
708             
709             future.setException(cause);
710         }
711     }
712 
713     /**
714      * {@inheritDoc}
715      */
716     @Override
717     public void fireInputClosed() {
718         Entry head = this.head;
719         callNextInputClosed(head, session);
720     }
721 
722     private void callNextInputClosed(Entry entry, IoSession session) {
723         try {
724             IoFilter filter = entry.getFilter();
725             NextFilter nextFilter = entry.getNextFilter();
726             filter.inputClosed(nextFilter, session);
727         } catch (Throwable e) {
728             fireExceptionCaught(e);
729         }
730     }
731 
732     /**
733      * {@inheritDoc}
734      */
735     @Override
736 public void fireFilterWrite(WriteRequest writeRequest) {
737         callPreviousFilterWrite(tail, session, writeRequest);
738     }
739 
740     private void callPreviousFilterWrite(Entry entry, IoSession session, WriteRequest writeRequest) {
741         try {
742             IoFilter filter = entry.getFilter();
743             NextFilter nextFilter = entry.getNextFilter();
744             filter.filterWrite(nextFilter, session, writeRequest);
745         } catch (Exception e) {
746             writeRequest.getFuture().setException(e);
747             fireExceptionCaught(e);
748         } catch (Error e) {
749             writeRequest.getFuture().setException(e);
750             fireExceptionCaught(e);
751             throw e;
752         }
753     }
754 
755     /**
756      * {@inheritDoc}
757      */
758     @Override
759     public void fireFilterClose() {
760         callPreviousFilterClose(tail, session);
761     }
762 
763     private void callPreviousFilterClose(Entry entry, IoSession session) {
764         try {
765             IoFilter filter = entry.getFilter();
766             NextFilter nextFilter = entry.getNextFilter();
767             filter.filterClose(nextFilter, session);
768         } catch (Exception e) {
769             fireExceptionCaught(e);
770         } catch (Error e) {
771             fireExceptionCaught(e);
772             throw e;
773         }
774     }
775 
776     /**
777      * {@inheritDoc}
778      */
779     @Override
780     public List<Entry> getAll() {
781         List<Entry> list = new ArrayList<>();
782         EntryImpl e = head.nextEntry;
783 
784         while (e != tail) {
785             list.add(e);
786             e = e.nextEntry;
787         }
788 
789         return list;
790     }
791 
792     /**
793      * {@inheritDoc}
794      */
795     @Override
796     public List<Entry> getAllReversed() {
797         List<Entry> list = new ArrayList<>();
798         EntryImpl e = tail.prevEntry;
799 
800         while (e != head) {
801             list.add(e);
802             e = e.prevEntry;
803         }
804 
805         return list;
806     }
807 
808     /**
809      * {@inheritDoc}
810      */
811     @Override
812     public boolean contains(String name) {
813         return getEntry(name) != null;
814     }
815 
816     /**
817      * {@inheritDoc}
818      */
819     @Override
820     public boolean contains(IoFilter filter) {
821         return getEntry(filter) != null;
822     }
823 
824     /**
825      * {@inheritDoc}
826      */
827     @Override
828     public boolean contains(Class<? extends IoFilter> filterType) {
829         return getEntry(filterType) != null;
830     }
831 
832     @Override
833     public String toString() {
834         StringBuilder buf = new StringBuilder();
835         buf.append("{ ");
836 
837         boolean empty = true;
838 
839         EntryImpl e = head.nextEntry;
840 
841         while (e != tail) {
842             if (!empty) {
843                 buf.append(", ");
844             } else {
845                 empty = false;
846             }
847 
848             buf.append('(');
849             buf.append(e.getName());
850             buf.append(':');
851             buf.append(e.getFilter());
852             buf.append(')');
853 
854             e = e.nextEntry;
855         }
856 
857         if (empty) {
858             buf.append("empty");
859         }
860 
861         buf.append(" }");
862 
863         return buf.toString();
864     }
865 
866     private class HeadFilter extends IoFilterAdapter {
867         @SuppressWarnings("unchecked")
868         @Override
869       public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
870             AbstractIoSession s = (AbstractIoSession) session;
871 
872             // Maintain counters.
873             if (writeRequest.getMessage() instanceof IoBuffer) {
874                 IoBuffer buffer = (IoBuffer) writeRequest.getMessage();
875                 // I/O processor implementation will call buffer.reset()
876                 // it after the write operation is finished, because
877                 // the buffer will be specified with messageSent event.
878                 buffer.mark();
879                 int remaining = buffer.remaining();
880 
881                 if (remaining > 0) {
882                     s.increaseScheduledWriteBytes(remaining);
883                 }
884             }
885 
886             if (!writeRequest.isEncoded()) {
887                 s.increaseScheduledWriteMessages();
888             }
889 
890             WriteRequestQueue writeRequestQueue = s.getWriteRequestQueue();
891 
892             if (!s.isWriteSuspended()) {
893                 if (writeRequestQueue.isEmpty(session)) {
894                     // We can write directly the message
895                     s.getProcessor().write(s, writeRequest);
896                 } else {
897                     s.getWriteRequestQueue().offer(s, writeRequest);
898                     s.getProcessor().flush(s);
899                 }
900             } else {
901                 s.getWriteRequestQueue().offer(s, writeRequest);
902             }
903         }
904 
905 
906         @SuppressWarnings("unchecked")
907         @Override
908         public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
909             ((AbstractIoSession) session).getProcessor().remove(session);
910         }
911     }
912 
913     private static class TailFilter extends IoFilterAdapter {
914         @Override
915         public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
916             try {
917                 session.getHandler().sessionCreated(session);
918             } finally {
919                 // Notify the related future.
920                 ConnectFuture future = (ConnectFuture) session.removeAttribute(SESSION_CREATED_FUTURE);
921 
922                 if (future != null) {
923                     future.setSession(session);
924                 }
925             }
926         }
927 
928         @Override
929         public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
930             session.getHandler().sessionOpened(session);
931         }
932 
933         @Override
934         public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
935             AbstractIoSession s = (AbstractIoSession) session;
936 
937             try {
938                 s.getHandler().sessionClosed(session);
939             } finally {
940                 try {
941                     s.getWriteRequestQueue().dispose(session);
942                 } finally {
943                     try {
944                         s.getAttributeMap().dispose(session);
945                     } finally {
946                         try {
947                             // Remove all filters.
948                             session.getFilterChain().clear();
949                         } finally {
950                             if (s.getConfig().isUseReadOperation()) {
951                                 s.offerClosedReadFuture();
952                             }
953                         }
954                     }
955                 }
956             }
957         }
958 
959         @Override
960         public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {
961             session.getHandler().sessionIdle(session, status);
962         }
963 
964         @Override
965         public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
966             AbstractIoSession s = (AbstractIoSession) session;
967 
968             try {
969                 s.getHandler().exceptionCaught(s, cause);
970             } finally {
971                 if (s.getConfig().isUseReadOperation()) {
972                     s.offerFailedReadFuture(cause);
973                 }
974             }
975         }
976 
977         @Override
978         public void inputClosed(NextFilter nextFilter, IoSession session) throws Exception {
979             session.getHandler().inputClosed(session);
980         }
981 
982         @Override
983         public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
984             AbstractIoSession s = (AbstractIoSession) session;
985 
986             if (!(message instanceof IoBuffer) || !((IoBuffer) message).hasRemaining()) {
987                 s.increaseReadMessages(System.currentTimeMillis());
988             }
989 
990             // Update the statistics
991             if (session.getService() instanceof AbstractIoService) {
992                 ((AbstractIoService) session.getService()).getStatistics().updateThroughput(System.currentTimeMillis());
993             }
994 
995             // Propagate the message
996             try {
997                 session.getHandler().messageReceived(s, message);
998             } finally {
999                 if (s.getConfig().isUseReadOperation()) {
1000                     s.offerReadFuture(message);
1001                 }
1002             }
1003         }
1004 
1005         @Override
1006         public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
1007             ((AbstractIoSession) session).increaseWrittenMessages(writeRequest, System.currentTimeMillis());
1008 
1009             // Update the statistics
1010             if (session.getService() instanceof AbstractIoService) {
1011                 ((AbstractIoService) session.getService()).getStatistics().updateThroughput(System.currentTimeMillis());
1012             }
1013 
1014             // Propagate the message
1015             session.getHandler().messageSent(session, writeRequest.getMessage());
1016         }
1017 
1018         @Override
1019         public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
1020             nextFilter.filterWrite(session, writeRequest);
1021         }
1022 
1023         @Override
1024         public void filterClose(NextFilter nextFilter, IoSession session) throws Exception {
1025             nextFilter.filterClose(session);
1026         }
1027     }
1028 
1029     private final class EntryImpl implements Entry {
1030         private EntryImpl prevEntry;
1031 
1032         private EntryImpl nextEntry;
1033 
1034         private final String name;
1035 
1036         private IoFilter filter;
1037 
1038         private final NextFilter nextFilter;
1039 
1040         private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry, String name, IoFilter filter) {
1041             if (filter == null) {
1042                 throw new IllegalArgumentException("filter");
1043             }
1044 
1045             if (name == null) {
1046                 throw new IllegalArgumentException("name");
1047             }
1048 
1049             this.prevEntry = prevEntry;
1050             this.nextEntry = nextEntry;
1051             this.name = name;
1052             this.filter = filter;
1053             this.nextFilter = new NextFilter() {
1054                 /**
1055                  * {@inheritDoc}
1056                  */
1057                 @Override
1058                 public void sessionCreated(IoSession session) {
1059                     Entry nextEntry = EntryImpl.this.nextEntry;
1060                     callNextSessionCreated(nextEntry, session);
1061                 }
1062 
1063                 /**
1064                  * {@inheritDoc}
1065                  */
1066                 @Override
1067                 public void sessionOpened(IoSession session) {
1068                     Entry nextEntry = EntryImpl.this.nextEntry;
1069                     callNextSessionOpened(nextEntry, session);
1070                 }
1071 
1072                 /**
1073                  * {@inheritDoc}
1074                  */
1075                 @Override
1076                 public void sessionClosed(IoSession session) {
1077                     Entry nextEntry = EntryImpl.this.nextEntry;
1078                     callNextSessionClosed(nextEntry, session);
1079                 }
1080 
1081                 /**
1082                  * {@inheritDoc}
1083                  */
1084                 @Override
1085                 public void sessionIdle(IoSession session, IdleStatus status) {
1086                     Entry nextEntry = EntryImpl.this.nextEntry;
1087                     callNextSessionIdle(nextEntry, session, status);
1088                 }
1089 
1090                 /**
1091                  * {@inheritDoc}
1092                  */
1093                 @Override
1094                 public void exceptionCaught(IoSession session, Throwable cause) {
1095                     Entry nextEntry = EntryImpl.this.nextEntry;
1096                     callNextExceptionCaught(nextEntry, session, cause);
1097                 }
1098 
1099                 /**
1100                  * {@inheritDoc}
1101                  */
1102                 @Override
1103                 public void inputClosed(IoSession session) {
1104                     Entry nextEntry = EntryImpl.this.nextEntry;
1105                     callNextInputClosed(nextEntry, session);
1106                 }
1107 
1108                 /**
1109                  * {@inheritDoc}
1110                  */
1111                 @Override
1112                 public void messageReceived(IoSession session, Object message) {
1113                     Entry nextEntry = EntryImpl.this.nextEntry;
1114                     callNextMessageReceived(nextEntry, session, message);
1115                 }
1116 
1117                 /**
1118                  * {@inheritDoc}
1119                  */
1120                 @Override
1121                 public void messageSent(IoSession session, WriteRequest writeRequest) {
1122                     Entry nextEntry = EntryImpl.this.nextEntry;
1123                     callNextMessageSent(nextEntry, session, writeRequest);
1124                 }
1125 
1126                 /**
1127                  * {@inheritDoc}
1128                  */
1129                 @Override
1130                 public void filterWrite(IoSession session, WriteRequest writeRequest) {
1131                     Entry nextEntry = EntryImpl.this.prevEntry;
1132                     callPreviousFilterWrite(nextEntry, session, writeRequest);
1133                 }
1134 
1135                 /**
1136                  * {@inheritDoc}
1137                  */
1138                 @Override
1139                 public void filterClose(IoSession session) {
1140                     Entry nextEntry = EntryImpl.this.prevEntry;
1141                     callPreviousFilterClose(nextEntry, session);
1142                 }
1143 
1144                 /**
1145                  * {@inheritDoc}
1146                  */
1147                 @Override
1148                 public String toString() {
1149                     return EntryImpl.this.nextEntry.name;
1150                 }
1151             };
1152         }
1153 
1154         /**
1155          * {@inheritDoc}
1156          */
1157         @Override
1158         public String getName() {
1159             return name;
1160         }
1161 
1162         /**
1163          * {@inheritDoc}
1164          */
1165         @Override
1166         public IoFilter getFilter() {
1167             return filter;
1168         }
1169 
1170         private void setFilter(IoFilter filter) {
1171             if (filter == null) {
1172                 throw new IllegalArgumentException("filter");
1173             }
1174 
1175             this.filter = filter;
1176         }
1177 
1178         /**
1179          * {@inheritDoc}
1180          */
1181         @Override
1182         public NextFilter getNextFilter() {
1183             return nextFilter;
1184         }
1185 
1186         @Override
1187         public String toString() {
1188             StringBuilder sb = new StringBuilder();
1189 
1190             // Add the current filter
1191             sb.append("('").append(getName()).append('\'');
1192 
1193             // Add the previous filter
1194             sb.append(", prev: '");
1195 
1196             if (prevEntry != null) {
1197                 sb.append(prevEntry.name);
1198                 sb.append(':');
1199                 sb.append(prevEntry.getFilter().getClass().getSimpleName());
1200             } else {
1201                 sb.append("null");
1202             }
1203 
1204             // Add the next filter
1205             sb.append("', next: '");
1206 
1207             if (nextEntry != null) {
1208                 sb.append(nextEntry.name);
1209                 sb.append(':');
1210                 sb.append(nextEntry.getFilter().getClass().getSimpleName());
1211             } else {
1212                 sb.append("null");
1213             }
1214 
1215             sb.append("')");
1216 
1217             return sb.toString();
1218         }
1219 
1220         /**
1221          * {@inheritDoc}
1222          */
1223         @Override
1224         public void addAfter(String name, IoFilter filter) {
1225             DefaultIoFilterChain.this.addAfter(getName(), name, filter);
1226         }
1227 
1228         /**
1229          * {@inheritDoc}
1230          */
1231         @Override
1232         public void addBefore(String name, IoFilter filter) {
1233             DefaultIoFilterChain.this.addBefore(getName(), name, filter);
1234         }
1235 
1236         /**
1237          * {@inheritDoc}
1238          */
1239         @Override
1240         public void remove() {
1241             DefaultIoFilterChain.this.remove(getName());
1242         }
1243 
1244         /**
1245          * {@inheritDoc}
1246          */
1247         @Override
1248         public void replace(IoFilter newFilter) {
1249             DefaultIoFilterChain.this.replace(getName(), newFilter);
1250         }
1251     }
1252 }