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    *   https://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.channel.ChannelDuplexHandler;
19  import io.netty.channel.ChannelHandlerContext;
20  import io.netty.channel.ChannelPromise;
21  import io.netty.util.ReferenceCounted;
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 message.
28   *
29   * This can be thought of as a combination of {@link MessageToMessageDecoder} and {@link MessageToMessageEncoder}.
30   *
31   * Here is an example of a {@link MessageToMessageCodec} which just decode from {@link Integer} to {@link Long}
32   * and encode from {@link Long} to {@link Integer}.
33   *
34   * <pre>
35   *     public class NumberCodec extends
36   *             {@link MessageToMessageCodec}&lt;{@link Integer}, {@link Long}&gt; {
37   *         {@code @Override}
38   *         public {@link Long} decode({@link ChannelHandlerContext} ctx, {@link Integer} msg, List&lt;Object&gt; out)
39   *                 throws {@link Exception} {
40   *             out.add(msg.longValue());
41   *         }
42   *
43   *         {@code @Override}
44   *         public {@link Integer} encode({@link ChannelHandlerContext} ctx, {@link Long} msg, List&lt;Object&gt; out)
45   *                 throws {@link Exception} {
46   *             out.add(msg.intValue());
47   *         }
48   *     }
49   * </pre>
50   *
51   * Be aware that you need to call {@link ReferenceCounted#retain()} on messages that are just passed through if they
52   * are of type {@link ReferenceCounted}. This is needed as the {@link MessageToMessageCodec} will call
53   * {@link ReferenceCounted#release()} on encoded / decoded messages.
54   */
55  public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelDuplexHandler {
56  
57      private final MessageToMessageDecoder<Object> decoder = new MessageToMessageDecoder<Object>() {
58  
59          @Override
60          public boolean acceptInboundMessage(Object msg) throws Exception {
61              return MessageToMessageCodec.this.acceptInboundMessage(msg);
62          }
63  
64          @Override
65          @SuppressWarnings("unchecked")
66          protected void decode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
67              MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out);
68          }
69  
70          @Override
71          public boolean isSharable() {
72              return MessageToMessageCodec.this.isSharable();
73          }
74      };
75      private final MessageToMessageEncoder<Object> encoder = new MessageToMessageEncoder<Object>() {
76  
77          @Override
78          public boolean acceptOutboundMessage(Object msg) throws Exception {
79              return MessageToMessageCodec.this.acceptOutboundMessage(msg);
80          }
81  
82          @Override
83          @SuppressWarnings("unchecked")
84          protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
85              MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out);
86          }
87  
88          @Override
89          public boolean isSharable() {
90              return MessageToMessageCodec.this.isSharable();
91          }
92      };
93  
94      private final TypeParameterMatcher inboundMsgMatcher;
95      private final TypeParameterMatcher outboundMsgMatcher;
96  
97      /**
98       * Create a new instance which will try to detect the types to decode and encode out of the type parameter
99       * of the class.
100      */
101     protected MessageToMessageCodec() {
102         inboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "INBOUND_IN");
103         outboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "OUTBOUND_IN");
104     }
105 
106     /**
107      * Create a new instance.
108      *
109      * @param inboundMessageType    The type of messages to decode
110      * @param outboundMessageType   The type of messages to encode
111      */
112     protected MessageToMessageCodec(
113             Class<? extends INBOUND_IN> inboundMessageType, Class<? extends OUTBOUND_IN> outboundMessageType) {
114         inboundMsgMatcher = TypeParameterMatcher.get(inboundMessageType);
115         outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType);
116     }
117 
118     @Override
119     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
120         decoder.channelRead(ctx, msg);
121     }
122 
123     @Override
124     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
125         decoder.channelReadComplete(ctx);
126     }
127 
128     @Override
129     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
130         encoder.write(ctx, msg, promise);
131     }
132 
133     /**
134      * Returns {@code true} if and only if the specified message can be decoded by this codec.
135      *
136      * @param msg the message
137      */
138     public boolean acceptInboundMessage(Object msg) throws Exception {
139         return inboundMsgMatcher.match(msg);
140     }
141 
142     /**
143      * Returns {@code true} if and only if the specified message can be encoded by this codec.
144      *
145      * @param msg the message
146      */
147     public boolean acceptOutboundMessage(Object msg) throws Exception {
148         return outboundMsgMatcher.match(msg);
149     }
150 
151     /**
152      * @see MessageToMessageEncoder#encode(ChannelHandlerContext, Object, List)
153      */
154     protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List<Object> out)
155             throws Exception;
156 
157     /**
158      * @see MessageToMessageDecoder#decode(ChannelHandlerContext, Object, List)
159      */
160     protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List<Object> out)
161             throws Exception;
162 }