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.netty5.handler.codec; 17 18 import io.netty5.channel.ChannelHandler; 19 import io.netty5.channel.ChannelHandlerAdapter; 20 import io.netty5.channel.ChannelHandlerContext; 21 import io.netty5.channel.ChannelPipeline; 22 import io.netty5.util.Resource; 23 import io.netty5.util.internal.TypeParameterMatcher; 24 25 import static io.netty5.util.internal.SilentDispose.autoClosing; 26 27 /** 28 * {@link ChannelHandler} which decodes from one message to another message. 29 * 30 * For example here is an implementation which decodes a {@link String} to an {@link Integer} which represent 31 * the length of the {@link String}. 32 * 33 * <pre>{@code 34 * public class StringToIntegerDecoder extends 35 * MessageToMessageDecoder<String> { 36 * 37 * @Override 38 * public void decode(ChannelHandlerContext ctx, String message, 39 * List<Object> out) throws Exception { 40 * out.add(message.length()); 41 * } 42 * } 43 * }</pre> 44 * 45 * Note that messages passed to {@link #decode(ChannelHandlerContext, Object)} will be 46 * {@linkplain Resource#dispose(Object) disposed of} automatically. 47 * <p> 48 * To take control of the message lifetime, you should instead override the 49 * {@link #decodeAndClose(ChannelHandlerContext, Object)} method. 50 * <p> 51 * Do not override both. 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 try { 84 if (acceptInboundMessage(msg)) { 85 @SuppressWarnings("unchecked") 86 I cast = (I) msg; 87 decodeAndClose(ctx, cast); 88 } else { 89 ctx.fireChannelRead(msg); 90 } 91 } catch (DecoderException e) { 92 throw e; 93 } catch (Exception e) { 94 throw new DecoderException(e); 95 } 96 } 97 98 /** 99 * Decode from one message to another. This method will be called for each written message that can be handled 100 * by this decoder. 101 * <p> 102 * The message will be {@linkplain Resource#dispose(Object) disposed of} after this call. 103 * <p> 104 * Subclasses that wish to sometimes pass messages through, should instead override the 105 * {@link #decodeAndClose(ChannelHandlerContext, Object)} method. 106 * 107 * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to 108 * @param msg the message to decode to another one 109 * @throws Exception is thrown if an error occurs 110 */ 111 protected void decode(ChannelHandlerContext ctx, I msg) throws Exception { 112 throw new CodecException(getClass().getName() + " must override either decode() or decodeAndClose()."); 113 } 114 115 /** 116 * Decode from one message to another. This method will be called for each written message that can be handled 117 * by this decoder. 118 * <p> 119 * The message will not be automatically {@linkplain Resource#dispose(Object) disposed of} after this call. 120 * Instead, the responsibility of ensuring that messages are disposed of falls upon the implementor of this method. 121 * <p> 122 * Subclasses that wish to have incoming messages automatically disposed of should instead override the 123 * {@link #decodeAndClose(ChannelHandlerContext, Object)} method. 124 * 125 * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to 126 * @param msg the message to decode to another one 127 * @throws Exception is thrown if an error occurs 128 */ 129 protected void decodeAndClose(ChannelHandlerContext ctx, I msg) throws Exception { 130 try (AutoCloseable ignore = autoClosing(msg)) { 131 decode(ctx, msg); 132 } 133 } 134 }