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 org.jboss.netty.logging.InternalLogger;
19  import org.jboss.netty.logging.InternalLoggerFactory;
20  import org.jboss.netty.util.internal.ConversionUtil;
21  
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.LinkedHashMap;
25  import java.util.List;
26  import java.util.Map;
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             @SuppressWarnings("unchecked")
288             T handler = (T) ctx.getHandler();
289             return handler;
290         }
291     }
292 
293     public ChannelHandlerContext getContext(String name) {
294         if (name == null) {
295             throw new NullPointerException("name");
296         }
297         return name2ctx.get(name);
298     }
299 
300     public ChannelHandlerContext getContext(ChannelHandler handler) {
301         if (handler == null) {
302             throw new NullPointerException("handler");
303         }
304         for (StaticChannelHandlerContext ctx: contexts) {
305             if (ctx.getHandler() == handler) {
306                 return ctx;
307             }
308         }
309         return null;
310     }
311 
312     public ChannelHandlerContext getContext(Class<? extends ChannelHandler> handlerType) {
313         if (handlerType == null) {
314             throw new NullPointerException("handlerType");
315         }
316         for (StaticChannelHandlerContext ctx: contexts) {
317             if (handlerType.isAssignableFrom(ctx.getHandler().getClass())) {
318                 return ctx;
319             }
320         }
321         return null;
322     }
323 
324     public List<String> getNames() {
325         List<String> list = new ArrayList<String>();
326         for (StaticChannelHandlerContext ctx: contexts) {
327             list.add(ctx.getName());
328         }
329         return list;
330     }
331 
332     public Map<String, ChannelHandler> toMap() {
333         Map<String, ChannelHandler> map = new LinkedHashMap<String, ChannelHandler>();
334         for (StaticChannelHandlerContext ctx: contexts) {
335             map.put(ctx.getName(), ctx.getHandler());
336         }
337         return map;
338     }
339 
340     /**
341      * Returns the {@link String} representation of this pipeline.
342      */
343     @Override
344     public String toString() {
345         StringBuilder buf = new StringBuilder();
346         buf.append(getClass().getSimpleName());
347         buf.append('{');
348 
349         for (StaticChannelHandlerContext ctx: contexts) {
350             buf.append('(');
351             buf.append(ctx.getName());
352             buf.append(" = ");
353             buf.append(ctx.getHandler().getClass().getName());
354             buf.append(')');
355             buf.append(", ");
356         }
357         buf.replace(buf.length() - 2, buf.length(), "}");
358         return buf.toString();
359     }
360 
361     public void sendUpstream(ChannelEvent e) {
362         StaticChannelHandlerContext head = getActualUpstreamContext(0);
363         if (head == null) {
364             logger.warn(
365                     "The pipeline contains no upstream handlers; discarding: " + e);
366             return;
367         }
368 
369         sendUpstream(head, e);
370     }
371 
372     void sendUpstream(StaticChannelHandlerContext ctx, ChannelEvent e) {
373         try {
374             ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e);
375         } catch (Throwable t) {
376             notifyHandlerException(e, t);
377         }
378     }
379 
380     public void sendDownstream(ChannelEvent e) {
381         StaticChannelHandlerContext tail = getActualDownstreamContext(lastIndex);
382         if (tail == null) {
383             try {
384                 getSink().eventSunk(this, e);
385                 return;
386             } catch (Throwable t) {
387                 notifyHandlerException(e, t);
388                 return;
389             }
390         }
391 
392         sendDownstream(tail, e);
393     }
394 
395     void sendDownstream(StaticChannelHandlerContext ctx, ChannelEvent e) {
396         if (e instanceof UpstreamMessageEvent) {
397             throw new IllegalArgumentException("cannot send an upstream event to downstream");
398         }
399 
400         try {
401             ((ChannelDownstreamHandler) ctx.getHandler()).handleDownstream(ctx, e);
402         } catch (Throwable t) {
403             // Unlike an upstream event, a downstream event usually has an
404             // incomplete future which is supposed to be updated by ChannelSink.
405             // However, if an exception is raised before the event reaches at
406             // ChannelSink, the future is not going to be updated, so we update
407             // here.
408             e.getFuture().setFailure(t);
409             notifyHandlerException(e, t);
410         }
411     }
412 
413     private StaticChannelHandlerContext getActualUpstreamContext(int index) {
414         for (int i = index; i < contexts.length; i ++) {
415             StaticChannelHandlerContext ctx = contexts[i];
416             if (ctx.canHandleUpstream()) {
417                 return ctx;
418             }
419         }
420         return null;
421     }
422 
423     private StaticChannelHandlerContext getActualDownstreamContext(int index) {
424         for (int i = index; i >= 0; i --) {
425             StaticChannelHandlerContext ctx = contexts[i];
426             if (ctx.canHandleDownstream()) {
427                 return ctx;
428             }
429         }
430         return null;
431     }
432 
433     protected void notifyHandlerException(ChannelEvent e, Throwable t) {
434         if (e instanceof ExceptionEvent) {
435             logger.warn(
436                     "An exception was thrown by a user handler " +
437                     "while handling an exception event (" + e + ')', t);
438             return;
439         }
440 
441         ChannelPipelineException pe;
442         if (t instanceof ChannelPipelineException) {
443             pe = (ChannelPipelineException) t;
444         } else {
445             pe = new ChannelPipelineException(t);
446         }
447 
448         try {
449             sink.exceptionCaught(this, e, pe);
450         } catch (Exception e1) {
451             logger.warn("An exception was thrown by an exception handler.", e1);
452         }
453     }
454 
455     private final class StaticChannelHandlerContext implements ChannelHandlerContext {
456         private final int index;
457         private final String name;
458         private final ChannelHandler handler;
459         private final boolean canHandleUpstream;
460         private final boolean canHandleDownstream;
461         private volatile Object attachment;
462 
463         StaticChannelHandlerContext(
464                 int index, String name, ChannelHandler handler) {
465 
466             if (name == null) {
467                 throw new NullPointerException("name");
468             }
469             if (handler == null) {
470                 throw new NullPointerException("handler");
471             }
472             canHandleUpstream = handler instanceof ChannelUpstreamHandler;
473             canHandleDownstream = handler instanceof ChannelDownstreamHandler;
474 
475             if (!canHandleUpstream && !canHandleDownstream) {
476                 throw new IllegalArgumentException(
477                         "handler must be either " +
478                         ChannelUpstreamHandler.class.getName() + " or " +
479                         ChannelDownstreamHandler.class.getName() + '.');
480             }
481 
482             this.index = index;
483             this.name = name;
484             this.handler = handler;
485         }
486 
487         public Channel getChannel() {
488             return getPipeline().getChannel();
489         }
490 
491         public ChannelPipeline getPipeline() {
492             return StaticChannelPipeline.this;
493         }
494 
495         public boolean canHandleDownstream() {
496             return canHandleDownstream;
497         }
498 
499         public boolean canHandleUpstream() {
500             return canHandleUpstream;
501         }
502 
503         public ChannelHandler getHandler() {
504             return handler;
505         }
506 
507         public String getName() {
508             return name;
509         }
510 
511         public Object getAttachment() {
512             return attachment;
513         }
514 
515         public void setAttachment(Object attachment) {
516             this.attachment = attachment;
517         }
518 
519         public void sendDownstream(ChannelEvent e) {
520             StaticChannelHandlerContext prev = getActualDownstreamContext(index - 1);
521             if (prev == null) {
522                 try {
523                     getSink().eventSunk(StaticChannelPipeline.this, e);
524                 } catch (Throwable t) {
525                     notifyHandlerException(e, t);
526                 }
527             } else {
528                 StaticChannelPipeline.this.sendDownstream(prev, e);
529             }
530         }
531 
532         public void sendUpstream(ChannelEvent e) {
533             StaticChannelHandlerContext next = getActualUpstreamContext(index + 1);
534             if (next != null) {
535                 StaticChannelPipeline.this.sendUpstream(next, e);
536             }
537         }
538     }
539 }