From 35678cdf7337f5d240a864f2170b25398fd4c291 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Thu, 4 Sep 2008 06:35:06 +0000 Subject: [PATCH] Added JavaDoc for handler.codec.replay --- .../codec/replay/ReplayingDecoder.java | 206 ++++++++++++++++-- .../netty/handler/codec/replay/VoidEnum.java | 4 + .../handler/codec/replay/package-info.java | 8 +- 3 files changed, 198 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/jboss/netty/handler/codec/replay/ReplayingDecoder.java b/src/main/java/org/jboss/netty/handler/codec/replay/ReplayingDecoder.java index ddbdddcc3d..0f95965d0c 100644 --- a/src/main/java/org/jboss/netty/handler/codec/replay/ReplayingDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/replay/ReplayingDecoder.java @@ -35,13 +35,151 @@ import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; 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. + *

+ * 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: + *

+ * public class IntegerHeaderFrameDecoder extends FrameDecoder {
+ *
+ *   protected Object decode(ChannelHandlerContext ctx,
+ *                           Channel channel,
+ *                           ChannelBuffer buf) throws Exception {
+ *
+ *     if (buf.readableBytes() < 4) {
+ *        return null;
+ *     }
+ *
+ *     buf.markReaderIndex();
+ *     int length = buf.readInt();
+ *
+ *     if (buf.readableBytes() < length) {
+ *        buf.resetReaderIndex();
+ *        return null;
+ *     }
+ *
+ *     return buf.readBytes(length);
+ *   }
+ * }
+ * 
+ * is simplified like the following with {@link ReplayingDecoder}: + *
+ * public class IntegerHeaderFrameDecoder
+ *      extends ReplayingDecoder<VoidEnum> {
+ *
+ *   protected Object decode(ChannelHandlerContext ctx,
+ *                           Channel channel,
+ *                           ChannelBuffer buf,
+ *                           VoidEnum state) throws Exception {
+ *
+ *     return buf.readBytes(buf.readInt());
+ *   }
+ * }
+ * 
+ * + *

Limitations

+ *

+ * At the cost of the simplicity, {@link ReplayingDecoder} enforces you a few + * limitations: + *

+ * + *

Improving the performance

+ *

+ * Fortunately, the performance of a {@link ReplayingDecoder} implementation + * can be improved significantly by using the {@code checkpoint()} method. + * + *

Calling {@code checkpoint(T)} with an {@link Enum}

+ *

+ * 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: + * + *

+ * public enum MyDecoderState {
+ *   READ_LENGTH,
+ *   READ_CONTENT;
+ * }
+ *
+ * public class IntegerHeaderFrameDecoder
+ *      extends ReplayingDecoder<MyDecoderState> {
+ *
+ *   private int length;
+ *
+ *   public IntegerHeaderFrameDecoder() {
+ *     // Set the initial state.
+ *     super(MyDecoderState.READ_LENGTH);
+ *   }
+ *
+ *   protected Object decode(ChannelHandlerContext ctx,
+ *                           Channel channel,
+ *                           ChannelBuffer buf,
+ *                           MyDecoderState state) throws Exception {
+ *     switch (state) {
+ *     case READ_LENGTH:
+ *       length = buf.readInt();
+ *       checkpoint(MyDecoderState.READ_CONTENT);
+ *     case READ_CONTENT:
+ *       ChannelBuffer frame = buf.readBytes(length);
+ *       checkpoint(MyDecoderState.READ_LENGTH);
+ *       return frame;
+ *     default:
+ *       throw new Error("Shouldn't reach here.");
+ *     }
+ *   }
+ * }
+ * 
+ * + *

Calling {@code checkpoint()} with no parameter

+ *

+ * An alternative way to manage the decoder state is to manage it by yourself. + *

+ * public class IntegerHeaderFrameDecoder
+ *      extends ReplayingDecoder<VoidEnum> {
+ *
+ *   private boolean readLength;
+ *   private int length;
+ *
+ *   protected Object decode(ChannelHandlerContext ctx,
+ *                           Channel channel,
+ *                           ChannelBuffer buf,
+ *                           MyDecoderState state) throws Exception {
+ *     if (!readLength) {
+ *       length = buf.readInt();
+ *       readLength = true;
+ *       checkpoint();
+ *     }
+ *
+ *     if (readLength) {
+ *       ChannelBuffer frame = buf.readBytes(length);
+ *       readLength = false;
+ *       checkpoint();
+ *       return frame;
+ *     }
+ *   }
+ * }
+ * 
+ * + * * @author The Netty Project (netty-dev@lists.jboss.org) * @author Trustin Lee (tlee@redhat.com) * * @version $Rev$, $Date$ * + * @param + * the state type; use {@link VoidEnum} if state management is unused */ @ChannelPipelineCoverage("one") public abstract class ReplayingDecoder> extends SimpleChannelHandler { @@ -51,14 +189,65 @@ public abstract class ReplayingDecoder> extends SimpleChannelH private volatile T state; private volatile int checkpoint; + /** + * Creates a new instance with no initial state (i.e. {@code null}). + */ protected ReplayingDecoder() { this(null); } + /** + * Creates a new instance with the specified initial state. + */ protected ReplayingDecoder(T 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 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { @@ -155,21 +344,4 @@ public abstract class ReplayingDecoder> extends SimpleChannelH 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); - } } diff --git a/src/main/java/org/jboss/netty/handler/codec/replay/VoidEnum.java b/src/main/java/org/jboss/netty/handler/codec/replay/VoidEnum.java index 83b490cdc6..05ba2c4664 100644 --- a/src/main/java/org/jboss/netty/handler/codec/replay/VoidEnum.java +++ b/src/main/java/org/jboss/netty/handler/codec/replay/VoidEnum.java @@ -23,6 +23,10 @@ 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 Trustin Lee (tlee@redhat.com) * diff --git a/src/main/java/org/jboss/netty/handler/codec/replay/package-info.java b/src/main/java/org/jboss/netty/handler/codec/replay/package-info.java index 0b010915ae..2ac7ddeb1d 100644 --- a/src/main/java/org/jboss/netty/handler/codec/replay/package-info.java +++ b/src/main/java/org/jboss/netty/handler/codec/replay/package-info.java @@ -22,7 +22,9 @@ */ /** - * Specialized decoder which enables implementation of a non-blocking decoder - * with blocking I/O paradigm. + * Specialized variation of {@link org.jboss.netty.handler.codec.frame.FrameDecoder} + * which enables implementation of a non-blocking decoder in the blocking I/O + * paradigm. */ -package org.jboss.netty.handler.codec.replay; \ No newline at end of file +package org.jboss.netty.handler.codec.replay; +