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