/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.channel;

import io.netty.util.Attribute;
import io.netty.util.AttributeKey;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

Handles an I/O event or intercepts an I/O operation, and forwards it to its next handler in its ChannelPipeline.

Sub-types

ChannelHandler itself does not provide many methods, but you usually have to implement one of its subtypes:

Alternatively, the following adapter classes are provided for your convenience:

For more information, please refer to the documentation of each subtype.

The context object

A ChannelHandler is provided with a ChannelHandlerContext object. A ChannelHandler is supposed to interact with the ChannelPipeline it belongs to via a context object. Using the context object, the ChannelHandler can pass events upstream or downstream, modify the pipeline dynamically, or store the information (using AttributeKeys) which is specific to the handler.

State management

A ChannelHandler often needs to store some stateful information. The simplest and recommended approach is to use member variables:
public interface Message { // your methods here } public class DataServerHandler extends SimpleChannelInboundHandler<Message> { private boolean loggedIn; @Override public void channelRead0(ChannelHandlerContext ctx, Message message) { Channel ch = e.getChannel(); if (message instanceof LoginMessage) { authenticate((LoginMessage) message); loggedIn = true;
        } else (message instanceof GetDataMessage) {
            if (loggedIn) {
                ch.write(fetchSecret((GetDataMessage) message));
            } else {
                fail();
            }
        }
    }
    ...
}
Because the handler instance has a state variable which is dedicated to one connection, you have to create a new handler instance for each new channel to avoid a race condition where a unauthenticated client can get the confidential information:
// Create a new handler instance per channel. // See ChannelInitializer.initChannel(Channel). public class DataServerInitializer extends ChannelInitializer<Channel> { @Override public void initChannel(Channel channel) { channel.pipeline().addLast("handler", new DataServerHandler());
    }
}

Using AttributeKeys

Although it's recommended to use member variables to store the state of a handler, for some reason you might not want to create many handler instances. In such a case, you can use AttributeKeys which is provided by ChannelHandlerContext:
public interface Message { // your methods here } @Sharable public class DataServerHandler extends SimpleChannelInboundHandler<Message> { private final AttributeKey<Boolean> auth = AttributeKey.valueOf("auth"); @Override public void channelRead(ChannelHandlerContext ctx, Message message) { Attribute<Boolean> attr = ctx.attr(auth); Channel ch = ctx.channel(); if (message instanceof LoginMessage) { authenticate((LoginMessage) o); attr.set(true);
        } else (message instanceof GetDataMessage) {
            if (Boolean.TRUE.equals(attr.get())) {
                ch.write(fetchSecret((GetDataMessage) o));
            } else {
                fail();
            }
        }
    }
    ...
}
Now that the state of the handler is attached to the ChannelHandlerContext, you can add the same handler instance to different pipelines:
public class DataServerInitializer extends ChannelInitializer<Channel> { private static final DataServerHandler SHARED = new DataServerHandler(); @Override public void initChannel(Channel channel) { channel.pipeline().addLast("handler", SHARED);
    }
}

The @Sharable annotation

In the example above which used an AttributeKey, you might have noticed the @Sharable annotation.

If a ChannelHandler is annotated with the @Sharable annotation, it means you can create an instance of the handler just once and add it to one or more ChannelPipelines multiple times without a race condition.

If this annotation is not specified, you have to create a new handler instance every time you add it to a pipeline because it has unshared state such as member variables.

This annotation is provided for documentation purpose, just like the JCIP annotations.

Additional resources worth reading

Please refer to the ChannelHandler, and ChannelPipeline to find out more about inbound and outbound operations, what fundamental differences they have, how they flow in a pipeline, and how to handle the operation in your application.

/** * Handles an I/O event or intercepts an I/O operation, and forwards it to its next handler in * its {@link ChannelPipeline}. * * <h3>Sub-types</h3> * <p> * {@link ChannelHandler} itself does not provide many methods, but you usually have to implement one of its subtypes: * <ul> * <li>{@link ChannelInboundHandler} to handle inbound I/O events, and</li> * <li>{@link ChannelOutboundHandler} to handle outbound I/O operations.</li> * </ul> * </p> * <p> * Alternatively, the following adapter classes are provided for your convenience: * <ul> * <li>{@link ChannelInboundHandlerAdapter} to handle inbound I/O events,</li> * <li>{@link ChannelOutboundHandlerAdapter} to handle outbound I/O operations, and</li> * <li>{@link ChannelDuplexHandler} to handle both inbound and outbound events</li> * </ul> * </p> * <p> * For more information, please refer to the documentation of each subtype. * </p> * * <h3>The context object</h3> * <p> * A {@link ChannelHandler} is provided with a {@link ChannelHandlerContext} * object. A {@link ChannelHandler} is supposed to interact with the * {@link ChannelPipeline} it belongs to via a context object. Using the * context object, the {@link ChannelHandler} can pass events upstream or * downstream, modify the pipeline dynamically, or store the information * (using {@link AttributeKey}s) which is specific to the handler. * * <h3>State management</h3> * * A {@link ChannelHandler} often needs to store some stateful information. * The simplest and recommended approach is to use member variables: * <pre> * public interface Message { * // your methods here * } * * public class DataServerHandler extends {@link SimpleChannelInboundHandler}&lt;Message&gt; { * * <b>private boolean loggedIn;</b> * * {@code @Override} * public void channelRead0({@link ChannelHandlerContext} ctx, Message message) { * {@link Channel} ch = e.getChannel(); * if (message instanceof LoginMessage) { * authenticate((LoginMessage) message); * <b>loggedIn = true;</b> * } else (message instanceof GetDataMessage) { * if (<b>loggedIn</b>) { * ch.write(fetchSecret((GetDataMessage) message)); * } else { * fail(); * } * } * } * ... * } * </pre> * Because the handler instance has a state variable which is dedicated to * one connection, you have to create a new handler instance for each new * channel to avoid a race condition where a unauthenticated client can get * the confidential information: * <pre> * // Create a new handler instance per channel. * // See {@link ChannelInitializer#initChannel(Channel)}. * public class DataServerInitializer extends {@link ChannelInitializer}&lt;{@link Channel}&gt; { * {@code @Override} * public void initChannel({@link Channel} channel) { * channel.pipeline().addLast("handler", <b>new DataServerHandler()</b>); * } * } * * </pre> * * <h4>Using {@link AttributeKey}s</h4> * * Although it's recommended to use member variables to store the state of a * handler, for some reason you might not want to create many handler instances. * In such a case, you can use {@link AttributeKey}s which is provided by * {@link ChannelHandlerContext}: * <pre> * public interface Message { * // your methods here * } * * {@code @Sharable} * public class DataServerHandler extends {@link SimpleChannelInboundHandler}&lt;Message&gt; { * private final {@link AttributeKey}&lt;{@link Boolean}&gt; auth = * {@link AttributeKey#valueOf(String) AttributeKey.valueOf("auth")}; * * {@code @Override} * public void channelRead({@link ChannelHandlerContext} ctx, Message message) { * {@link Attribute}&lt;{@link Boolean}&gt; attr = ctx.attr(auth); * {@link Channel} ch = ctx.channel(); * if (message instanceof LoginMessage) { * authenticate((LoginMessage) o); * <b>attr.set(true)</b>; * } else (message instanceof GetDataMessage) { * if (<b>Boolean.TRUE.equals(attr.get())</b>) { * ch.write(fetchSecret((GetDataMessage) o)); * } else { * fail(); * } * } * } * ... * } * </pre> * Now that the state of the handler is attached to the {@link ChannelHandlerContext}, you can add the * same handler instance to different pipelines: * <pre> * public class DataServerInitializer extends {@link ChannelInitializer}&lt;{@link Channel}&gt; { * * private static final DataServerHandler <b>SHARED</b> = new DataServerHandler(); * * {@code @Override} * public void initChannel({@link Channel} channel) { * channel.pipeline().addLast("handler", <b>SHARED</b>); * } * } * </pre> * * * <h4>The {@code @Sharable} annotation</h4> * <p> * In the example above which used an {@link AttributeKey}, * you might have noticed the {@code @Sharable} annotation. * <p> * If a {@link ChannelHandler} is annotated with the {@code @Sharable} * annotation, it means you can create an instance of the handler just once and * add it to one or more {@link ChannelPipeline}s multiple times without * a race condition. * <p> * If this annotation is not specified, you have to create a new handler * instance every time you add it to a pipeline because it has unshared state * such as member variables. * <p> * This annotation is provided for documentation purpose, just like * <a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>. * * <h3>Additional resources worth reading</h3> * <p> * Please refer to the {@link ChannelHandler}, and * {@link ChannelPipeline} to find out more about inbound and outbound operations, * what fundamental differences they have, how they flow in a pipeline, and how to handle * the operation in your application. */
public interface ChannelHandler {
Gets called after the ChannelHandler was added to the actual context and it's ready to handle events.
/** * Gets called after the {@link ChannelHandler} was added to the actual context and it's ready to handle events. */
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
Gets called after the ChannelHandler was removed from the actual context and it doesn't handle events anymore.
/** * Gets called after the {@link ChannelHandler} was removed from the actual context and it doesn't handle events * anymore. */
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
Gets called if a Throwable was thrown.
Deprecated:is part of ChannelInboundHandler
/** * Gets called if a {@link Throwable} was thrown. * * @deprecated is part of {@link ChannelInboundHandler} */
@Deprecated void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
Indicates that the same instance of the annotated ChannelHandler can be added to one or more ChannelPipelines multiple times without a race condition.

If this annotation is not specified, you have to create a new handler instance every time you add it to a pipeline because it has unshared state such as member variables.

This annotation is provided for documentation purpose, just like the JCIP annotations.

/** * Indicates that the same instance of the annotated {@link ChannelHandler} * can be added to one or more {@link ChannelPipeline}s multiple times * without a race condition. * <p> * If this annotation is not specified, you have to create a new handler * instance every time you add it to a pipeline because it has unshared * state such as member variables. * <p> * This annotation is provided for documentation purpose, just like * <a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>. */
@Inherited @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Sharable { // no value } }