View Javadoc

    1   /*
    2    * Copyright 2009 Red Hat, Inc.
    3    *
    4    * Red Hat licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the
    6    * 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.net.SocketAddress;
    19  import java.util.Map;
    20
    21  import org.jboss.netty.util.internal.ConversionUtil;
    22
    23
    24  /**
    25   * A helper class which provides various convenience methods related with
    26   * {@link Channel}, {@link ChannelHandler}, and {@link ChannelPipeline}.
    27   *
    28   * <h3>Factory methods</h3>
    29   * <p>
    30   * It is always recommended to use the factory methods provided by
    31   * {@link Channels} rather than calling the constructor of the implementation
    32   * types.
    33   * <ul>
    34   * <li>{@link #pipeline()}</li>
    35   * <li>{@link #pipeline(ChannelPipeline)}</li>
    36   * <li>{@link #pipelineFactory(ChannelPipeline)}</li>
    37   * <li>{@link #succeededFuture(Channel)}</li>
    38   * <li>{@link #failedFuture(Channel, Throwable)}</li>
    39   * </ul>
    40   *
    41   * <h3>Upstream and downstream event generation</h3>
    42   * <p>
    43   * Various event generation methods are provided to simplify the generation of
    44   * upstream events and downstream events.  It is always recommended to use the
    45   * event generation methods provided by {@link Channels} rather than calling
    46   * {@link ChannelHandlerContext#sendUpstream(ChannelEvent)} or
    47   * {@link ChannelHandlerContext#sendDownstream(ChannelEvent)} by yourself.
    48   *
    49   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    50   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    51   *
    52   * @apiviz.landmark
    53   *
    54   * @version $Rev: 2210 $, $Date: 2010-03-04 08:11:39 +0900 (Thu, 04 Mar 2010) $
    55   */
    56  public class Channels {
    57
    58      // pipeline factory methods
    59
    60      /**
    61       * Creates a new {@link ChannelPipeline}.
    62       */
    63      public static ChannelPipeline pipeline() {
    64          return new DefaultChannelPipeline();
    65      }
    66
    67      /**
    68       * Creates a new {@link ChannelPipeline} which contains the specified
    69       * {@link ChannelHandler}s.  The names of the specified handlers are
    70       * generated automatically; the first handler's name is {@code "0"},
    71       * the second handler's name is {@code "1"}, the third handler's name is
    72       * {@code "2"}, and so on.
    73       */
    74      public static ChannelPipeline pipeline(ChannelHandler... handlers) {
    75          if (handlers == null) {
    76              throw new NullPointerException("handlers");
    77          }
    78
    79          ChannelPipeline newPipeline = pipeline();
    80          for (int i = 0; i < handlers.length; i ++) {
    81              ChannelHandler h = handlers[i];
    82              if (h == null) {
    83                  break;
    84              }
    85              newPipeline.addLast(ConversionUtil.toString(i), h);
    86          }
    87          return newPipeline;
    88      }
    89
    90      /**
    91       * Creates a new {@link ChannelPipeline} which contains the same entries
    92       * with the specified {@code pipeline}.  Please note that only the names
    93       * and the references of the {@link ChannelHandler}s will be copied; a new
    94       * {@link ChannelHandler} instance will never be created.
    95       */
    96      public static ChannelPipeline pipeline(ChannelPipeline pipeline) {
    97          ChannelPipeline newPipeline = pipeline();
    98          for (Map.Entry<String, ChannelHandler> e: pipeline.toMap().entrySet()) {
    99              newPipeline.addLast(e.getKey(), e.getValue());
    100         }
    101         return newPipeline;
    102     }
    103
    104     /**
    105      * Creates a new {@link ChannelPipelineFactory} which creates a new
    106      * {@link ChannelPipeline} which contains the same entries with the
    107      * specified {@code pipeline}.  Please note that only the names and the
    108      * references of the {@link ChannelHandler}s will be copied; a new
    109      * {@link ChannelHandler} instance will never be created.
    110      */
    111     public static ChannelPipelineFactory pipelineFactory(
    112             final ChannelPipeline pipeline) {
    113         return new ChannelPipelineFactory() {
    114             public ChannelPipeline getPipeline() {
    115                 return pipeline(pipeline);
    116             }
    117         };
    118     }
    119
    120     // future factory methods
    121
    122     /**
    123      * Creates a new non-cancellable {@link ChannelFuture} for the specified
    124      * {@link Channel}.
    125      */
    126     public static ChannelFuture future(Channel channel) {
    127         return future(channel, false);
    128     }
    129
    130     /**
    131      * Creates a new {@link ChannelFuture} for the specified {@link Channel}.
    132      *
    133      * @param cancellable {@code true} if and only if the returned future
    134      *                    can be canceled by {@link ChannelFuture#cancel()}
    135      */
    136     public static ChannelFuture future(Channel channel, boolean cancellable) {
    137         return new DefaultChannelFuture(channel, cancellable);
    138     }
    139
    140     /**
    141      * Creates a new {@link ChannelFuture} which is already succeeded for the
    142      * specified {@link Channel}.
    143      */
    144     public static ChannelFuture succeededFuture(Channel channel) {
    145         if (channel instanceof AbstractChannel) {
    146             return ((AbstractChannel) channel).getSucceededFuture();
    147         } else {
    148             return new SucceededChannelFuture(channel);
    149         }
    150     }
    151
    152     /**
    153      * Creates a new {@link ChannelFuture} which has failed already for the
    154      * specified {@link Channel}.
    155      *
    156      * @param cause the cause of the failure
    157      */
    158     public static ChannelFuture failedFuture(Channel channel, Throwable cause) {
    159         return new FailedChannelFuture(channel, cause);
    160     }
    161
    162     // event emission methods
    163
    164     /**
    165      * Sends a {@code "channelOpen"} event to the first
    166      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    167      * the specified {@link Channel}.  If the specified channel has a parent,
    168      * a {@code "childChannelOpen"} event will be sent, too.
    169      */
    170     public static void fireChannelOpen(Channel channel) {
    171         // Notify the parent handler.
    172         if (channel.getParent() != null) {
    173             fireChildChannelStateChanged(channel.getParent(), channel);
    174         }
    175
    176         channel.getPipeline().sendUpstream(
    177                 new UpstreamChannelStateEvent(
    178                         channel, ChannelState.OPEN, Boolean.TRUE));
    179     }
    180
    181     /**
    182      * Sends a {@code "channelOpen"} event to the
    183      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    184      * from the handler associated with the specified
    185      * {@link ChannelHandlerContext}.
    186      * <p>
    187      * Please note that this method does not trigger a
    188      * {@code "childChannelOpen"} event unlike {@link #fireChannelOpen(Channel)}
    189      * method.
    190      */
    191     public static void fireChannelOpen(ChannelHandlerContext ctx) {
    192         ctx.sendUpstream(new UpstreamChannelStateEvent(
    193                 ctx.getChannel(), ChannelState.OPEN, Boolean.TRUE));
    194     }
    195
    196     /**
    197      * Sends a {@code "channelBound"} event to the first
    198      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    199      * the specified {@link Channel}.
    200      *
    201      * @param localAddress
    202      *        the local address where the specified channel is bound
    203      */
    204     public static void fireChannelBound(Channel channel, SocketAddress localAddress) {
    205         channel.getPipeline().sendUpstream(
    206                 new UpstreamChannelStateEvent(
    207                         channel, ChannelState.BOUND, localAddress));
    208     }
    209
    210     /**
    211      * Sends a {@code "channelBound"} event to the
    212      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    213      * from the handler associated with the specified
    214      * {@link ChannelHandlerContext}.
    215      *
    216      * @param localAddress
    217      *        the local address where the specified channel is bound
    218      */
    219     public static void fireChannelBound(ChannelHandlerContext ctx, SocketAddress localAddress) {
    220         ctx.sendUpstream(new UpstreamChannelStateEvent(
    221                 ctx.getChannel(), ChannelState.BOUND, localAddress));
    222     }
    223
    224     /**
    225      * Sends a {@code "channelConnected"} event to the first
    226      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    227      * the specified {@link Channel}.
    228      *
    229      * @param remoteAddress
    230      *        the remote address where the specified channel is connected
    231      */
    232     public static void fireChannelConnected(Channel channel, SocketAddress remoteAddress) {
    233         channel.getPipeline().sendUpstream(
    234                 new UpstreamChannelStateEvent(
    235                         channel, ChannelState.CONNECTED, remoteAddress));
    236     }
    237
    238     /**
    239      * Sends a {@code "channelConnected"} event to the
    240      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    241      * from the handler associated with the specified
    242      * {@link ChannelHandlerContext}.
    243      *
    244      * @param remoteAddress
    245      *        the remote address where the specified channel is connected
    246      */
    247     public static void fireChannelConnected(ChannelHandlerContext ctx, SocketAddress remoteAddress) {
    248
    249         ctx.sendUpstream(new UpstreamChannelStateEvent(
    250                 ctx.getChannel(), ChannelState.CONNECTED, remoteAddress));
    251     }
    252
    253     /**
    254      * Sends a {@code "messageReceived"} event to the first
    255      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    256      * the specified {@link Channel}.
    257      *
    258      * @param message  the received message
    259      */
    260     public static void fireMessageReceived(Channel channel, Object message) {
    261         fireMessageReceived(channel, message, null);
    262     }
    263
    264     /**
    265      * Sends a {@code "messageReceived"} event to the first
    266      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    267      * the specified {@link Channel} belongs.
    268      *
    269      * @param message        the received message
    270      * @param remoteAddress  the remote address where the received message
    271      *                       came from
    272      */
    273     public static void fireMessageReceived(Channel channel, Object message, SocketAddress remoteAddress) {
    274         channel.getPipeline().sendUpstream(
    275                 new UpstreamMessageEvent(channel, message, remoteAddress));
    276     }
    277
    278     /**
    279      * Sends a {@code "messageReceived"} event to the
    280      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    281      * from the handler associated with the specified
    282      * {@link ChannelHandlerContext}.
    283      *
    284      * @param message  the received message
    285      */
    286     public static void fireMessageReceived(ChannelHandlerContext ctx, Object message) {
    287         ctx.sendUpstream(new UpstreamMessageEvent(ctx.getChannel(), message, null));
    288     }
    289
    290     /**
    291      * Sends a {@code "messageReceived"} event to the
    292      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    293      * from the handler associated with the specified
    294      * {@link ChannelHandlerContext}.
    295      *
    296      * @param message        the received message
    297      * @param remoteAddress  the remote address where the received message
    298      *                       came from
    299      */
    300     public static void fireMessageReceived(
    301             ChannelHandlerContext ctx, Object message, SocketAddress remoteAddress) {
    302         ctx.sendUpstream(new UpstreamMessageEvent(
    303                 ctx.getChannel(), message, remoteAddress));
    304     }
    305
    306     /**
    307      * Sends a {@code "writeComplete"} event to the first
    308      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    309      * the specified {@link Channel}.
    310      */
    311     public static void fireWriteComplete(Channel channel, long amount) {
    312         if (amount == 0) {
    313             return;
    314         }
    315
    316         channel.getPipeline().sendUpstream(
    317                 new DefaultWriteCompletionEvent(channel, amount));
    318     }
    319
    320     /**
    321      * Sends a {@code "writeComplete"} event to the
    322      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    323      * from the handler associated with the specified
    324      * {@link ChannelHandlerContext}.
    325      */
    326     public static void fireWriteComplete(ChannelHandlerContext ctx, long amount) {
    327         ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount));
    328     }
    329     /**
    330      * Sends a {@code "channelInterestChanged"} event to the first
    331      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    332      * the specified {@link Channel}.
    333      */
    334     public static void fireChannelInterestChanged(Channel channel) {
    335         channel.getPipeline().sendUpstream(
    336                 new UpstreamChannelStateEvent(
    337                         channel, ChannelState.INTEREST_OPS, Channel.OP_READ));
    338     }
    339
    340     /**
    341      * Sends a {@code "channelInterestChanged"} event to the
    342      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    343      * from the handler associated with the specified
    344      * {@link ChannelHandlerContext}.
    345      */
    346     public static void fireChannelInterestChanged(
    347             ChannelHandlerContext ctx) {
    348
    349         ctx.sendUpstream(
    350                 new UpstreamChannelStateEvent(
    351                         ctx.getChannel(), ChannelState.INTEREST_OPS, Channel.OP_READ));
    352     }
    353
    354     /**
    355      * Sends a {@code "channelDisconnected"} event to the first
    356      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    357      * the specified {@link Channel}.
    358      */
    359     public static void fireChannelDisconnected(Channel channel) {
    360         channel.getPipeline().sendUpstream(
    361                 new UpstreamChannelStateEvent(
    362                         channel, ChannelState.CONNECTED, null));
    363     }
    364
    365     /**
    366      * Sends a {@code "channelDisconnected"} event to the
    367      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    368      * from the handler associated with the specified
    369      * {@link ChannelHandlerContext}.
    370      */
    371     public static void fireChannelDisconnected(ChannelHandlerContext ctx) {
    372         ctx.sendUpstream(new UpstreamChannelStateEvent(
    373                 ctx.getChannel(), ChannelState.CONNECTED, null));
    374     }
    375
    376     /**
    377      * Sends a {@code "channelUnbound"} event to the first
    378      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    379      * the specified {@link Channel}.
    380      */
    381     public static void fireChannelUnbound(Channel channel) {
    382         channel.getPipeline().sendUpstream(new UpstreamChannelStateEvent(
    383                 channel, ChannelState.BOUND, null));
    384     }
    385
    386     /**
    387      * Sends a {@code "channelUnbound"} event to the
    388      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    389      * from the handler associated with the specified
    390      * {@link ChannelHandlerContext}.
    391      */
    392     public static void fireChannelUnbound(ChannelHandlerContext ctx) {
    393
    394         ctx.sendUpstream(new UpstreamChannelStateEvent(
    395                 ctx.getChannel(), ChannelState.BOUND, null));
    396     }
    397
    398     /**
    399      * Sends a {@code "channelClosed"} event to the first
    400      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    401      * the specified {@link Channel}.
    402      */
    403     public static void fireChannelClosed(Channel channel) {
    404         channel.getPipeline().sendUpstream(
    405                 new UpstreamChannelStateEvent(
    406                         channel, ChannelState.OPEN, Boolean.FALSE));
    407
    408         // Notify the parent handler.
    409         if (channel.getParent() != null) {
    410             fireChildChannelStateChanged(channel.getParent(), channel);
    411         }
    412     }
    413
    414     /**
    415      * Sends a {@code "channelClosed"} event to the
    416      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    417      * from the handler associated with the specified
    418      * {@link ChannelHandlerContext}.
    419      */
    420     public static void fireChannelClosed(ChannelHandlerContext ctx) {
    421         ctx.sendUpstream(
    422                 new UpstreamChannelStateEvent(
    423                         ctx.getChannel(), ChannelState.OPEN, Boolean.FALSE));
    424     }
    425
    426     /**
    427      * Sends a {@code "exceptionCaught"} event to the first
    428      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
    429      * the specified {@link Channel}.
    430      */
    431     public static void fireExceptionCaught(Channel channel, Throwable cause) {
    432         channel.getPipeline().sendUpstream(
    433                 new DefaultExceptionEvent(channel, cause));
    434     }
    435
    436     /**
    437      * Sends a {@code "exceptionCaught"} event to the
    438      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
    439      * from the handler associated with the specified
    440      * {@link ChannelHandlerContext}.
    441      */
    442     public static void fireExceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    443         ctx.sendUpstream(new DefaultExceptionEvent(ctx.getChannel(), cause));
    444     }
    445
    446     private static void fireChildChannelStateChanged(
    447             Channel channel, Channel childChannel) {
    448         channel.getPipeline().sendUpstream(
    449                 new DefaultChildChannelStateEvent(channel, childChannel));
    450     }
    451
    452     /**
    453      * Sends a {@code "bind"} request to the last
    454      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
    455      * the specified {@link Channel}.
    456      *
    457      * @param channel  the channel to bind
    458      * @param localAddress  the local address to bind to
    459      *
    460      * @return the {@link ChannelFuture} which will be notified when the
    461      *         bind operation is done
    462      */
    463     public static ChannelFuture bind(Channel channel, SocketAddress localAddress) {
    464         if (localAddress == null) {
    465             throw new NullPointerException("localAddress");
    466         }
    467         ChannelFuture future = future(channel);
    468         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
    469                 channel, future, ChannelState.BOUND, localAddress));
    470         return future;
    471     }
    472
    473     /**
    474      * Sends a {@code "bind"} request to the
    475      * {@link ChannelDownstreamHandler} which is placed in the closest
    476      * downstream from the handler associated with the specified
    477      * {@link ChannelHandlerContext}.
    478      *
    479      * @param ctx     the context
    480      * @param future  the future which will be notified when the bind
    481      *                operation is done
    482      * @param localAddress the local address to bind to
    483      */
    484     public static void bind(
    485             ChannelHandlerContext ctx, ChannelFuture future, SocketAddress localAddress) {
    486         if (localAddress == null) {
    487             throw new NullPointerException("localAddress");
    488         }
    489         ctx.sendDownstream(new DownstreamChannelStateEvent(
    490                 ctx.getChannel(), future, ChannelState.BOUND, localAddress));
    491     }
    492
    493     /**
    494      * Sends a {@code "unbind"} request to the
    495      * {@link ChannelDownstreamHandler} which is placed in the closest
    496      * downstream from the handler associated with the specified
    497      * {@link ChannelHandlerContext}.
    498      *
    499      * @param ctx     the context
    500      * @param future  the future which will be notified when the unbind
    501      *                operation is done
    502      */
    503     public static void unbind(ChannelHandlerContext ctx, ChannelFuture future) {
    504         ctx.sendDownstream(new DownstreamChannelStateEvent(
    505                 ctx.getChannel(), future, ChannelState.BOUND, null));
    506     }
    507
    508     /**
    509      * Sends a {@code "unbind"} request to the last
    510      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
    511      * the specified {@link Channel}.
    512      *
    513      * @param channel  the channel to unbind
    514      *
    515      * @return the {@link ChannelFuture} which will be notified when the
    516      *         unbind operation is done
    517      */
    518     public static ChannelFuture unbind(Channel channel) {
    519         ChannelFuture future = future(channel);
    520         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
    521                 channel, future, ChannelState.BOUND, null));
    522         return future;
    523     }
    524
    525     /**
    526      * Sends a {@code "connect"} request to the last
    527      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
    528      * the specified {@link Channel}.
    529      *
    530      * @param channel  the channel to attempt a connection
    531      * @param remoteAddress  the remote address to connect to
    532      *
    533      * @return the {@link ChannelFuture} which will be notified when the
    534      *         connection attempt is done
    535      */
    536     public static ChannelFuture connect(Channel channel, SocketAddress remoteAddress) {
    537         if (remoteAddress == null) {
    538             throw new NullPointerException("remoteAddress");
    539         }
    540         ChannelFuture future = future(channel, true);
    541         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
    542                 channel, future, ChannelState.CONNECTED, remoteAddress));
    543         return future;
    544     }
    545
    546     /**
    547      * Sends a {@code "connect"} request to the
    548      * {@link ChannelDownstreamHandler} which is placed in the closest
    549      * downstream from the handler associated with the specified
    550      * {@link ChannelHandlerContext}.
    551      *
    552      * @param ctx     the context
    553      * @param future  the future which will be notified when the connection
    554      *                attempt is done
    555      * @param remoteAddress the remote address to connect to
    556      */
    557     public static void connect(
    558             ChannelHandlerContext ctx, ChannelFuture future, SocketAddress remoteAddress) {
    559         if (remoteAddress == null) {
    560             throw new NullPointerException("remoteAddress");
    561         }
    562         ctx.sendDownstream(new DownstreamChannelStateEvent(
    563                 ctx.getChannel(), future, ChannelState.CONNECTED, remoteAddress));
    564     }
    565
    566     /**
    567      * Sends a {@code "write"} request to the last
    568      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
    569      * the specified {@link Channel}.
    570      *
    571      * @param channel  the channel to write a message
    572      * @param message  the message to write to the channel
    573      *
    574      * @return the {@link ChannelFuture} which will be notified when the
    575      *         write operation is done
    576      */
    577     public static ChannelFuture write(Channel channel, Object message) {
    578         return write(channel, message, null);
    579     }
    580
    581     /**
    582      * Sends a {@code "write"} request to the
    583      * {@link ChannelDownstreamHandler} which is placed in the closest
    584      * downstream from the handler associated with the specified
    585      * {@link ChannelHandlerContext}.
    586      *
    587      * @param ctx     the context
    588      * @param future  the future which will be notified when the write
    589      *                operation is done
    590      */
    591     public static void write(
    592             ChannelHandlerContext ctx, ChannelFuture future, Object message) {
    593         write(ctx, future, message, null);
    594     }
    595
    596     /**
    597      * Sends a {@code "write"} request to the last
    598      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
    599      * the specified {@link Channel}.
    600      *
    601      * @param channel  the channel to write a message
    602      * @param message  the message to write to the channel
    603      * @param remoteAddress  the destination of the message.
    604      *                       {@code null} to use the default remote address
    605      *
    606      * @return the {@link ChannelFuture} which will be notified when the
    607      *         write operation is done
    608      */
    609     public static ChannelFuture write(Channel channel, Object message, SocketAddress remoteAddress) {
    610         ChannelFuture future = future(channel);
    611         channel.getPipeline().sendDownstream(
    612                 new DownstreamMessageEvent(channel, future, message, remoteAddress));
    613         return future;
    614     }
    615
    616     /**
    617      * Sends a {@code "write"} request to the
    618      * {@link ChannelDownstreamHandler} which is placed in the closest
    619      * downstream from the handler associated with the specified
    620      * {@link ChannelHandlerContext}.
    621      *
    622      * @param ctx     the context
    623      * @param future  the future which will be notified when the write
    624      *                operation is done
    625      * @param message the message to write to the channel
    626      * @param remoteAddress  the destination of the message.
    627      *                       {@code null} to use the default remote address.
    628      */
    629     public static void write(
    630             ChannelHandlerContext ctx, ChannelFuture future,
    631             Object message, SocketAddress remoteAddress) {
    632         ctx.sendDownstream(
    633                 new DownstreamMessageEvent(ctx.getChannel(), future, message, remoteAddress));
    634     }
    635
    636     /**
    637      * Sends a {@code "setInterestOps"} request to the last
    638      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
    639      * the specified {@link Channel}.
    640      *
    641      * @param channel     the channel to change its interestOps
    642      * @param interestOps the new interestOps
    643      *
    644      * @return the {@link ChannelFuture} which will be notified when the
    645      *         interestOps is changed
    646      */
    647     public static ChannelFuture setInterestOps(Channel channel, int interestOps) {
    648         validateInterestOps(interestOps);
    649         interestOps = filterDownstreamInterestOps(interestOps);
    650
    651         ChannelFuture future = future(channel);
    652         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
    653                 channel, future, ChannelState.INTEREST_OPS, Integer.valueOf(interestOps)));
    654         return future;
    655     }
    656
    657     /**
    658      * Sends a {@code "setInterestOps"} request to the
    659      * {@link ChannelDownstreamHandler} which is placed in the closest
    660      * downstream from the handler associated with the specified
    661      * {@link ChannelHandlerContext}.
    662      *
    663      * @param ctx     the context
    664      * @param future  the future which will be notified when the interestOps is
    665      *                changed.
    666      */
    667     public static void setInterestOps(
    668             ChannelHandlerContext ctx, ChannelFuture future, int interestOps) {
    669         validateInterestOps(interestOps);
    670         interestOps = filterDownstreamInterestOps(interestOps);
    671
    672         ctx.sendDownstream(
    673                 new DownstreamChannelStateEvent(
    674                         ctx.getChannel(), future, ChannelState.INTEREST_OPS,
    675                         Integer.valueOf(interestOps)));
    676     }
    677
    678     /**
    679      * Sends a {@code "disconnect"} request to the last
    680      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
    681      * the specified {@link Channel}.
    682      *
    683      * @param channel  the channel to disconnect
    684      *
    685      * @return the {@link ChannelFuture} which will be notified on disconnection
    686      */
    687     public static ChannelFuture disconnect(Channel channel) {
    688         ChannelFuture future = future(channel);
    689         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
    690                 channel, future, ChannelState.CONNECTED, null));
    691         return future;
    692     }
    693
    694     /**
    695      * Sends a {@code "disconnect"} request to the
    696      * {@link ChannelDownstreamHandler} which is placed in the closest
    697      * downstream from the handler associated with the specified
    698      * {@link ChannelHandlerContext}.
    699      *
    700      * @param ctx     the context
    701      * @param future  the future which will be notified on disconnection
    702      */
    703     public static void disconnect(
    704             ChannelHandlerContext ctx, ChannelFuture future) {
    705         ctx.sendDownstream(new DownstreamChannelStateEvent(
    706                 ctx.getChannel(), future, ChannelState.CONNECTED, null));
    707     }
    708
    709     /**
    710      * Sends a {@code "close"} request to the last
    711      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
    712      * the specified {@link Channel}.
    713      *
    714      * @param channel  the channel to close
    715      *
    716      * @return the {@link ChannelFuture} which will be notified on closure
    717      */
    718     public static ChannelFuture close(Channel channel) {
    719         ChannelFuture future = channel.getCloseFuture();
    720         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
    721                 channel, future, ChannelState.OPEN, Boolean.FALSE));
    722         return future;
    723     }
    724
    725     /**
    726      * Sends a {@code "close"} request to the
    727      * {@link ChannelDownstreamHandler} which is placed in the closest
    728      * downstream from the handler associated with the specified
    729      * {@link ChannelHandlerContext}.
    730      *
    731      * @param ctx     the context
    732      * @param future  the future which will be notified on closure
    733      */
    734     public static void close(
    735             ChannelHandlerContext ctx, ChannelFuture future) {
    736         ctx.sendDownstream(new DownstreamChannelStateEvent(
    737                 ctx.getChannel(), future, ChannelState.OPEN, Boolean.FALSE));
    738     }
    739
    740     private static void validateInterestOps(int interestOps) {
    741         switch (interestOps) {
    742         case Channel.OP_NONE:
    743         case Channel.OP_READ:
    744         case Channel.OP_WRITE:
    745         case Channel.OP_READ_WRITE:
    746             break;
    747         default:
    748             throw new IllegalArgumentException(
    749                     "Invalid interestOps: " + interestOps);
    750         }
    751     }
    752
    753     private static int filterDownstreamInterestOps(int interestOps) {
    754         return interestOps & ~Channel.OP_WRITE;
    755     }
    756
    757     private Channels() {
    758         // Unused
    759     }
    760 }