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 io.netty.handler.codec;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.channel.ChannelHandlerAdapter;
20  import io.netty.channel.ChannelHandlerContext;
21  import io.netty.channel.ChannelPromise;
22  import io.netty.util.internal.TypeParameterMatcher;
23  
24  import java.util.List;
25  
26  /**
27   * A Codec for on-the-fly encoding/decoding of bytes to messages and vise-versa.
28   *
29   * This can be thought of as a combination of {@link ByteToMessageDecoder} and {@link MessageToByteEncoder}.
30   *
31   * Be aware that sub-classes of {@link ByteToMessageCodec} <strong>MUST NOT</strong>
32   * annotated with {@link @Sharable}.
33   */
34  public abstract class ByteToMessageCodec<I> extends ChannelHandlerAdapter {
35  
36      private final TypeParameterMatcher outboundMsgMatcher;
37      private final MessageToByteEncoder<I> encoder;
38  
39      private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() {
40          @Override
41          public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
42              ByteToMessageCodec.this.decode(ctx, in, out);
43          }
44  
45          @Override
46          protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
47              ByteToMessageCodec.this.decodeLast(ctx, in, out);
48          }
49      };
50  
51      /**
52       * @see {@link #ByteToMessageCodec(boolean)} with {@code true} as boolean parameter.
53       */
54      protected ByteToMessageCodec() {
55          this(true);
56      }
57  
58      /**
59       * @see {@link #ByteToMessageCodec(Class, boolean)} with {@code true} as boolean value.
60       */
61      protected ByteToMessageCodec(Class<? extends I> outboundMessageType) {
62          this(outboundMessageType, true);
63      }
64  
65      /**
66       * Create a new instance which will try to detect the types to match out of the type parameter of the class.
67       *
68       * @param preferDirect          {@code true} if a direct {@link ByteBuf} should be tried to be used as target for
69       *                              the encoded messages. If {@code false} is used it will allocate a heap
70       *                              {@link ByteBuf}, which is backed by an byte array.
71       */
72      protected ByteToMessageCodec(boolean preferDirect) {
73          CodecUtil.ensureNotSharable(this);
74          outboundMsgMatcher = TypeParameterMatcher.find(this, ByteToMessageCodec.class, "I");
75          encoder = new Encoder(preferDirect);
76      }
77  
78      /**
79       * Create a new instance
80       *
81       * @param outboundMessageType   The type of messages to match
82       * @param preferDirect          {@code true} if a direct {@link ByteBuf} should be tried to be used as target for
83       *                              the encoded messages. If {@code false} is used it will allocate a heap
84       *                              {@link ByteBuf}, which is backed by an byte array.
85       */
86      protected ByteToMessageCodec(Class<? extends I> outboundMessageType, boolean preferDirect) {
87          CodecUtil.ensureNotSharable(this);
88          outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType);
89          encoder = new Encoder(preferDirect);
90      }
91  
92      /**
93       * Returns {@code true} if and only if the specified message can be encoded by this codec.
94       *
95       * @param msg the message
96       */
97      public boolean acceptOutboundMessage(Object msg) throws Exception {
98          return outboundMsgMatcher.match(msg);
99      }
100 
101     @Override
102     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
103         decoder.channelRead(ctx, msg);
104     }
105 
106     @Override
107     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
108         encoder.write(ctx, msg, promise);
109     }
110 
111     /**
112      * @see MessageToByteEncoder#encode(ChannelHandlerContext, Object, ByteBuf)
113      */
114     protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
115 
116     /**
117      * @see ByteToMessageDecoder#decode(ChannelHandlerContext, ByteBuf, List)
118      */
119     protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
120 
121     /**
122      * @see ByteToMessageDecoder#decodeLast(ChannelHandlerContext, ByteBuf, List)
123      */
124     protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
125         decode(ctx, in, out);
126     }
127 
128     private final class Encoder extends MessageToByteEncoder<I> {
129         Encoder(boolean preferDirect) {
130             super(preferDirect);
131         }
132 
133         @Override
134         public boolean acceptOutboundMessage(Object msg) throws Exception {
135             return ByteToMessageCodec.this.acceptOutboundMessage(msg);
136         }
137 
138         @Override
139         protected void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception {
140             ByteToMessageCodec.this.encode(ctx, msg, out);
141         }
142     }
143 }