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.ChannelDuplexHandler;
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 ChannelDuplexHandler {
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          ensureNotSharable();
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          ensureNotSharable();
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     @Override
112     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
113         decoder.channelReadComplete(ctx);
114     }
115 
116     @Override
117     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
118         decoder.channelInactive(ctx);
119     }
120 
121     @Override
122     public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
123         try {
124             decoder.handlerAdded(ctx);
125         } finally {
126             encoder.handlerAdded(ctx);
127         }
128     }
129 
130     @Override
131     public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
132         try {
133             decoder.handlerRemoved(ctx);
134         } finally {
135             encoder.handlerRemoved(ctx);
136         }
137     }
138 
139     /**
140      * @see MessageToByteEncoder#encode(ChannelHandlerContext, Object, ByteBuf)
141      */
142     protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
143 
144     /**
145      * @see ByteToMessageDecoder#decode(ChannelHandlerContext, ByteBuf, List)
146      */
147     protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
148 
149     /**
150      * @see ByteToMessageDecoder#decodeLast(ChannelHandlerContext, ByteBuf, List)
151      */
152     protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
153         if (in.isReadable()) {
154             // Only call decode() if there is something left in the buffer to decode.
155             // See https://github.com/netty/netty/issues/4386
156             decode(ctx, in, out);
157         }
158     }
159 
160     private final class Encoder extends MessageToByteEncoder<I> {
161         Encoder(boolean preferDirect) {
162             super(preferDirect);
163         }
164 
165         @Override
166         public boolean acceptOutboundMessage(Object msg) throws Exception {
167             return ByteToMessageCodec.this.acceptOutboundMessage(msg);
168         }
169 
170         @Override
171         protected void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception {
172             ByteToMessageCodec.this.encode(ctx, msg, out);
173         }
174     }
175 }