Added JavaDoc for handler.codec.replay
This commit is contained in:
parent
f744de9d85
commit
35678cdf73
|
@ -35,13 +35,151 @@ import org.jboss.netty.channel.Channels;
|
||||||
import org.jboss.netty.channel.ExceptionEvent;
|
import org.jboss.netty.channel.ExceptionEvent;
|
||||||
import org.jboss.netty.channel.MessageEvent;
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||||
|
import org.jboss.netty.handler.codec.frame.FrameDecoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A specialized variation of {@link FrameDecoder} which enables implementation
|
||||||
|
* of a non-blocking decoder in the blocking I/O paradigm.
|
||||||
|
* <p>
|
||||||
|
* The biggest difference between {@link ReplayingDecoder} and
|
||||||
|
* {@link FrameDecoder} is that {@link ReplayingDecoder} allows you to
|
||||||
|
* implement the {@code decode()} and {@code decodeLast()} methods just like
|
||||||
|
* all required bytes were received already, rather than checking the
|
||||||
|
* availability of the required bytes. For example, the following
|
||||||
|
* {@link FrameDecoder} implementation:
|
||||||
|
* <pre>
|
||||||
|
* public class IntegerHeaderFrameDecoder extends FrameDecoder {
|
||||||
|
*
|
||||||
|
* protected Object decode(ChannelHandlerContext ctx,
|
||||||
|
* Channel channel,
|
||||||
|
* ChannelBuffer buf) throws Exception {
|
||||||
|
*
|
||||||
|
* if (buf.readableBytes() < 4) {
|
||||||
|
* return <strong>null</strong>;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* buf.markReaderIndex();
|
||||||
|
* int length = buf.readInt();
|
||||||
|
*
|
||||||
|
* if (buf.readableBytes() < length) {
|
||||||
|
* buf.resetReaderIndex();
|
||||||
|
* return <strong>null</strong>;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* return buf.readBytes(length);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* is simplified like the following with {@link ReplayingDecoder}:
|
||||||
|
* <pre>
|
||||||
|
* public class IntegerHeaderFrameDecoder
|
||||||
|
* extends ReplayingDecoder<VoidEnum> {
|
||||||
|
*
|
||||||
|
* protected Object decode(ChannelHandlerContext ctx,
|
||||||
|
* Channel channel,
|
||||||
|
* ChannelBuffer buf,
|
||||||
|
* VoidEnum state) throws Exception {
|
||||||
|
*
|
||||||
|
* return buf.readBytes(buf.readInt());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h3>Limitations</h3>
|
||||||
|
* <p>
|
||||||
|
* At the cost of the simplicity, {@link ReplayingDecoder} enforces you a few
|
||||||
|
* limitations:
|
||||||
|
* <ul>
|
||||||
|
* <li>Some buffer operations are prohibited.</li>
|
||||||
|
* <li>Performance can be worse if the network is slow and the message
|
||||||
|
* format is complicated unlike the example above.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h3>Improving the performance</h3>
|
||||||
|
* <p>
|
||||||
|
* Fortunately, the performance of a {@link ReplayingDecoder} implementation
|
||||||
|
* can be improved significantly by using the {@code checkpoint()} method.
|
||||||
|
*
|
||||||
|
* <h4>Calling {@code checkpoint(T)} with an {@link Enum}</h4>
|
||||||
|
* <p>
|
||||||
|
* The easiest way is to create an {@link Enum} type which represents the
|
||||||
|
* current state of the decoder and to call {@code checkpoint(T)} method
|
||||||
|
* whenever the state changes. You can have as many states as you want
|
||||||
|
* depending on the complexity of the message:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public enum MyDecoderState {
|
||||||
|
* READ_LENGTH,
|
||||||
|
* READ_CONTENT;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public class IntegerHeaderFrameDecoder
|
||||||
|
* extends ReplayingDecoder<<strong>MyDecoderState</strong>> {
|
||||||
|
*
|
||||||
|
* private int length;
|
||||||
|
*
|
||||||
|
* public IntegerHeaderFrameDecoder() {
|
||||||
|
* // Set the initial state.
|
||||||
|
* <strong>super(MyDecoderState.READ_LENGTH);</strong>
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* protected Object decode(ChannelHandlerContext ctx,
|
||||||
|
* Channel channel,
|
||||||
|
* ChannelBuffer buf,
|
||||||
|
* MyDecoderState state) throws Exception {
|
||||||
|
* switch (state) {
|
||||||
|
* case READ_LENGTH:
|
||||||
|
* length = buf.readInt();
|
||||||
|
* <strong>checkpoint(MyDecoderState.READ_CONTENT);</strong>
|
||||||
|
* case READ_CONTENT:
|
||||||
|
* ChannelBuffer frame = buf.readBytes(length);
|
||||||
|
* <strong>checkpoint(MyDecoderState.READ_LENGTH);</strong>
|
||||||
|
* return frame;
|
||||||
|
* default:
|
||||||
|
* throw new Error("Shouldn't reach here.");
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h4>Calling {@code checkpoint()} with no parameter</h4>
|
||||||
|
* <p>
|
||||||
|
* An alternative way to manage the decoder state is to manage it by yourself.
|
||||||
|
* <pre>
|
||||||
|
* public class IntegerHeaderFrameDecoder
|
||||||
|
* extends ReplayingDecoder<<strong>VoidEnum</strong>> {
|
||||||
|
*
|
||||||
|
* <strong>private boolean readLength;</strong>
|
||||||
|
* private int length;
|
||||||
|
*
|
||||||
|
* protected Object decode(ChannelHandlerContext ctx,
|
||||||
|
* Channel channel,
|
||||||
|
* ChannelBuffer buf,
|
||||||
|
* MyDecoderState state) throws Exception {
|
||||||
|
* if (!readLength) {
|
||||||
|
* length = buf.readInt();
|
||||||
|
* <strong>readLength = true;</strong>
|
||||||
|
* <strong>checkpoint();</strong>
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* if (readLength) {
|
||||||
|
* ChannelBuffer frame = buf.readBytes(length);
|
||||||
|
* <strong>readLength = false;</strong>
|
||||||
|
* <strong>checkpoint();</strong>
|
||||||
|
* return frame;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||||
* @author Trustin Lee (tlee@redhat.com)
|
* @author Trustin Lee (tlee@redhat.com)
|
||||||
*
|
*
|
||||||
* @version $Rev$, $Date$
|
* @version $Rev$, $Date$
|
||||||
*
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the state type; use {@link VoidEnum} if state management is unused
|
||||||
*/
|
*/
|
||||||
@ChannelPipelineCoverage("one")
|
@ChannelPipelineCoverage("one")
|
||||||
public abstract class ReplayingDecoder<T extends Enum<T>> extends SimpleChannelHandler {
|
public abstract class ReplayingDecoder<T extends Enum<T>> extends SimpleChannelHandler {
|
||||||
|
@ -51,14 +189,65 @@ public abstract class ReplayingDecoder<T extends Enum<T>> extends SimpleChannelH
|
||||||
private volatile T state;
|
private volatile T state;
|
||||||
private volatile int checkpoint;
|
private volatile int checkpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance with no initial state (i.e. {@code null}).
|
||||||
|
*/
|
||||||
protected ReplayingDecoder() {
|
protected ReplayingDecoder() {
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance with the specified initial state.
|
||||||
|
*/
|
||||||
protected ReplayingDecoder(T initialState) {
|
protected ReplayingDecoder(T initialState) {
|
||||||
this.state = initialState;
|
this.state = initialState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the internal cumulative buffer's reader position.
|
||||||
|
*/
|
||||||
|
protected void checkpoint() {
|
||||||
|
checkpoint = cumulation.readerIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the internal cumulative buffer's reader position and updates
|
||||||
|
* the current decoder state.
|
||||||
|
*/
|
||||||
|
protected void checkpoint(T state) {
|
||||||
|
this.state = state;
|
||||||
|
checkpoint = cumulation.readerIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the received packets so far into a frame.
|
||||||
|
*
|
||||||
|
* @param ctx the context of this handler
|
||||||
|
* @param channel the current channel
|
||||||
|
* @param buffer the cumulative buffer of received packets so far\
|
||||||
|
* @param state the current decoder state ({@code null} if unused)
|
||||||
|
*
|
||||||
|
* @return the decoded frame
|
||||||
|
*/
|
||||||
|
protected abstract Object decode(ChannelHandlerContext ctx,
|
||||||
|
Channel channel, ChannelBuffer buffer, T state) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the received data so far into a frame when the channel is
|
||||||
|
* disconnected.
|
||||||
|
*
|
||||||
|
* @param ctx the context of this handler
|
||||||
|
* @param channel the current channel
|
||||||
|
* @param buffer the cumulative buffer of received packets so far
|
||||||
|
* @param state the current decoder state ({@code null} if unused)
|
||||||
|
*
|
||||||
|
* @return the decoded frame
|
||||||
|
*/
|
||||||
|
protected Object decodeLast(
|
||||||
|
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, T state) throws Exception {
|
||||||
|
return decode(ctx, channel, buffer, state);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
@ -155,21 +344,4 @@ public abstract class ReplayingDecoder<T extends Enum<T>> extends SimpleChannelH
|
||||||
ctx.sendUpstream(e);
|
ctx.sendUpstream(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void checkpoint() {
|
|
||||||
checkpoint = cumulation.readerIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void checkpoint(T state) {
|
|
||||||
this.state = state;
|
|
||||||
checkpoint = cumulation.readerIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Object decode(ChannelHandlerContext ctx,
|
|
||||||
Channel channel, ChannelBuffer buffer, T state) throws Exception;
|
|
||||||
|
|
||||||
protected Object decodeLast(
|
|
||||||
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, T state) throws Exception {
|
|
||||||
return decode(ctx, channel, buffer, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
package org.jboss.netty.handler.codec.replay;
|
package org.jboss.netty.handler.codec.replay;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A placeholder {@link Enum} which could be specified as a type parameter of
|
||||||
|
* {@link ReplayingDecoder} when a user wants to manage the decoder state by
|
||||||
|
* oneself or there's no state to manage.
|
||||||
|
*
|
||||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||||
* @author Trustin Lee (tlee@redhat.com)
|
* @author Trustin Lee (tlee@redhat.com)
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specialized decoder which enables implementation of a non-blocking decoder
|
* Specialized variation of {@link org.jboss.netty.handler.codec.frame.FrameDecoder}
|
||||||
* with blocking I/O paradigm.
|
* which enables implementation of a non-blocking decoder in the blocking I/O
|
||||||
|
* paradigm.
|
||||||
*/
|
*/
|
||||||
package org.jboss.netty.handler.codec.replay;
|
package org.jboss.netty.handler.codec.replay;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user