001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.core.filterchain;
021
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Map;
025import java.util.concurrent.ConcurrentHashMap;
026
027import org.apache.mina.core.buffer.IoBuffer;
028import org.apache.mina.core.filterchain.IoFilter.NextFilter;
029import org.apache.mina.core.future.ConnectFuture;
030import org.apache.mina.core.future.IoFuture;
031import org.apache.mina.core.service.AbstractIoService;
032import org.apache.mina.core.session.AbstractIoSession;
033import org.apache.mina.core.session.AttributeKey;
034import org.apache.mina.core.session.IdleStatus;
035import org.apache.mina.core.session.IoSession;
036import org.apache.mina.core.write.WriteRequest;
037import org.apache.mina.core.write.WriteRequestQueue;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041/**
042 * A default implementation of {@link IoFilterChain} that provides
043 * all operations for developers who want to implement their own
044 * transport layer once used with {@link AbstractIoSession}.
045 *
046 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
047 */
048public class DefaultIoFilterChain implements IoFilterChain {
049    /**
050     * A session attribute that stores an {@link IoFuture} related with
051     * the {@link IoSession}.  {@link DefaultIoFilterChain} clears this
052     * attribute and notifies the future when {@link #fireSessionCreated()}
053     * or {@link #fireExceptionCaught(Throwable)} is invoked.
054     */
055    public static final AttributeKey SESSION_CREATED_FUTURE = new AttributeKey(DefaultIoFilterChain.class,
056            "connectFuture");
057
058    /** The associated session */
059    private final AbstractIoSession session;
060
061    /** The mapping between the filters and their associated name */
062    private final Map<String, Entry> name2entry = new ConcurrentHashMap<String, Entry>();
063
064    /** The chain head */
065    private final EntryImpl head;
066
067    /** The chain tail */
068    private final EntryImpl tail;
069
070    /** The logger for this class */
071    private final static Logger LOGGER = LoggerFactory.getLogger(DefaultIoFilterChain.class);
072
073    /**
074     * Create a new default chain, associated with a session. It will only contain a
075     * HeadFilter and a TailFilter.
076     *
077     * @param session The session associated with the created filter chain
078     */
079    public DefaultIoFilterChain(AbstractIoSession session) {
080        if (session == null) {
081            throw new IllegalArgumentException("session");
082        }
083
084        this.session = session;
085        head = new EntryImpl(null, null, "head", new HeadFilter());
086        tail = new EntryImpl(head, null, "tail", new TailFilter());
087        head.nextEntry = tail;
088    }
089
090    public IoSession getSession() {
091        return session;
092    }
093
094    public Entry getEntry(String name) {
095        Entry e = name2entry.get(name);
096
097        if (e == null) {
098            return null;
099        }
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}