org.jboss.netty.channel
Annotation Type ChannelPipelineCoverage


@Inherited
@Documented
@Target(value=TYPE)
@Retention(value=RUNTIME)
public @interface ChannelPipelineCoverage

Specifies if the same instance of the annotated ChannelHandler type can be added to more than one ChannelPipeline.

All handler types are expected to specify this annotation. Otherwise you will be warned in runtime. Only two values are allowed for this annotation: "all" and "one".

Please note that this annotation does not prevent a handler annotated with the value "one" from being added to more than one pipeline. This annotation is used for documentation purpose only.

ChannelPipelineCoverage("all")

"all" means you can add the same instance of the annotated handler type to more than one ChannelPipeline. It means the member variables of the handler instance is shared among multiple channels and it is designed to be OK to do so (or there's nothing to share.) The following code shows an example of a handler type annotated with "all" value:
 public class StatelessHandler extends SimpleChannelHandler {

     // No state properties - you are safe to add the same instance to
     //                       multiple pipelines.

     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         // Prepend a length field to the message.
         ChannelBuffer body = (ChannelBuffer) e.getMessage();
         ChannelBuffer header = ChannelBuffers.buffer(4);
         header.writeInt(body.readableBytes());

         // Create a message prepended with the header and send a new event.
         ChannelBuffer message = ChannelBuffers.wrappedBuffer(header, body);
         Channels.fireMessageReceived(ctx, message, e.getRemoteAddress());
     }
     ...
 }
 
Please note that the handler annotated with "all" can even be added to the same pipeline more than once:
 ChannelPipeline p = ...;
 StatelessHandler h = ...;
 p.addLast("handler1", h);
 p.addLast("handler2", h);
 

ChannelPipelineCoverage("one")

"one" means you must create a new instance of the annotated handler type for each new channel. It means the member variables of the handler instance can not be shared at all, and violating this contract will lead the handler to a race condition. A new handler instance is usually created by ChannelPipelineFactory. The following code shows an example of a handler type annotated with "one" value:
 public class StatefulHandler extends SimpleChannelHandler {

     // Stateful property - adding the same instance to multiple pipelines
     //                     can lead to a race condition.
     private int messageId;

     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         // Prepend a message ID and length field to the message.
         ChannelBuffer body = (ChannelBuffer) e.getMessage();
         ChannelBuffer header = ChannelBuffers.buffer(8);
         header.writeInt(messageId);
         header.writeInt(body.readableBytes());

         // Update the stateful property.
         messageId ++;

         // Create a message prepended with the header and send a new event.
         ChannelBuffer message = ChannelBuffers.wrappedBuffer(header, body);
         Channels.fireMessageReceived(ctx, message, e.getRemoteAddress());
     }
     ...
 }

 // Create a new handler instance per channel.
 public class MyPipelineFactory implements ChannelPipelineFactory {

     public ChannelPipeline getPipeline() {
         ChannelPipeline p = Channels.pipeline();
         p.addLast("handler", new StatefulHandler());
     }
 }
 

Writing a stateful handler with the "all" coverage

Although it's recommended to write a stateful handler with the "one" coverage, you might sometimes want to write it with the "all" coverage for some reason. In such a case, you need to use either ChannelHandlerContext.attachment property or a ConcurrentMap whose key is ChannelHandlerContext.

The following is an example that uses the context attachment:

 public class StatefulHandler extends SimpleChannelHandler {

     public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
         // Initialize the message ID counter.
         // Please note that the attachment (the counter in this case) will be
         // dereferenced and marked for garbage collection automatically on
         // disconnection.
         ctx.setAttachment(Integer.valueOf(0));
     }

     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         // Fetch the current message ID.
         int messageId = ((Integer) ctx.getAttachment()).intValue();

         // Prepend a message ID and length field to the message.
         ChannelBuffer body = (ChannelBuffer) e.getMessage();
         ChannelBuffer header = ChannelBuffers.buffer(8);
         header.writeInt(messageId);
         header.writeInt(body.readableBytes());

         // Update the stateful property.
         ctx.setAttachment(Integer.valueOf(messageId + 1));

         // Create a message prepended with the header and send a new event.
         ChannelBuffer message = ChannelBuffers.wrappedBuffer(header, body);
         Channels.fireMessageReceived(ctx, message, e.getRemoteAddress());
     }
     ...
 }
 
and here's another example that uses a map:
 public class StatefulHandler extends SimpleChannelHandler {

     private final ConcurrentMap<ChannelHandlerContext, Integer> messageIds =
             new ConcurrentHashMap<ChannelHandlerContext, Integer>();

     public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
         // Initialize the message ID counter.
         messageIds.put(ctx, Integer.valueOf(0));
     }

     public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) {
         // Remove the message ID counter from the map.
         // Please note that the context attachment does not need this step.
         messageIds.remove(ctx);
     }

     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         // Fetch the current message ID.
         int messageId = messageIds.get(ctx).intValue();

         // Prepend a message ID and length field to the message.
         ChannelBuffer body = (ChannelBuffer) e.getMessage();
         ChannelBuffer header = ChannelBuffers.buffer(8);
         header.writeInt(messageId);
         header.writeInt(body.readableBytes());

         // Update the stateful property.
         messageIds.put(ctx, Integer.valueOf(messageId + 1));

         // Create a message prepended with the header and send a new event.
         ChannelBuffer message = ChannelBuffers.wrappedBuffer(header, body);
         Channels.fireMessageReceived(ctx, message, e.getRemoteAddress());
     }
     ...
 }
 
Please note that the examples above in this section assume that the handlers are added before the channelOpen event and removed after the channelClosed event. The initialization and removal of the message ID property could have been more complicated otherwise.

Version:
$Rev: 1726 $, $Date: 2009-09-10 13:25:05 +0900 (목, 10 9 2009) $
Author:
The Netty Project (netty-dev@lists.jboss.org), Trustin Lee (tlee@redhat.com)

Required Element Summary
 String value
          The value of this annotation
 

Element Detail

value

public abstract String value
The value of this annotation



Copyright © 2008-2009 JBoss, by Red Hat. All Rights Reserved.