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.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}<{@link Integer}, {@link Long}> {
37 * {@code @Override}
38 * public {@link Long} decode({@link ChannelHandlerContext} ctx, {@link Integer} msg, List<Object> 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<Object> 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 MessageToMessageEncoder<Object> encoder = new MessageToMessageEncoder<Object>() {
58
59 @Override
60 public boolean acceptOutboundMessage(Object msg) throws Exception {
61 return MessageToMessageCodec.this.acceptOutboundMessage(msg);
62 }
63
64 @Override
65 @SuppressWarnings("unchecked")
66 protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
67 MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out);
68 }
69 };
70
71 private final MessageToMessageDecoder<Object> decoder = new MessageToMessageDecoder<Object>() {
72
73 @Override
74 public boolean acceptInboundMessage(Object msg) throws Exception {
75 return MessageToMessageCodec.this.acceptInboundMessage(msg);
76 }
77
78 @Override
79 @SuppressWarnings("unchecked")
80 protected void decode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
81 MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out);
82 }
83 };
84
85 private final TypeParameterMatcher inboundMsgMatcher;
86 private final TypeParameterMatcher outboundMsgMatcher;
87
88 /**
89 * Create a new instance which will try to detect the types to decode and encode out of the type parameter
90 * of the class.
91 */
92 protected MessageToMessageCodec() {
93 inboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "INBOUND_IN");
94 outboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "OUTBOUND_IN");
95 }
96
97 /**
98 * Create a new instance.
99 *
100 * @param inboundMessageType The type of messages to decode
101 * @param outboundMessageType The type of messages to encode
102 */
103 protected MessageToMessageCodec(
104 Class<? extends INBOUND_IN> inboundMessageType, Class<? extends OUTBOUND_IN> outboundMessageType) {
105 inboundMsgMatcher = TypeParameterMatcher.get(inboundMessageType);
106 outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType);
107 }
108
109 @Override
110 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
111 decoder.channelRead(ctx, msg);
112 }
113
114 @Override
115 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
116 encoder.write(ctx, msg, promise);
117 }
118
119 /**
120 * Returns {@code true} if and only if the specified message can be decoded by this codec.
121 *
122 * @param msg the message
123 */
124 public boolean acceptInboundMessage(Object msg) throws Exception {
125 return inboundMsgMatcher.match(msg);
126 }
127
128 /**
129 * Returns {@code true} if and only if the specified message can be encoded by this codec.
130 *
131 * @param msg the message
132 */
133 public boolean acceptOutboundMessage(Object msg) throws Exception {
134 return outboundMsgMatcher.match(msg);
135 }
136
137 /**
138 * @see MessageToMessageEncoder#encode(ChannelHandlerContext, Object, List)
139 */
140 protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List<Object> out)
141 throws Exception;
142
143 /**
144 * @see MessageToMessageDecoder#decode(ChannelHandlerContext, Object, List)
145 */
146 protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List<Object> out)
147 throws Exception;
148 }