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 }