View Javadoc

    1   /*
    2    * Copyright 2012 The Netty Project
    3    *
    4    * The Netty Project licenses this file to you under the Apache License,
    5    * version 2.0 (the "License"); you may not use this file except in compliance
    6    * with the 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.ChannelHandler.Sharable;
    26  import org.jboss.netty.channel.ChannelHandlerContext;
    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  @Sharable
    58  public class LengthFieldPrepender extends OneToOneEncoder {
    59
    60      private final int lengthFieldLength;
    61      private final boolean lengthIncludesLengthFieldLength;
    62
    63      /**
    64       * Creates a new instance.
    65       *
    66       * @param lengthFieldLength the length of the prepended length field.
    67       *                          Only 1, 2, 3, 4, and 8 are allowed.
    68       *
    69       * @throws IllegalArgumentException
    70       *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
    71       */
    72      public LengthFieldPrepender(int lengthFieldLength) {
    73          this(lengthFieldLength, false);
    74      }
    75
    76      /**
    77       * Creates a new instance.
    78       *
    79       * @param lengthFieldLength the length of the prepended length field.
    80       *                          Only 1, 2, 3, 4, and 8 are allowed.
    81       * @param lengthIncludesLengthFieldLength
    82       *                          if {@code true}, the length of the prepended
    83       *                          length field is added to the value of the
    84       *                          prepended length field.
    85       *
    86       * @throws IllegalArgumentException
    87       *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
    88       */
    89      public LengthFieldPrepender(
    90              int lengthFieldLength, boolean lengthIncludesLengthFieldLength) {
    91          if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
    92              lengthFieldLength != 3 && lengthFieldLength != 4 &&
    93              lengthFieldLength != 8) {
    94              throw new IllegalArgumentException(
    95                      "lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
    96                      lengthFieldLength);
    97          }
    98
    99          this.lengthFieldLength = lengthFieldLength;
    100         this.lengthIncludesLengthFieldLength = lengthIncludesLengthFieldLength;
    101     }
    102
    103     @Override
    104     protected Object encode(
    105             ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
    106         if (!(msg instanceof ChannelBuffer)) {
    107             return msg;
    108         }
    109
    110         ChannelBuffer body = (ChannelBuffer) msg;
    111         ChannelBuffer header = channel.getConfig().getBufferFactory().getBuffer(body.order(), lengthFieldLength);
    112
    113         int length = lengthIncludesLengthFieldLength?
    114                 body.readableBytes() + lengthFieldLength : body.readableBytes();
    115         switch (lengthFieldLength) {
    116         case 1:
    117             if (length >= 256) {
    118                 throw new IllegalArgumentException(
    119                         "length does not fit into a byte: " + length);
    120             }
    121             header.writeByte((byte) length);
    122             break;
    123         case 2:
    124             if (length >= 65536) {
    125                 throw new IllegalArgumentException(
    126                         "length does not fit into a short integer: " + length);
    127             }
    128             header.writeShort((short) length);
    129             break;
    130         case 3:
    131             if (length >= 16777216) {
    132                 throw new IllegalArgumentException(
    133                         "length does not fit into a medium integer: " + length);
    134             }
    135             header.writeMedium(length);
    136             break;
    137         case 4:
    138             header.writeInt(length);
    139             break;
    140         case 8:
    141             header.writeLong(length);
    142             break;
    143         default:
    144             throw new Error("should not reach here");
    145         }
    146         return wrappedBuffer(header, body);
    147     }
    148 }