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