org.jboss.netty.channel
Interface ChannelHandler

All Known Subinterfaces:
ChannelDownstreamHandler, ChannelUpstreamHandler, LifeCycleAwareChannelHandler
All Known Implementing Classes:
Base64Decoder, Base64Encoder, BlockingReadHandler, BufferedWriteHandler, ChunkedWriteHandler, CompatibleObjectDecoder, CompatibleObjectEncoder, DelimiterBasedFrameDecoder, ExecutionHandler, FixedLengthFrameDecoder, FrameDecoder, HttpChunkAggregator, HttpClientCodec, HttpContentCompressor, HttpContentDecoder, HttpContentDecompressor, HttpContentEncoder, HttpMessageDecoder, HttpMessageEncoder, HttpRequestDecoder, HttpRequestEncoder, HttpResponseDecoder, HttpResponseEncoder, HttpServerCodec, IdleStateAwareChannelHandler, IdleStateAwareChannelUpstreamHandler, IdleStateHandler, LengthFieldBasedFrameDecoder, LengthFieldPrepender, LoggingHandler, ObjectDecoder, ObjectEncoder, OneToOneDecoder, OneToOneEncoder, ProtobufDecoder, ProtobufEncoder, ProtobufVarint32FrameDecoder, ProtobufVarint32LengthFieldPrepender, ReadTimeoutHandler, ReplayingDecoder, RtspMessageDecoder, RtspMessageEncoder, RtspRequestDecoder, RtspRequestEncoder, RtspResponseDecoder, RtspResponseEncoder, SimpleChannelDownstreamHandler, SimpleChannelHandler, SimpleChannelUpstreamHandler, SslHandler, StringDecoder, StringEncoder, WebSocketFrameDecoder, WebSocketFrameEncoder, WriteTimeoutHandler, ZlibDecoder, ZlibEncoder

public interface ChannelHandler

Handles or intercepts a ChannelEvent, and sends a ChannelEvent to the next handler in a ChannelPipeline.

Sub-types

ChannelHandler itself does not provide any method. To handle a ChannelEvent you need to implement its sub-interfaces. There are two sub-interfaces which handles a received event, one for upstream events and the other for downstream events:

You will also find more detailed explanation from the documentation of each sub-interface on how an event is interpreted when it goes upstream and downstream respectively.

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 (attachment) 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 class DataServerHandler extends SimpleChannelHandler {

     private boolean loggedIn;

     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         Channel ch = e.getChannel();
         Object o = e.getMessage();
         if (o instanceof LoginMessage) {
             authenticate((LoginMessage) o);
             loggedIn = true;
         } else (o instanceof GetDataMessage) {
             if (loggedIn) {
                 ch.write(fetchSecret((GetDataMessage) o));
             } 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 Bootstrap.setPipelineFactory(ChannelPipelineFactory).
 public class DataServerPipelineFactory implements ChannelPipelineFactory {
     public ChannelPipeline getPipeline() {
         return Channels.pipeline(new DataServerHandler());
     }
 }
 

Using an attachment

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 an attachment which is provided by ChannelHandlerContext:
 @Sharable
 public class DataServerHandler extends SimpleChannelHandler {

     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         Channel ch = e.getChannel();
         Object o = e.getMessage();
         if (o instanceof LoginMessage) {
             authenticate((LoginMessage) o);
             ctx.setAttachment(true);
         } else (o instanceof GetDataMessage) {
             if (Boolean.TRUE.equals(ctx.getAttachment())) {
                 ch.write(fetchSecret((GetDataMessage) o));
             } else {
                 fail();
             }
         }
     }
     ...
 }
 
Now that the state of the handler is stored as an attachment, you can add the same handler instance to different pipelines:
 public class DataServerPipelineFactory implements ChannelPipelineFactory {

     private static final DataServerHandler SHARED = new DataServerHandler();

     public ChannelPipeline getPipeline() {
         return Channels.pipeline(SHARED);
     }
 }
 

Using a ChannelLocal

If you have a state variable which needs to be accessed either from other handlers or outside handlers, you can use ChannelLocal:
 public final class DataServerState {

     public static final ChannelLocal<Boolean> loggedIn = new ChannelLocal<Boolean>() {
         protected Boolean initialValue(Channel channel) {
             return false;
         }
     }
     ...
 }

 @Sharable
 public class DataServerHandler extends SimpleChannelHandler {

     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         Channel ch = e.getChannel();
         Object o = e.getMessage();
         if (o instanceof LoginMessage) {
             authenticate((LoginMessage) o);
             DataServerState.loggedIn.set(ch, true);
         } else (o instanceof GetDataMessage) {
             if (DataServerState.loggedIn.get(ch)) {
                 ctx.getChannel().write(fetchSecret((GetDataMessage) o));
             } else {
                 fail();
             }
         }
     }
     ...
 }

 // Print the remote addresses of the authenticated clients:
 ChannelGroup allClientChannels = ...;
 for (Channel ch: allClientChannels) {
     if (DataServerState.loggedIn.get(ch)) {
         System.out.println(ch.getRemoteAddress());
     }
 }
 

The @Sharable annotation

In the examples above which used an attachment or a ChannelLocal, 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 ChannelEvent and ChannelPipeline to find out what a upstream event and a downstream event are, what fundamental differences they have, and how they flow in a pipeline.

Version:
$Rev: 2152 $, $Date: 2010-02-17 17:22:45 +0900 (Wed, 17 Feb 2010) $
Author:
The Netty Project, Trustin Lee

Nested Class Summary
static interface ChannelHandler.Sharable
          Indicates that the same instance of the annotated ChannelHandler can be added to one or more ChannelPipelines multiple times without a race condition.
 



Copyright © 2008-2011 JBoss, a division of Red Hat, Inc.. All Rights Reserved.