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.ChannelHandlerContext;
19 import io.netty.channel.ChannelInboundHandler;
20 import io.netty.channel.ChannelInboundHandlerAdapter;
21 import io.netty.channel.ChannelPipeline;
22 import io.netty.util.ReferenceCountUtil;
23 import io.netty.util.ReferenceCounted;
24 import io.netty.util.internal.TypeParameterMatcher;
25
26 import java.util.List;
27
28 /**
29 * {@link ChannelInboundHandlerAdapter} which decodes from one message to an other message.
30 *
31 *
32 * For example here is an implementation which decodes a {@link String} to an {@link Integer} which represent
33 * the length of the {@link String}.
34 *
35 * <pre>
36 * public class StringToIntegerDecoder extends
37 * {@link MessageToMessageDecoder}<{@link String}> {
38 *
39 * {@code @Override}
40 * public void decode({@link ChannelHandlerContext} ctx, {@link String} message,
41 * List<Object> out) throws {@link Exception} {
42 * out.add(message.length());
43 * }
44 * }
45 * </pre>
46 *
47 * Be aware that you need to call {@link ReferenceCounted#retain()} on messages that are just passed through if they
48 * are of type {@link ReferenceCounted}. This is needed as the {@link MessageToMessageDecoder} will call
49 * {@link ReferenceCounted#release()} on decoded messages.
50 *
51 */
52 public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter {
53
54 private final TypeParameterMatcher matcher;
55
56 /**
57 * Create a new instance which will try to detect the types to match out of the type parameter of the class.
58 */
59 protected MessageToMessageDecoder() {
60 matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I");
61 }
62
63 /**
64 * Create a new instance
65 *
66 * @param inboundMessageType The type of messages to match and so decode
67 */
68 protected MessageToMessageDecoder(Class<? extends I> inboundMessageType) {
69 matcher = TypeParameterMatcher.get(inboundMessageType);
70 }
71
72 /**
73 * Returns {@code true} if the given message should be handled. If {@code false} it will be passed to the next
74 * {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
75 */
76 public boolean acceptInboundMessage(Object msg) throws Exception {
77 return matcher.match(msg);
78 }
79
80 @Override
81 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
82 CodecOutputList out = CodecOutputList.newInstance();
83 try {
84 if (acceptInboundMessage(msg)) {
85 @SuppressWarnings("unchecked")
86 I cast = (I) msg;
87 try {
88 decode(ctx, cast, out);
89 } finally {
90 ReferenceCountUtil.release(cast);
91 }
92 } else {
93 out.add(msg);
94 }
95 } catch (DecoderException e) {
96 throw e;
97 } catch (Exception e) {
98 throw new DecoderException(e);
99 } finally {
100 try {
101 int size = out.size();
102 for (int i = 0; i < size; i++) {
103 ctx.fireChannelRead(out.getUnsafe(i));
104 }
105 } finally {
106 out.recycle();
107 }
108 }
109 }
110
111 /**
112 * Decode from one message to an other. This method will be called for each written message that can be handled
113 * by this decoder.
114 *
115 * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to
116 * @param msg the message to decode to an other one
117 * @param out the {@link List} to which decoded messages should be added
118 * @throws Exception is thrown if an error occurs
119 */
120 protected abstract void decode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
121 }
122