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.lang.annotation.Documented;
    19  import java.lang.annotation.ElementType;
    20  import java.lang.annotation.Inherited;
    21  import java.lang.annotation.Retention;
    22  import java.lang.annotation.RetentionPolicy;
    23  import java.lang.annotation.Target;
    24
    25  import org.jboss.netty.bootstrap.Bootstrap;
    26  import org.jboss.netty.channel.group.ChannelGroup;
    27
    28  /**
    29   * Handles or intercepts a {@link ChannelEvent}, and sends a
    30   * {@link ChannelEvent} to the next handler in a {@link ChannelPipeline}.
    31   *
    32   * <h3>Sub-types</h3>
    33   * <p>
    34   * {@link ChannelHandler} itself does not provide any method.  To handle a
    35   * {@link ChannelEvent} you need to implement its sub-interfaces.  There are
    36   * two sub-interfaces which handles a received event, one for upstream events
    37   * and the other for downstream events:
    38   * <ul>
    39   * <li>{@link ChannelUpstreamHandler} handles and intercepts an upstream {@link ChannelEvent}.</li>
    40   * <li>{@link ChannelDownstreamHandler} handles and intercepts a downstream {@link ChannelEvent}.</li>
    41   * </ul>
    42   *
    43   * You will also find more detailed explanation from the documentation of
    44   * each sub-interface on how an event is interpreted when it goes upstream and
    45   * downstream respectively.
    46   *
    47   * <h3>The context object</h3>
    48   * <p>
    49   * A {@link ChannelHandler} is provided with a {@link ChannelHandlerContext}
    50   * object.  A {@link ChannelHandler} is supposed to interact with the
    51   * {@link ChannelPipeline} it belongs to via a context object.  Using the
    52   * context object, the {@link ChannelHandler} can pass events upstream or
    53   * downstream, modify the pipeline dynamically, or store the information
    54   * (attachment) which is specific to the handler.
    55   *
    56   * <h3>State management</h3>
    57   *
    58   * A {@link ChannelHandler} often needs to store some stateful information.
    59   * The simplest and recommended approach is to use member variables:
    60   * <pre>
    61   * public class DataServerHandler extends {@link SimpleChannelHandler} {
    62   *
    63   *     <b>private boolean loggedIn;</b>
    64   *
    65   *     {@code @Override}
    66   *     public void messageReceived({@link ChannelHandlerContext} ctx, {@link MessageEvent} e) {
    67   *         {@link Channel} ch = e.getChannel();
    68   *         Object o = e.getMessage();
    69   *         if (o instanceof LoginMessage) {
    70   *             authenticate((LoginMessage) o);
    71   *             <b>loggedIn = true;</b>
    72   *         } else (o instanceof GetDataMessage) {
    73   *             if (<b>loggedIn</b>) {
    74   *                 ch.write(fetchSecret((GetDataMessage) o));
    75   *             } else {
    76   *                 fail();
    77   *             }
    78   *         }
    79   *     }
    80   *     ...
    81   * }
    82   * </pre>
    83   * Because the handler instance has a state variable which is dedicated to
    84   * one connection, you have to create a new handler instance for each new
    85   * channel to avoid a race condition where a unauthenticated client can get
    86   * the confidential information:
    87   * <pre>
    88   * // Create a new handler instance per channel.
    89   * // See {@link Bootstrap#setPipelineFactory(ChannelPipelineFactory)}.
    90   * public class DataServerPipelineFactory implements {@link ChannelPipelineFactory} {
    91   *     public {@link ChannelPipeline} getPipeline() {
    92   *         return {@link Channels}.pipeline(<b>new DataServerHandler()</b>);
    93   *     }
    94   * }
    95   * </pre>
    96   *
    97   * <h4>Using an attachment</h4>
    98   *
    99   * Although it's recommended to use member variables to store the state of a
    100  * handler, for some reason you might not want to create many handler instances.
    101  * In such a case, you can use an <em>attachment</em> which is provided by
    102  * {@link ChannelHandlerContext}:
    103  * <pre>
    104  * {@code @Sharable}
    105  * public class DataServerHandler extends {@link SimpleChannelHandler} {
    106  *
    107  *     {@code @Override}
    108  *     public void messageReceived({@link ChannelHandlerContext} ctx, {@link MessageEvent} e) {
    109  *         {@link Channel} ch = e.getChannel();
    110  *         Object o = e.getMessage();
    111  *         if (o instanceof LoginMessage) {
    112  *             authenticate((LoginMessage) o);
    113  *             <b>ctx.setAttachment(true)</b>;
    114  *         } else (o instanceof GetDataMessage) {
    115  *             if (<b>Boolean.TRUE.equals(ctx.getAttachment())</b>) {
    116  *                 ch.write(fetchSecret((GetDataMessage) o));
    117  *             } else {
    118  *                 fail();
    119  *             }
    120  *         }
    121  *     }
    122  *     ...
    123  * }
    124  * </pre>
    125  * Now that the state of the handler is stored as an attachment, you can add the
    126  * same handler instance to different pipelines:
    127  * <pre>
    128  * public class DataServerPipelineFactory implements {@link ChannelPipelineFactory} {
    129  *
    130  *     private static final DataServerHandler <b>SHARED</b> = new DataServerHandler();
    131  *
    132  *     public {@link ChannelPipeline} getPipeline() {
    133  *         return {@link Channels}.pipeline(<b>SHARED</b>);
    134  *     }
    135  * }
    136  * </pre>
    137  *
    138  * <h4>Using a {@link ChannelLocal}</h4>
    139  *
    140  * If you have a state variable which needs to be accessed either from other
    141  * handlers or outside handlers, you can use {@link ChannelLocal}:
    142  * <pre>
    143  * public final class DataServerState {
    144  *
    145  *     <b>public static final {@link ChannelLocal}&lt;Boolean&gt; loggedIn = new {@link ChannelLocal}&lt;&gt;() {
    146  *         protected Boolean initialValue(Channel channel) {
    147  *             return false;
    148  *         }
    149  *     }</b>
    150  *     ...
    151  * }
    152  *
    153  * {@code @Sharable}
    154  * public class DataServerHandler extends {@link SimpleChannelHandler} {
    155  *
    156  *     {@code @Override}
    157  *     public void messageReceived({@link ChannelHandlerContext} ctx, {@link MessageEvent} e) {
    158  *         Channel ch = e.getChannel();
    159  *         Object o = e.getMessage();
    160  *         if (o instanceof LoginMessage) {
    161  *             authenticate((LoginMessage) o);
    162  *             <b>DataServerState.loggedIn.set(ch, true);</b>
    163  *         } else (o instanceof GetDataMessage) {
    164  *             if (<b>DataServerState.loggedIn.get(ch)</b>) {
    165  *                 ctx.getChannel().write(fetchSecret((GetDataMessage) o));
    166  *             } else {
    167  *                 fail();
    168  *             }
    169  *         }
    170  *     }
    171  *     ...
    172  * }
    173  *
    174  * // Print the remote addresses of the authenticated clients:
    175  * {@link ChannelGroup} allClientChannels = ...;
    176  * for ({@link Channel} ch: allClientChannels) {
    177  *     if (<b>DataServerState.loggedIn.get(ch)</b>) {
    178  *         System.out.println(ch.getRemoteAddress());
    179  *     }
    180  * }
    181  * </pre>
    182  *
    183  * <h4>The {@code @Sharable} annotation</h4>
    184  * <p>
    185  * In the examples above which used an attachment or a {@link ChannelLocal},
    186  * you might have noticed the {@code @Sharable} annotation.
    187  * <p>
    188  * If a {@link ChannelHandler} is annotated with the {@code @Sharable}
    189  * annotation, it means you can create an instance of the handler just once and
    190  * add it to one or more {@link ChannelPipeline}s multiple times without
    191  * a race condition.
    192  * <p>
    193  * If this annotation is not specified, you have to create a new handler
    194  * instance every time you add it to a pipeline because it has unshared state
    195  * such as member variables.
    196  * <p>
    197  * This annotation is provided for documentation purpose, just like
    198  * <a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>.
    199  *
    200  * <h3>Additional resources worth reading</h3>
    201  * <p>
    202  * Please refer to the {@link ChannelEvent} and {@link ChannelPipeline} to find
    203  * out what a upstream event and a downstream event are, what fundamental
    204  * differences they have, and how they flow in a pipeline.
    205  *
    206  * @apiviz.landmark
    207  * @apiviz.exclude ^org\.jboss\.netty\.handler\..*$
    208  */
    209 public interface ChannelHandler {
    210
    211     /**
    212      * Indicates that the same instance of the annotated {@link ChannelHandler}
    213      * can be added to one or more {@link ChannelPipeline}s multiple times
    214      * without a race condition.
    215      * <p>
    216      * If this annotation is not specified, you have to create a new handler
    217      * instance every time you add it to a pipeline because it has unshared
    218      * state such as member variables.
    219      * <p>
    220      * This annotation is provided for documentation purpose, just like
    221      * <a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>.
    222      */
    223     @Inherited
    224     @Documented
    225     @Target(ElementType.TYPE)
    226     @Retention(RetentionPolicy.RUNTIME)
    227     public @interface Sharable {
    228         // no value
    229     }
    230 }