/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel;
import io.netty.buffer.ChannelBuffer;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
/**
* A list of {@link ChannelHandler}s which handles or intercepts
* {@link ChannelEvent}s of a {@link Channel}. {@link ChannelPipeline}
* implements an advanced form of the
* 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's
* constructor:
*
* import static io.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 closest
* handler 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} for
* more information.
*
* 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} means
* it 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} skips
* the 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.
*
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 ExecutionHandler(...));
* 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 an
* encryption handler 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 io.netty.channel.ChannelHandlerContext
* @apiviz.owns io.netty.channel.ChannelHandler
* @apiviz.uses io.netty.channel.ChannelSink - - sends events downstream
*/
public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundInvoker {
Queue