{@code @Sharable}public class DataServerHandler extends {@link SimpleChannelHandler} {{@code @Override}public void messageReceived( {@link ChannelHandlerContext} ctx, {@link MessageEvent} e) {{@link 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 {@link ChannelPipelineFactory} {private static final DataServerHandler SHARED = new DataServerHandler(); public {@link ChannelPipeline} getPipeline() {return {@link Channels}.pipeline(SHARED); } }
public final class DataServerState { public static final {@link ChannelLocal}<Boolean> loggedIn = new {@link ChannelLocal}<>() { protected Boolean initialValue(Channel channel) { return false; } } ... } {@code @Sharable}public class DataServerHandler extends {@link SimpleChannelHandler} {{@code @Override}public void messageReceived( {@link ChannelHandlerContext} ctx, {@link 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: {@link ChannelGroup} allClientChannels = ...;for ( {@link Channel} ch: allClientChannels) {if (DataServerState.loggedIn.get(ch)) { System.out.println(ch.getRemoteAddress()); } }
In the examples above which used an attachment or a {@link ChannelLocal}, you might have noticed the {@code @Sharable} annotation.
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.
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.
Please refer to the {@link ChannelEvent} and {@link ChannelPipeline} to findout what a upstream event and a downstream event are, what fundamental differences they have, and how they flow in a pipeline. @apiviz.landmark @apiviz.exclude ^org\.jboss\.netty\.handler\..*$
// Create a new handler instance per channel. // See {@link ChannelInitializer#initChannel(Channel)}. public class DataServerInitializer extends {@link ChannelInitializer}< {@link Channel}> { {@code @Override}public void initChannel( {@link Channel} channel) {channel.pipeline().addLast("handler", new DataServerHandler()); } }
public interface Message { // your methods here } {@code @Sharable}public class DataServerHandler extends {@link ChannelInboundMessageHandlerAdapter}<Message> { private final {@link AttributeKey}< {@link Boolean}> auth = new {@link AttributeKey}< {@link Boolean}>("auth"); // This handler will receive a sequence of increasing integers starting // from 1. {@code @Override}public void messageReceived( {@link ChannelHandlerContext} ctx, {@link Integer} integer) {{@link Attribute}< {@link Boolean}> attr = ctx.getAttr(auth); {@code @Override}public void messageReceived( {@link ChannelHandlerContext} ctx, Message message) {{@link 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 stored as an attachment, you can add the same handler instance to different pipelines:
public class DataServerInitializer extends {@link ChannelInitializer}< {@link Channel}> { private static final DataServerHandler SHARED = new DataServerHandler(); {@code @Override}public void initChannel( {@link Channel} channel) {channel.pipeline().addLast("handler", SHARED); } }
In the examples above which used an attachment, you might have noticed the {@code @Sharable} annotation.
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.
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.
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.
For the sake of simplicity the interface contains methods for sending and receiving of messages but each separate instance if ChannelHandler
is only required to perform either sending or receiving of messages depending on which side of a virtual channel it is handling.
It is guaranteed that for sending side the receiving methods will not be called and vice versa. In case there are separate implementations of ChannelHandler
for sending and receiving sides unimplemented methods should throw {@link UnsupportedOperationException}.
ChannelHandler
must act independently of other instances. That is: ChannelHandler
will be accessed by one thread at a time with the exception of stop()
method. It may (and usually will) be called concurrently by another thread. All non-constructor methods except stop()
will normally be accessed only by one thread throughout the lifetime of a ChannelHandler
instance. If the message has been successfully forwarded then {@link #commit}method is called signifying that the message has been successfully received and can be removed from transmission queue. If the message forwarding failed for some reason (communication, processing or other reason) {@link #rollback}method is called signifying that the message must be returned to the transmission queue and its delivery must be reattempted.
stop()
is called while the channel and respective ports are still operational. The second stage matches that of the sending endpoint. When close()
method is called the channel handler should release any resources allocated to this communication channel. If a transaction is still active during the closing of channel it must be rolled back automatically.
@author Baltic Amadeus, JSC
@author Antanas Kompanas
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|