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