|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: REQUIRED | OPTIONAL | DETAIL: ELEMENT |
@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()); } }
"all"
coverage"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.
Required Element Summary | |
---|---|
String |
value
The value of this annotation |
Element Detail |
---|
public abstract String value
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: REQUIRED | OPTIONAL | DETAIL: ELEMENT |