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 }