sun.com/blueprints/corej2eepatterns/Patterns/InterceptingFilter.html">Intercepting Filter pattern to give a user full control over how an event is handled and how the {@link ChannelHandler}s in the pipeline interact with each other.
Creation of a pipeline
For each new channel, a new pipeline must be created and attached to the channel. Once attached, the coupling between the channel and the pipeline is permanent; the channel cannot attach another pipeline to it nor detach the current pipeline from it.
The recommended way to create a new pipeline is to use the helper methods in {@link Channels} rather than calling an individual implementation'sconstructor:
import static com.facebook.presto.jdbc.internal.netty.channel. {@link Channels}.*; {@link ChannelPipeline} pipeline = pipeline(); // same with Channels.pipeline()
How an event flows in a pipeline
The following diagram describes how {@link ChannelEvent}s are processed by {@link ChannelHandler}s in a {@link ChannelPipeline} typically.A {@link ChannelEvent} can be handled by either a {@link ChannelUpstreamHandler}or a {@link ChannelDownstreamHandler} and be forwarded to the closesthandler by calling {@link ChannelHandlerContext#sendUpstream(ChannelEvent)}or {@link ChannelHandlerContext#sendDownstream(ChannelEvent)}. The meaning of the event is interpreted somewhat differently depending on whether it is going upstream or going downstream. Please refer to {@link ChannelEvent} formore information.
I/O Request via {@link Channel} or{@link ChannelHandlerContext}| +----------------------------------------+---------------+ | ChannelPipeline | | | \|/ | | +----------------------+ +-----------+------------+ | | | Upstream Handler N | | Downstream Handler 1 | | | +----------+-----------+ +-----------+------------+ | | /|\ | | | | \|/ | | +----------+-----------+ +-----------+------------+ | | | Upstream Handler N-1 | | Downstream Handler 2 | | | +----------+-----------+ +-----------+------------+ | | /|\ . | | . . | | [ sendUpstream() ] [ sendDownstream() ] | | [ + INBOUND data ] [ + OUTBOUND data ] | | . . | | . \|/ | | +----------+-----------+ +-----------+------------+ | | | Upstream Handler 2 | | Downstream Handler M-1 | | | +----------+-----------+ +-----------+------------+ | | /|\ | | | | \|/ | | +----------+-----------+ +-----------+------------+ | | | Upstream Handler 1 | | Downstream Handler M | | | +----------+-----------+ +-----------+------------+ | | /|\ | | +-------------+--------------------------+---------------+ | \|/ +-------------+--------------------------+---------------+ | | | | | [ Socket.read() ] [ Socket.write() ] | | | | Netty Internal I/O Threads (Transport Implementation) | +--------------------------------------------------------+
An upstream event is handled by the upstream handlers in the bottom-up direction as shown on the left side of the diagram. An upstream handler usually handles the inbound data generated by the I/O thread on the bottom of the diagram. The inbound data is often read from a remote peer via the actual input operation such as {@link InputStream#read(byte[])}. If an upstream event goes beyond the top upstream handler, it is discarded silently.
A downstream event is handled by the downstream handler in the top-down direction as shown on the right side of the diagram. A downstream handler usually generates or transforms the outbound traffic such as write requests. If a downstream event goes beyond the bottom downstream handler, it is handled by an I/O thread associated with the {@link Channel}. The I/O thread often performs the actual output operation such as {@link OutputStream#write(byte[])}.
For example, let us assume that we created the following pipeline:
{@link ChannelPipeline} p = {@link Channels}.pipeline(); p.addLast("1", new UpstreamHandlerA()); p.addLast("2", new UpstreamHandlerB()); p.addLast("3", new DownstreamHandlerA()); p.addLast("4", new DownstreamHandlerB()); p.addLast("5", new UpstreamHandlerX());
In the example above, the class whose name starts with {@code Upstream} meansit is an upstream handler. The class whose name starts with {@code Downstream} means it is a downstream handler.
In the given example configuration, the handler evaluation order is 1, 2, 3, 4, 5 when an event goes upstream. When an event goes downstream, the order is 5, 4, 3, 2, 1. On top of this principle, {@link ChannelPipeline} skipsthe evaluation of certain handlers to shorten the stack depth:
- 3 and 4 don't implement {@link ChannelUpstreamHandler}, and therefore the actual evaluation order of an upstream event will be: 1, 2, and 5.
- 1, 2, and 5 don't implement {@link ChannelDownstreamHandler}, and therefore the actual evaluation order of a downstream event will be: 4 and 3.
- If 5 extended {@link SimpleChannelHandler} which implements both{@link ChannelUpstreamHandler} and {@link ChannelDownstreamHandler}, the evaluation order of an upstream and a downstream event could be 125 and 543 respectively.
Building a pipeline
A user is supposed to have one or more {@link ChannelHandler}s in a pipeline to receive I/O events (e.g. read) and to request I/O operations (e.g. write and close). For example, a typical server will have the following handlers in each channel's pipeline, but your mileage may vary depending on the complexity and characteristics of the protocol and business logic:
- Protocol Decoder - translates binary data (e.g. {@link ChannelBuffer}) into a Java object.
- Protocol Encoder - translates a Java object into binary data.
- {@link ExecutionHandler} - applies a thread model.
- Business Logic Handler - performs the actual business logic (e.g. database access).
and it could be represented as shown in the following example:
{@link ChannelPipeline} pipeline = {@link Channels#pipeline() Channels.pipeline()}; pipeline.addLast("decoder", new MyProtocolDecoder()); pipeline.addLast("encoder", new MyProtocolEncoder()); pipeline.addLast("executor", new {@link ExecutionHandler}( new {@link OrderedMemoryAwareThreadPoolExecutor}(16, 1048576, 1048576))); pipeline.addLast("handler", new MyBusinessLogicHandler());
Thread safety
A {@link ChannelHandler} can be added or removed at any time because a{@link ChannelPipeline} is thread safe. For example, you can insert a{@link SslHandler} when sensitive information is about to be exchanged,and remove it after the exchange.
Pitfall
Due to the internal implementation detail of the current default {@link ChannelPipeline}, the following code does not work as expected if FirstHandler is the last handler in the pipeline:
public class FirstHandler extends {@link SimpleChannelUpstreamHandler} {{@code @Override}public void messageReceived( {@link ChannelHandlerContext} ctx, {@link MessageEvent} e) {// Remove this handler from the pipeline, ctx.getPipeline().remove(this); // And let SecondHandler handle the current event. ctx.getPipeline().addLast("2nd", new SecondHandler()); ctx.sendUpstream(e); } }
To implement the expected behavior, you have to add
SecondHandler before the removal or make sure there is at least one more handler between
FirstHandler and
SecondHandler.
@apiviz.landmark
@apiviz.composedOf com.facebook.presto.jdbc.internal.netty.channel.ChannelHandlerContext
@apiviz.owns com.facebook.presto.jdbc.internal.netty.channel.ChannelHandler
@apiviz.uses com.facebook.presto.jdbc.internal.netty.channel.ChannelSink - - sends events downstream