View Javadoc

    1   /*
    2    * Copyright 2009 Red Hat, Inc.
    3    *
    4    * Red Hat licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the
    6    * License.  You may obtain a copy of the License at:
    7    *
    8    *    http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
    13   * License for the specific language governing permissions and limitations
    14   * under the License.
    15   */
    16  package org.jboss.netty.handler.codec.frame;
    17
    18  import static org.jboss.netty.buffer.ChannelBuffers.*;
    19
    20  import java.nio.ByteOrder;
    21
    22  import org.jboss.netty.buffer.ChannelBuffer;
    23  import org.jboss.netty.buffer.ChannelBufferFactory;
    24  import org.jboss.netty.channel.Channel;
    25  import org.jboss.netty.channel.ChannelHandlerContext;
    26  import org.jboss.netty.channel.ChannelHandler.Sharable;
    27  import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
    28
    29  /**
    30   * An encoder that prepends the length of the message.  The length value is
    31   * prepended as a binary form.  It is encoded in either big endian or little
    32   * endian depending on the default {@link ByteOrder} of the current
    33   * {@link ChannelBufferFactory}.
    34   * <p>
    35   * For example, <tt>{@link LengthFieldPrepender}(2)</tt> will encode the
    36   * following 12-bytes string:
    37   * <pre>
    38   * +----------------+
    39   * | "HELLO, WORLD" |
    40   * +----------------+
    41   * </pre>
    42   * into the following:
    43   * <pre>
    44   * +--------+----------------+
    45   * + 0x000C | "HELLO, WORLD" |
    46   * +--------+----------------+
    47   * </pre>
    48   * If you turned on the {@code lengthIncludesLengthFieldLength} flag in the
    49   * constructor, the encoded data would look like the following
    50   * (12 (original data) + 2 (prepended data) = 14 (0xE)):
    51   * <pre>
    52   * +--------+----------------+
    53   * + 0x000E | "HELLO, WORLD" |
    54   * +--------+----------------+
    55   * </pre>
    56   *
    57   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    58   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    59   * @version $Rev: 2121 $, $Date: 2010-02-02 09:38:07 +0900 (Tue, 02 Feb 2010) $
    60   */
    61  @Sharable
    62  public class LengthFieldPrepender extends OneToOneEncoder {
    63
    64      private final int lengthFieldLength;
    65      private final boolean lengthIncludesLengthFieldLength;
    66
    67      /**
    68       * Creates a new instance.
    69       *
    70       * @param lengthFieldLength the length of the prepended length field.
    71       *                          Only 1, 2, 3, 4, and 8 are allowed.
    72       *
    73       * @throws IllegalArgumentException
    74       *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
    75       */
    76      public LengthFieldPrepender(int lengthFieldLength) {
    77          this(lengthFieldLength, false);
    78      }
    79
    80      /**
    81       * Creates a new instance.
    82       *
    83       * @param lengthFieldLength the length of the prepended length field.
    84       *                          Only 1, 2, 3, 4, and 8 are allowed.
    85       * @param lengthIncludesLengthFieldLength
    86       *                          if {@code true}, the length of the prepended
    87       *                          length field is added to the value of the
    88       *                          prepended length field.
    89       *
    90       * @throws IllegalArgumentException
    91       *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
    92       */
    93      public LengthFieldPrepender(
    94              int lengthFieldLength, boolean lengthIncludesLengthFieldLength) {
    95          if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
    96              lengthFieldLength != 3 && lengthFieldLength != 4 &&
    97              lengthFieldLength != 8) {
    98              throw new IllegalArgumentException(
    99                      "lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
    100                     lengthFieldLength);
    101         }
    102
    103         this.lengthFieldLength = lengthFieldLength;
    104         this.lengthIncludesLengthFieldLength = lengthIncludesLengthFieldLength;
    105     }
    106
    107     @Override
    108     protected Object encode(
    109             ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
    110         if (!(msg instanceof ChannelBuffer)) {
    111             return msg;
    112         }
    113
    114         ChannelBuffer body = (ChannelBuffer) msg;
    115         ChannelBuffer header = channel.getConfig().getBufferFactory().getBuffer(body.order(), lengthFieldLength);
    116
    117         int length = lengthIncludesLengthFieldLength?
    118                 body.readableBytes() + lengthFieldLength : body.readableBytes();
    119         switch (lengthFieldLength) {
    120         case 1:
    121             if (length >= 256) {
    122                 throw new IllegalArgumentException(
    123                         "length does not fit into a byte: " + length);
    124             }
    125             header.writeByte((byte) length);
    126             break;
    127         case 2:
    128             if (length >= 65536) {
    129                 throw new IllegalArgumentException(
    130                         "length does not fit into a short integer: " + length);
    131             }
    132             header.writeShort((short) length);
    133             break;
    134         case 3:
    135             if (length >= 16777216) {
    136                 throw new IllegalArgumentException(
    137                         "length does not fit into a medium integer: " + length);
    138             }
    139             header.writeMedium(length);
    140             break;
    141         case 4:
    142             header.writeInt(length);
    143             break;
    144         case 8:
    145             header.writeLong(length);
    146             break;
    147         default:
    148             throw new Error("should not reach here");
    149         }
    150         return wrappedBuffer(header, body);
    151     }
    152 }