View Javadoc

1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.channel;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.LinkedHashMap;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.NoSuchElementException;
24  import java.util.concurrent.RejectedExecutionException;
25  
26  import org.jboss.netty.logging.InternalLogger;
27  import org.jboss.netty.logging.InternalLoggerFactory;
28  
29  /**
30   * The default {@link ChannelPipeline} implementation.  It is recommended
31   * to use {@link Channels#pipeline()} to create a new {@link ChannelPipeline}
32   * instance rather than calling the constructor directly.
33   */
34  public class DefaultChannelPipeline implements ChannelPipeline {
35  
36      static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class);
37      static final ChannelSink discardingSink = new DiscardingChannelSink();
38  
39      private volatile Channel channel;
40      private volatile ChannelSink sink;
41      private volatile DefaultChannelHandlerContext head;
42      private volatile DefaultChannelHandlerContext tail;
43      private final Map<String, DefaultChannelHandlerContext> name2ctx =
44          new HashMap<String, DefaultChannelHandlerContext>(4);
45  
46      /**
47       * Creates a new empty pipeline.
48       */
49      public DefaultChannelPipeline() {
50          super();
51      }
52  
53      public Channel getChannel() {
54          return channel;
55      }
56  
57      public ChannelSink getSink() {
58          ChannelSink sink = this.sink;
59          if (sink == null) {
60              return discardingSink;
61          }
62          return sink;
63      }
64  
65      public void attach(Channel channel, ChannelSink sink) {
66          if (channel == null) {
67              throw new NullPointerException("channel");
68          }
69          if (sink == null) {
70              throw new NullPointerException("sink");
71          }
72          if (this.channel != null || this.sink != null) {
73              throw new IllegalStateException("attached already");
74          }
75          this.channel = channel;
76          this.sink = sink;
77      }
78  
79      public boolean isAttached() {
80          return sink != null;
81      }
82  
83      public synchronized void addFirst(String name, ChannelHandler handler) {
84          if (name2ctx.isEmpty()) {
85              init(name, handler);
86          } else {
87              checkDuplicateName(name);
88              DefaultChannelHandlerContext oldHead = head;
89              DefaultChannelHandlerContext newHead = new DefaultChannelHandlerContext(null, oldHead, name, handler);
90  
91              callBeforeAdd(newHead);
92  
93              oldHead.prev = newHead;
94              head = newHead;
95              name2ctx.put(name, newHead);
96  
97              callAfterAdd(newHead);
98          }
99      }
100 
101     public synchronized void addLast(String name, ChannelHandler handler) {
102         if (name2ctx.isEmpty()) {
103             init(name, handler);
104         } else {
105             checkDuplicateName(name);
106             DefaultChannelHandlerContext oldTail = tail;
107             DefaultChannelHandlerContext newTail = new DefaultChannelHandlerContext(oldTail, null, name, handler);
108 
109             callBeforeAdd(newTail);
110 
111             oldTail.next = newTail;
112             tail = newTail;
113             name2ctx.put(name, newTail);
114 
115             callAfterAdd(newTail);
116         }
117     }
118 
119     public synchronized void addBefore(String baseName, String name, ChannelHandler handler) {
120         DefaultChannelHandlerContext ctx = getContextOrDie(baseName);
121         if (ctx == head) {
122             addFirst(name, handler);
123         } else {
124             checkDuplicateName(name);
125             DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(ctx.prev, ctx, name, handler);
126 
127             callBeforeAdd(newCtx);
128 
129             ctx.prev.next = newCtx;
130             ctx.prev = newCtx;
131             name2ctx.put(name, newCtx);
132 
133             callAfterAdd(newCtx);
134         }
135     }
136 
137     public synchronized void addAfter(String baseName, String name, ChannelHandler handler) {
138         DefaultChannelHandlerContext ctx = getContextOrDie(baseName);
139         if (ctx == tail) {
140             addLast(name, handler);
141         } else {
142             checkDuplicateName(name);
143             DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(ctx, ctx.next, name, handler);
144 
145             callBeforeAdd(newCtx);
146 
147             ctx.next.prev = newCtx;
148             ctx.next = newCtx;
149             name2ctx.put(name, newCtx);
150 
151             callAfterAdd(newCtx);
152         }
153     }
154 
155     public synchronized void remove(ChannelHandler handler) {
156         remove(getContextOrDie(handler));
157     }
158 
159     public synchronized ChannelHandler remove(String name) {
160         return remove(getContextOrDie(name)).getHandler();
161     }
162 
163     @SuppressWarnings("unchecked")
164     public synchronized <T extends ChannelHandler> T remove(Class<T> handlerType) {
165         return (T) remove(getContextOrDie(handlerType)).getHandler();
166     }
167 
168     private DefaultChannelHandlerContext remove(DefaultChannelHandlerContext ctx) {
169         if (head == tail) {
170             head = tail = null;
171             name2ctx.clear();
172         } else if (ctx == head) {
173             removeFirst();
174         } else if (ctx == tail) {
175             removeLast();
176         } else {
177             callBeforeRemove(ctx);
178 
179             DefaultChannelHandlerContext prev = ctx.prev;
180             DefaultChannelHandlerContext next = ctx.next;
181             prev.next = next;
182             next.prev = prev;
183             name2ctx.remove(ctx.getName());
184 
185             callAfterRemove(ctx);
186         }
187         return ctx;
188     }
189 
190     public synchronized ChannelHandler removeFirst() {
191         if (name2ctx.isEmpty()) {
192             throw new NoSuchElementException();
193         }
194 
195         DefaultChannelHandlerContext oldHead = head;
196         if (oldHead == null) {
197             throw new NoSuchElementException();
198         }
199 
200         callBeforeRemove(oldHead);
201 
202         if (oldHead.next == null) {
203             head = tail = null;
204             name2ctx.clear();
205         } else {
206             oldHead.next.prev = null;
207             head = oldHead.next;
208             name2ctx.remove(oldHead.getName());
209         }
210 
211         callAfterRemove(oldHead);
212 
213         return oldHead.getHandler();
214     }
215 
216     public synchronized ChannelHandler removeLast() {
217         if (name2ctx.isEmpty()) {
218             throw new NoSuchElementException();
219         }
220 
221         DefaultChannelHandlerContext oldTail = tail;
222         if (oldTail == null) {
223             throw new NoSuchElementException();
224         }
225 
226         callBeforeRemove(oldTail);
227 
228         if (oldTail.prev == null) {
229             head = tail = null;
230             name2ctx.clear();
231         } else {
232             oldTail.prev.next = null;
233             tail = oldTail.prev;
234             name2ctx.remove(oldTail.getName());
235         }
236 
237         callBeforeRemove(oldTail);
238 
239         return oldTail.getHandler();
240     }
241 
242     public synchronized void replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) {
243         replace(getContextOrDie(oldHandler), newName, newHandler);
244     }
245 
246     public synchronized ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler) {
247         return replace(getContextOrDie(oldName), newName, newHandler);
248     }
249 
250     @SuppressWarnings("unchecked")
251     public synchronized <T extends ChannelHandler> T replace(
252             Class<T> oldHandlerType, String newName, ChannelHandler newHandler) {
253         return (T) replace(getContextOrDie(oldHandlerType), newName, newHandler);
254     }
255 
256     private ChannelHandler replace(DefaultChannelHandlerContext ctx, String newName, ChannelHandler newHandler) {
257         if (ctx == head) {
258             removeFirst();
259             addFirst(newName, newHandler);
260         } else if (ctx == tail) {
261             removeLast();
262             addLast(newName, newHandler);
263         } else {
264             boolean sameName = ctx.getName().equals(newName);
265             if (!sameName) {
266                 checkDuplicateName(newName);
267             }
268 
269             DefaultChannelHandlerContext prev = ctx.prev;
270             DefaultChannelHandlerContext next = ctx.next;
271             DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(prev, next, newName, newHandler);
272 
273             callBeforeRemove(ctx);
274             callBeforeAdd(newCtx);
275 
276             prev.next = newCtx;
277             next.prev = newCtx;
278 
279             if (!sameName) {
280                 name2ctx.remove(ctx.getName());
281             }
282             name2ctx.put(newName, newCtx);
283 
284             ChannelHandlerLifeCycleException removeException = null;
285             ChannelHandlerLifeCycleException addException = null;
286             boolean removed = false;
287             try {
288                 callAfterRemove(ctx);
289                 removed = true;
290             } catch (ChannelHandlerLifeCycleException e) {
291                 removeException = e;
292             }
293 
294             boolean added = false;
295             try {
296                 callAfterAdd(newCtx);
297                 added = true;
298             } catch (ChannelHandlerLifeCycleException e) {
299                 addException = e;
300             }
301 
302             if (!removed && !added) {
303                 logger.warn(removeException.getMessage(), removeException);
304                 logger.warn(addException.getMessage(), addException);
305                 throw new ChannelHandlerLifeCycleException(
306                         "Both " + ctx.getHandler().getClass().getName() +
307                         ".afterRemove() and " + newCtx.getHandler().getClass().getName() +
308                         ".afterAdd() failed; see logs.");
309             } else if (!removed) {
310                 throw removeException;
311             } else if (!added) {
312                 throw addException;
313             }
314         }
315 
316         return ctx.getHandler();
317     }
318 
319     private static void callBeforeAdd(ChannelHandlerContext ctx) {
320         if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) {
321             return;
322         }
323 
324         LifeCycleAwareChannelHandler h =
325             (LifeCycleAwareChannelHandler) ctx.getHandler();
326 
327         try {
328             h.beforeAdd(ctx);
329         } catch (Throwable t) {
330             throw new ChannelHandlerLifeCycleException(
331                     h.getClass().getName() +
332                     ".beforeAdd() has thrown an exception; not adding.", t);
333         }
334     }
335 
336     private void callAfterAdd(ChannelHandlerContext ctx) {
337         if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) {
338             return;
339         }
340 
341         LifeCycleAwareChannelHandler h =
342             (LifeCycleAwareChannelHandler) ctx.getHandler();
343 
344         try {
345             h.afterAdd(ctx);
346         } catch (Throwable t) {
347             boolean removed = false;
348             try {
349                 remove((DefaultChannelHandlerContext) ctx);
350                 removed = true;
351             } catch (Throwable t2) {
352                 if (logger.isWarnEnabled()) {
353                     logger.warn("Failed to remove a handler: " + ctx.getName(), t2);
354                 }
355             }
356 
357             if (removed) {
358                 throw new ChannelHandlerLifeCycleException(
359                         h.getClass().getName() +
360                         ".afterAdd() has thrown an exception; removed.", t);
361             } else {
362                 throw new ChannelHandlerLifeCycleException(
363                         h.getClass().getName() +
364                         ".afterAdd() has thrown an exception; also failed to remove.", t);
365             }
366         }
367     }
368 
369     private static void callBeforeRemove(ChannelHandlerContext ctx) {
370         if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) {
371             return;
372         }
373 
374         LifeCycleAwareChannelHandler h =
375             (LifeCycleAwareChannelHandler) ctx.getHandler();
376 
377         try {
378             h.beforeRemove(ctx);
379         } catch (Throwable t) {
380             throw new ChannelHandlerLifeCycleException(
381                     h.getClass().getName() +
382                     ".beforeRemove() has thrown an exception; not removing.", t);
383         }
384     }
385 
386     private static void callAfterRemove(ChannelHandlerContext ctx) {
387         if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) {
388             return;
389         }
390 
391         LifeCycleAwareChannelHandler h =
392             (LifeCycleAwareChannelHandler) ctx.getHandler();
393 
394         try {
395             h.afterRemove(ctx);
396         } catch (Throwable t) {
397             throw new ChannelHandlerLifeCycleException(
398                     h.getClass().getName() +
399                     ".afterRemove() has thrown an exception.", t);
400         }
401     }
402 
403     public synchronized ChannelHandler getFirst() {
404         DefaultChannelHandlerContext head = this.head;
405         if (head == null) {
406             return null;
407         }
408         return head.getHandler();
409     }
410 
411     public synchronized ChannelHandler getLast() {
412         DefaultChannelHandlerContext tail = this.tail;
413         if (tail == null) {
414             return null;
415         }
416         return tail.getHandler();
417     }
418 
419     public synchronized ChannelHandler get(String name) {
420         DefaultChannelHandlerContext ctx = name2ctx.get(name);
421         if (ctx == null) {
422             return null;
423         } else {
424             return ctx.getHandler();
425         }
426     }
427 
428     public synchronized <T extends ChannelHandler> T get(Class<T> handlerType) {
429         ChannelHandlerContext ctx = getContext(handlerType);
430         if (ctx == null) {
431             return null;
432         } else {
433             return (T) ctx.getHandler();
434         }
435     }
436 
437     public synchronized ChannelHandlerContext getContext(String name) {
438         if (name == null) {
439             throw new NullPointerException("name");
440         }
441         return name2ctx.get(name);
442     }
443 
444     public synchronized ChannelHandlerContext getContext(ChannelHandler handler) {
445         if (handler == null) {
446             throw new NullPointerException("handler");
447         }
448         if (name2ctx.isEmpty()) {
449             return null;
450         }
451         DefaultChannelHandlerContext ctx = head;
452         for (;;) {
453             if (ctx.getHandler() == handler) {
454                 return ctx;
455             }
456 
457             ctx = ctx.next;
458             if (ctx == null) {
459                 break;
460             }
461         }
462         return null;
463     }
464 
465     public synchronized ChannelHandlerContext getContext(
466             Class<? extends ChannelHandler> handlerType) {
467         if (handlerType == null) {
468             throw new NullPointerException("handlerType");
469         }
470 
471         if (name2ctx.isEmpty()) {
472             return null;
473         }
474         DefaultChannelHandlerContext ctx = head;
475         for (;;) {
476             if (handlerType.isAssignableFrom(ctx.getHandler().getClass())) {
477                 return ctx;
478             }
479 
480             ctx = ctx.next;
481             if (ctx == null) {
482                 break;
483             }
484         }
485         return null;
486     }
487 
488     public List<String> getNames() {
489         List<String> list = new ArrayList<String>();
490         if (name2ctx.isEmpty()) {
491             return list;
492         }
493 
494         DefaultChannelHandlerContext ctx = head;
495         for (;;) {
496             list.add(ctx.getName());
497             ctx = ctx.next;
498             if (ctx == null) {
499                 break;
500             }
501         }
502         return list;
503     }
504 
505     public Map<String, ChannelHandler> toMap() {
506         Map<String, ChannelHandler> map = new LinkedHashMap<String, ChannelHandler>();
507         if (name2ctx.isEmpty()) {
508             return map;
509         }
510 
511         DefaultChannelHandlerContext ctx = head;
512         for (;;) {
513             map.put(ctx.getName(), ctx.getHandler());
514             ctx = ctx.next;
515             if (ctx == null) {
516                 break;
517             }
518         }
519         return map;
520     }
521 
522     /**
523      * Returns the {@link String} representation of this pipeline.
524      */
525     @Override
526     public String toString() {
527         StringBuilder buf = new StringBuilder();
528         buf.append(getClass().getSimpleName());
529         buf.append('{');
530         DefaultChannelHandlerContext ctx = head;
531         if (ctx != null) {
532             for (;;) {
533                 buf.append('(');
534                 buf.append(ctx.getName());
535                 buf.append(" = ");
536                 buf.append(ctx.getHandler().getClass().getName());
537                 buf.append(')');
538                 ctx = ctx.next;
539                 if (ctx == null) {
540                     break;
541                 }
542                 buf.append(", ");
543             }
544         }
545         buf.append('}');
546         return buf.toString();
547     }
548 
549     public void sendUpstream(ChannelEvent e) {
550         DefaultChannelHandlerContext head = getActualUpstreamContext(this.head);
551         if (head == null) {
552             if (logger.isWarnEnabled()) {
553                 logger.warn(
554                         "The pipeline contains no upstream handlers; discarding: " + e);
555             }
556 
557             return;
558         }
559 
560         sendUpstream(head, e);
561     }
562 
563     void sendUpstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
564         try {
565             ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e);
566         } catch (Throwable t) {
567             notifyHandlerException(e, t);
568         }
569     }
570 
571     public void sendDownstream(ChannelEvent e) {
572         DefaultChannelHandlerContext tail = getActualDownstreamContext(this.tail);
573         if (tail == null) {
574             try {
575                 getSink().eventSunk(this, e);
576                 return;
577             } catch (Throwable t) {
578                 notifyHandlerException(e, t);
579                 return;
580             }
581         }
582 
583         sendDownstream(tail, e);
584     }
585 
586     void sendDownstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
587         if (e instanceof UpstreamMessageEvent) {
588             throw new IllegalArgumentException("cannot send an upstream event to downstream");
589         }
590 
591         try {
592             ((ChannelDownstreamHandler) ctx.getHandler()).handleDownstream(ctx, e);
593         } catch (Throwable t) {
594             // Unlike an upstream event, a downstream event usually has an
595             // incomplete future which is supposed to be updated by ChannelSink.
596             // However, if an exception is raised before the event reaches at
597             // ChannelSink, the future is not going to be updated, so we update
598             // here.
599             e.getFuture().setFailure(t);
600             notifyHandlerException(e, t);
601         }
602     }
603 
604     DefaultChannelHandlerContext getActualUpstreamContext(DefaultChannelHandlerContext ctx) {
605         if (ctx == null) {
606             return null;
607         }
608 
609         DefaultChannelHandlerContext realCtx = ctx;
610         while (!realCtx.canHandleUpstream()) {
611             realCtx = realCtx.next;
612             if (realCtx == null) {
613                 return null;
614             }
615         }
616 
617         return realCtx;
618     }
619 
620     DefaultChannelHandlerContext getActualDownstreamContext(DefaultChannelHandlerContext ctx) {
621         if (ctx == null) {
622             return null;
623         }
624 
625         DefaultChannelHandlerContext realCtx = ctx;
626         while (!realCtx.canHandleDownstream()) {
627             realCtx = realCtx.prev;
628             if (realCtx == null) {
629                 return null;
630             }
631         }
632 
633         return realCtx;
634     }
635 
636     public ChannelFuture execute(Runnable task) {
637         return getSink().execute(this, task);
638     }
639 
640     protected void notifyHandlerException(ChannelEvent e, Throwable t) {
641         if (e instanceof ExceptionEvent) {
642             if (logger.isWarnEnabled()) {
643                 logger.warn(
644                         "An exception was thrown by a user handler " +
645                         "while handling an exception event (" + e + ")", t);
646             }
647 
648             return;
649         }
650 
651         ChannelPipelineException pe;
652         if (t instanceof ChannelPipelineException) {
653             pe = (ChannelPipelineException) t;
654         } else {
655             pe = new ChannelPipelineException(t);
656         }
657 
658         try {
659             sink.exceptionCaught(this, e, pe);
660         } catch (Exception e1) {
661             if (logger.isWarnEnabled()) {
662                 logger.warn("An exception was thrown by an exception handler.", e1);
663             }
664         }
665     }
666 
667     private void init(String name, ChannelHandler handler) {
668         DefaultChannelHandlerContext ctx = new DefaultChannelHandlerContext(null, null, name, handler);
669         callBeforeAdd(ctx);
670         head = tail = ctx;
671         name2ctx.clear();
672         name2ctx.put(name, ctx);
673         callAfterAdd(ctx);
674     }
675 
676     private void checkDuplicateName(String name) {
677         if (name2ctx.containsKey(name)) {
678             throw new IllegalArgumentException("Duplicate handler name: " + name);
679         }
680     }
681 
682     private DefaultChannelHandlerContext getContextOrDie(String name) {
683         DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) getContext(name);
684         if (ctx == null) {
685             throw new NoSuchElementException(name);
686         } else {
687             return ctx;
688         }
689     }
690 
691     private DefaultChannelHandlerContext getContextOrDie(ChannelHandler handler) {
692         DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) getContext(handler);
693         if (ctx == null) {
694             throw new NoSuchElementException(handler.getClass().getName());
695         } else {
696             return ctx;
697         }
698     }
699 
700     private DefaultChannelHandlerContext getContextOrDie(Class<? extends ChannelHandler> handlerType) {
701         DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) getContext(handlerType);
702         if (ctx == null) {
703             throw new NoSuchElementException(handlerType.getName());
704         } else {
705             return ctx;
706         }
707     }
708 
709     private final class DefaultChannelHandlerContext implements ChannelHandlerContext {
710         volatile DefaultChannelHandlerContext next;
711         volatile DefaultChannelHandlerContext prev;
712         private final String name;
713         private final ChannelHandler handler;
714         private final boolean canHandleUpstream;
715         private final boolean canHandleDownstream;
716         private volatile Object attachment;
717 
718         DefaultChannelHandlerContext(
719                 DefaultChannelHandlerContext prev, DefaultChannelHandlerContext next,
720                 String name, ChannelHandler handler) {
721 
722             if (name == null) {
723                 throw new NullPointerException("name");
724             }
725             if (handler == null) {
726                 throw new NullPointerException("handler");
727             }
728             canHandleUpstream = handler instanceof ChannelUpstreamHandler;
729             canHandleDownstream = handler instanceof ChannelDownstreamHandler;
730 
731 
732             if (!canHandleUpstream && !canHandleDownstream) {
733                 throw new IllegalArgumentException(
734                         "handler must be either " +
735                         ChannelUpstreamHandler.class.getName() + " or " +
736                         ChannelDownstreamHandler.class.getName() + '.');
737             }
738 
739             this.prev = prev;
740             this.next = next;
741             this.name = name;
742             this.handler = handler;
743         }
744 
745         public Channel getChannel() {
746             return getPipeline().getChannel();
747         }
748 
749         public ChannelPipeline getPipeline() {
750             return DefaultChannelPipeline.this;
751         }
752 
753         public boolean canHandleDownstream() {
754             return canHandleDownstream;
755         }
756 
757         public boolean canHandleUpstream() {
758             return canHandleUpstream;
759         }
760 
761         public ChannelHandler getHandler() {
762             return handler;
763         }
764 
765         public String getName() {
766             return name;
767         }
768 
769         public Object getAttachment() {
770             return attachment;
771         }
772 
773         public void setAttachment(Object attachment) {
774             this.attachment = attachment;
775         }
776 
777         public void sendDownstream(ChannelEvent e) {
778             DefaultChannelHandlerContext prev = getActualDownstreamContext(this.prev);
779             if (prev == null) {
780                 try {
781                     getSink().eventSunk(DefaultChannelPipeline.this, e);
782                 } catch (Throwable t) {
783                     notifyHandlerException(e, t);
784                 }
785             } else {
786                 DefaultChannelPipeline.this.sendDownstream(prev, e);
787             }
788         }
789 
790         public void sendUpstream(ChannelEvent e) {
791             DefaultChannelHandlerContext next = getActualUpstreamContext(this.next);
792             if (next != null) {
793                 DefaultChannelPipeline.this.sendUpstream(next, e);
794             }
795         }
796     }
797 
798     private static final class DiscardingChannelSink implements ChannelSink {
799         DiscardingChannelSink() {
800             super();
801         }
802 
803         public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) {
804             if (logger.isWarnEnabled()) {
805                 logger.warn("Not attached yet; discarding: " + e);
806             }
807 
808         }
809 
810         public void exceptionCaught(ChannelPipeline pipeline,
811                 ChannelEvent e, ChannelPipelineException cause) throws Exception {
812             throw cause;
813         }
814 
815 
816         public ChannelFuture execute(ChannelPipeline pipeline, Runnable task) {
817             if (logger.isWarnEnabled()) {
818                 logger.warn("Not attached yet; rejecting: " + task);
819             }
820             return Channels.failedFuture(pipeline.getChannel(), new RejectedExecutionException("Not attached yet"));
821         }
822     }
823 }