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}&lt;{@link String}&gt; {
38  *
39  *         {@code @Override}
40  *         public void decode({@link ChannelHandlerContext} ctx, {@link String} message,
41  *                            List&lt;Object&gt; 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 trueif 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