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  
24  import org.jboss.netty.logging.InternalLogger;
25  import org.jboss.netty.logging.InternalLoggerFactory;
26  import org.jboss.netty.util.internal.ConversionUtil;
27  
28  /**
29   * A {@link ChannelPipeline} that might perform better at the cost of
30   * disabled dynamic insertion and removal of {@link ChannelHandler}s.
31   * An attempt to insert, remove, or replace a handler in this pipeline will
32   * trigger an {@link UnsupportedOperationException}.
33   *
34   * @deprecated use {@link DefaultChannelPipeline}
35   */
36  @Deprecated
37  public class StaticChannelPipeline implements ChannelPipeline {
38  
39      // FIXME Code duplication with DefaultChannelPipeline
40      static final InternalLogger logger = InternalLoggerFactory.getInstance(StaticChannelPipeline.class);
41  
42      private volatile Channel channel;
43      private volatile ChannelSink sink;
44      private final StaticChannelHandlerContext[] contexts;
45      private final int lastIndex;
46      private final Map<String, StaticChannelHandlerContext> name2ctx =
47          new HashMap<String, StaticChannelHandlerContext>(4);
48  
49      /**
50       * Creates a new pipeline from the specified handlers.
51       * The names of the specified handlers are generated automatically;
52       * the first handler's name is {@code "0"}, the second handler's name is
53       * {@code "1"}, the third handler's name is {@code "2"}, and so on.
54       */
55      public StaticChannelPipeline(ChannelHandler... handlers) {
56          if (handlers == null) {
57              throw new NullPointerException("handlers");
58          }
59          if (handlers.length == 0) {
60              throw new IllegalArgumentException("no handlers specified");
61          }
62  
63          // Get the number of first non-null handlers.
64          StaticChannelHandlerContext[] contexts =
65              new StaticChannelHandlerContext[handlers.length];
66          int nContexts;
67          for (nContexts = 0; nContexts < contexts.length; nContexts ++) {
68              ChannelHandler h = handlers[nContexts];
69              if (h == null) {
70                  break;
71              }
72          }
73  
74          if (nContexts == contexts.length) {
75              this.contexts = contexts;
76              lastIndex = contexts.length - 1;
77          } else {
78              this.contexts = contexts =
79                  new StaticChannelHandlerContext[nContexts];
80              lastIndex = nContexts - 1;
81          }
82  
83          // Initialize the first non-null handlers only.
84          for (int i = 0; i < nContexts; i ++) {
85              ChannelHandler h = handlers[i];
86              String name = ConversionUtil.toString(i);
87              StaticChannelHandlerContext ctx =
88                  new StaticChannelHandlerContext(i, name, h);
89              contexts[i] = ctx;
90              name2ctx.put(name, ctx);
91          }
92  
93          for (ChannelHandlerContext ctx: contexts) {
94              callBeforeAdd(ctx);
95              callAfterAdd(ctx);
96          }
97      }
98  
99      public ChannelFuture execute(Runnable task) {
100         return getSink().execute(this, task);
101     }
102 
103     public Channel getChannel() {
104         return channel;
105     }
106 
107     public ChannelSink getSink() {
108         ChannelSink sink = this.sink;
109         if (sink == null) {
110             return DefaultChannelPipeline.discardingSink;
111         }
112         return sink;
113     }
114 
115     public void attach(Channel channel, ChannelSink sink) {
116         if (channel == null) {
117             throw new NullPointerException("channel");
118         }
119         if (sink == null) {
120             throw new NullPointerException("sink");
121         }
122         if (this.channel != null || this.sink != null) {
123             throw new IllegalStateException("attached already");
124         }
125         this.channel = channel;
126         this.sink = sink;
127     }
128 
129     public boolean isAttached() {
130         return sink != null;
131     }
132 
133     public void addFirst(String name, ChannelHandler handler) {
134         throw new UnsupportedOperationException();
135     }
136 
137     public void addLast(String name, ChannelHandler handler) {
138         throw new UnsupportedOperationException();
139     }
140 
141     public void addBefore(String baseName, String name, ChannelHandler handler) {
142         throw new UnsupportedOperationException();
143     }
144 
145     public void addAfter(String baseName, String name, ChannelHandler handler) {
146         throw new UnsupportedOperationException();
147     }
148 
149     public void remove(ChannelHandler handler) {
150         throw new UnsupportedOperationException();
151     }
152 
153     public ChannelHandler remove(String name) {
154         throw new UnsupportedOperationException();
155     }
156 
157     public <T extends ChannelHandler> T remove(Class<T> handlerType) {
158         throw new UnsupportedOperationException();
159     }
160 
161     public ChannelHandler removeFirst() {
162         throw new UnsupportedOperationException();
163     }
164 
165     public ChannelHandler removeLast() {
166         throw new UnsupportedOperationException();
167     }
168 
169     public void replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) {
170         throw new UnsupportedOperationException();
171     }
172 
173     public ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler) {
174         throw new UnsupportedOperationException();
175     }
176 
177     public <T extends ChannelHandler> T replace(
178             Class<T> oldHandlerType, String newName, ChannelHandler newHandler) {
179         throw new UnsupportedOperationException();
180     }
181 
182     private static void callBeforeAdd(ChannelHandlerContext ctx) {
183         if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) {
184             return;
185         }
186 
187         LifeCycleAwareChannelHandler h =
188             (LifeCycleAwareChannelHandler) ctx.getHandler();
189 
190         try {
191             h.beforeAdd(ctx);
192         } catch (Throwable t) {
193             throw new ChannelHandlerLifeCycleException(
194                     h.getClass().getName() +
195                     ".beforeAdd() has thrown an exception; not adding.", t);
196         }
197     }
198 
199     private static void callAfterAdd(ChannelHandlerContext ctx) {
200         if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) {
201             return;
202         }
203 
204         LifeCycleAwareChannelHandler h =
205             (LifeCycleAwareChannelHandler) ctx.getHandler();
206 
207         try {
208             h.afterAdd(ctx);
209         } catch (Throwable t) {
210             boolean removed = false;
211             try {
212                 callBeforeRemove(ctx);
213                 callAfterRemove(ctx);
214                 removed = true;
215             } catch (Throwable t2) {
216                 logger.warn("Failed to remove a handler: " + ctx.getName(), t2);
217             }
218 
219             if (removed) {
220                 throw new ChannelHandlerLifeCycleException(
221                         h.getClass().getName() +
222                         ".afterAdd() has thrown an exception; removed.", t);
223             } else {
224                 throw new ChannelHandlerLifeCycleException(
225                         h.getClass().getName() +
226                         ".afterAdd() has thrown an exception; also failed to remove.", t);
227             }
228         }
229     }
230 
231     private static void callBeforeRemove(ChannelHandlerContext ctx) {
232         if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) {
233             return;
234         }
235 
236         LifeCycleAwareChannelHandler h =
237             (LifeCycleAwareChannelHandler) ctx.getHandler();
238 
239         try {
240             h.beforeRemove(ctx);
241         } catch (Throwable t) {
242             throw new ChannelHandlerLifeCycleException(
243                     h.getClass().getName() +
244                     ".beforeRemove() has thrown an exception; not removing.", t);
245         }
246     }
247 
248     private static void callAfterRemove(ChannelHandlerContext ctx) {
249         if (!(ctx.getHandler() instanceof LifeCycleAwareChannelHandler)) {
250             return;
251         }
252 
253         LifeCycleAwareChannelHandler h =
254             (LifeCycleAwareChannelHandler) ctx.getHandler();
255 
256         try {
257             h.afterRemove(ctx);
258         } catch (Throwable t) {
259             throw new ChannelHandlerLifeCycleException(
260                     h.getClass().getName() +
261                     ".afterRemove() has thrown an exception.", t);
262         }
263     }
264 
265     public ChannelHandler getFirst() {
266         return contexts[0].getHandler();
267     }
268 
269     public ChannelHandler getLast() {
270         return contexts[contexts.length - 1].getHandler();
271     }
272 
273     public ChannelHandler get(String name) {
274         StaticChannelHandlerContext ctx = name2ctx.get(name);
275         if (ctx == null) {
276             return null;
277         } else {
278             return ctx.getHandler();
279         }
280     }
281 
282     public <T extends ChannelHandler> T get(Class<T> handlerType) {
283         ChannelHandlerContext ctx = getContext(handlerType);
284         if (ctx == null) {
285             return null;
286         } else {
287             return (T) ctx.getHandler();
288         }
289     }
290 
291     public ChannelHandlerContext getContext(String name) {
292         if (name == null) {
293             throw new NullPointerException("name");
294         }
295         return name2ctx.get(name);
296     }
297 
298     public ChannelHandlerContext getContext(ChannelHandler handler) {
299         if (handler == null) {
300             throw new NullPointerException("handler");
301         }
302         for (StaticChannelHandlerContext ctx: contexts) {
303             if (ctx.getHandler() == handler) {
304                 return ctx;
305             }
306         }
307         return null;
308     }
309 
310     public ChannelHandlerContext getContext(Class<? extends ChannelHandler> handlerType) {
311         if (handlerType == null) {
312             throw new NullPointerException("handlerType");
313         }
314         for (StaticChannelHandlerContext ctx: contexts) {
315             if (handlerType.isAssignableFrom(ctx.getHandler().getClass())) {
316                 return ctx;
317             }
318         }
319         return null;
320     }
321 
322     public List<String> getNames() {
323         List<String> list = new ArrayList<String>();
324         for (StaticChannelHandlerContext ctx: contexts) {
325             list.add(ctx.getName());
326         }
327         return list;
328     }
329 
330     public Map<String, ChannelHandler> toMap() {
331         Map<String, ChannelHandler> map = new LinkedHashMap<String, ChannelHandler>();
332         for (StaticChannelHandlerContext ctx: contexts) {
333             map.put(ctx.getName(), ctx.getHandler());
334         }
335         return map;
336     }
337 
338     /**
339      * Returns the {@link String} representation of this pipeline.
340      */
341     @Override
342     public String toString() {
343         StringBuilder buf = new StringBuilder();
344         buf.append(getClass().getSimpleName());
345         buf.append('{');
346 
347         for (StaticChannelHandlerContext ctx: contexts) {
348             buf.append('(');
349             buf.append(ctx.getName());
350             buf.append(" = ");
351             buf.append(ctx.getHandler().getClass().getName());
352             buf.append(')');
353             buf.append(", ");
354         }
355         buf.replace(buf.length() - 2, buf.length(), "}");
356         return buf.toString();
357     }
358 
359     public void sendUpstream(ChannelEvent e) {
360         StaticChannelHandlerContext head = getActualUpstreamContext(0);
361         if (head == null) {
362             logger.warn(
363                     "The pipeline contains no upstream handlers; discarding: " + e);
364             return;
365         }
366 
367         sendUpstream(head, e);
368     }
369 
370     void sendUpstream(StaticChannelHandlerContext ctx, ChannelEvent e) {
371         try {
372             ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e);
373         } catch (Throwable t) {
374             notifyHandlerException(e, t);
375         }
376     }
377 
378     public void sendDownstream(ChannelEvent e) {
379         StaticChannelHandlerContext tail = getActualDownstreamContext(lastIndex);
380         if (tail == null) {
381             try {
382                 getSink().eventSunk(this, e);
383                 return;
384             } catch (Throwable t) {
385                 notifyHandlerException(e, t);
386                 return;
387             }
388         }
389 
390         sendDownstream(tail, e);
391     }
392 
393     void sendDownstream(StaticChannelHandlerContext ctx, ChannelEvent e) {
394         if (e instanceof UpstreamMessageEvent) {
395             throw new IllegalArgumentException("cannot send an upstream event to downstream");
396         }
397 
398         try {
399             ((ChannelDownstreamHandler) ctx.getHandler()).handleDownstream(ctx, e);
400         } catch (Throwable t) {
401             // Unlike an upstream event, a downstream event usually has an
402             // incomplete future which is supposed to be updated by ChannelSink.
403             // However, if an exception is raised before the event reaches at
404             // ChannelSink, the future is not going to be updated, so we update
405             // here.
406             e.getFuture().setFailure(t);
407             notifyHandlerException(e, t);
408         }
409     }
410 
411     StaticChannelHandlerContext getActualUpstreamContext(int index) {
412         for (int i = index; i < contexts.length; i ++) {
413             StaticChannelHandlerContext ctx = contexts[i];
414             if (ctx.canHandleUpstream()) {
415                 return ctx;
416             }
417         }
418         return null;
419     }
420 
421     StaticChannelHandlerContext getActualDownstreamContext(int index) {
422         for (int i = index; i >= 0; i --) {
423             StaticChannelHandlerContext ctx = contexts[i];
424             if (ctx.canHandleDownstream()) {
425                 return ctx;
426             }
427         }
428         return null;
429     }
430 
431     protected void notifyHandlerException(ChannelEvent e, Throwable t) {
432         if (e instanceof ExceptionEvent) {
433             logger.warn(
434                     "An exception was thrown by a user handler " +
435                     "while handling an exception event (" + e + ')', t);
436             return;
437         }
438 
439         ChannelPipelineException pe;
440         if (t instanceof ChannelPipelineException) {
441             pe = (ChannelPipelineException) t;
442         } else {
443             pe = new ChannelPipelineException(t);
444         }
445 
446         try {
447             sink.exceptionCaught(this, e, pe);
448         } catch (Exception e1) {
449             logger.warn("An exception was thrown by an exception handler.", e1);
450         }
451     }
452 
453     private final class StaticChannelHandlerContext implements ChannelHandlerContext {
454         private final int index;
455         private final String name;
456         private final ChannelHandler handler;
457         private final boolean canHandleUpstream;
458         private final boolean canHandleDownstream;
459         private volatile Object attachment;
460 
461         StaticChannelHandlerContext(
462                 int index, String name, ChannelHandler handler) {
463 
464             if (name == null) {
465                 throw new NullPointerException("name");
466             }
467             if (handler == null) {
468                 throw new NullPointerException("handler");
469             }
470             canHandleUpstream = handler instanceof ChannelUpstreamHandler;
471             canHandleDownstream = handler instanceof ChannelDownstreamHandler;
472 
473 
474             if (!canHandleUpstream && !canHandleDownstream) {
475                 throw new IllegalArgumentException(
476                         "handler must be either " +
477                         ChannelUpstreamHandler.class.getName() + " or " +
478                         ChannelDownstreamHandler.class.getName() + '.');
479             }
480 
481             this.index = index;
482             this.name = name;
483             this.handler = handler;
484         }
485 
486         public Channel getChannel() {
487             return getPipeline().getChannel();
488         }
489 
490         public ChannelPipeline getPipeline() {
491             return StaticChannelPipeline.this;
492         }
493 
494         public boolean canHandleDownstream() {
495             return canHandleDownstream;
496         }
497 
498         public boolean canHandleUpstream() {
499             return canHandleUpstream;
500         }
501 
502         public ChannelHandler getHandler() {
503             return handler;
504         }
505 
506         public String getName() {
507             return name;
508         }
509 
510         public Object getAttachment() {
511             return attachment;
512         }
513 
514         public void setAttachment(Object attachment) {
515             this.attachment = attachment;
516         }
517 
518         public void sendDownstream(ChannelEvent e) {
519             StaticChannelHandlerContext prev = getActualDownstreamContext(index - 1);
520             if (prev == null) {
521                 try {
522                     getSink().eventSunk(StaticChannelPipeline.this, e);
523                 } catch (Throwable t) {
524                     notifyHandlerException(e, t);
525                 }
526             } else {
527                 StaticChannelPipeline.this.sendDownstream(prev, e);
528             }
529         }
530 
531         public void sendUpstream(ChannelEvent e) {
532             StaticChannelHandlerContext next = getActualUpstreamContext(index + 1);
533             if (next != null) {
534                 StaticChannelPipeline.this.sendUpstream(next, e);
535             }
536         }
537     }
538 }