109 lines
3.9 KiB
Java
109 lines
3.9 KiB
Java
/*
|
||
* Copyright 2012 The Netty Project
|
||
*
|
||
* The Netty Project licenses this file to you under the Apache License,
|
||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||
* with the License. You may obtain a copy of the License at:
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||
* License for the specific language governing permissions and limitations
|
||
* under the License.
|
||
*/
|
||
package io.netty.handler.codec;
|
||
|
||
import io.netty.channel.ChannelHandler;
|
||
import io.netty.channel.ChannelHandlerAdapter;
|
||
import io.netty.channel.ChannelHandlerContext;
|
||
import io.netty.channel.ChannelPipeline;
|
||
import io.netty.util.ReferenceCountUtil;
|
||
import io.netty.util.ReferenceCounted;
|
||
import io.netty.util.internal.TypeParameterMatcher;
|
||
|
||
/**
|
||
* {@link ChannelHandler} which decodes from one message to an other message.
|
||
*
|
||
*
|
||
* For example here is an implementation which decodes a {@link String} to an {@link Integer} which represent
|
||
* the length of the {@link String}.
|
||
*
|
||
* <pre>
|
||
* public class StringToIntegerDecoder extends
|
||
* {@link MessageToMessageDecoder}<{@link String}> {
|
||
*
|
||
* {@code @Override}
|
||
* public void decode({@link ChannelHandlerContext} ctx, {@link String} message,
|
||
* List<Object> out) throws {@link Exception} {
|
||
* out.add(message.length());
|
||
* }
|
||
* }
|
||
* </pre>
|
||
*
|
||
* Be aware that you need to call {@link ReferenceCounted#retain()} on messages that are just passed through if they
|
||
* are of type {@link ReferenceCounted}. This is needed as the {@link MessageToMessageDecoder} will call
|
||
* {@link ReferenceCounted#release()} on decoded messages.
|
||
*
|
||
*/
|
||
public abstract class MessageToMessageDecoder<I> extends ChannelHandlerAdapter {
|
||
|
||
private final TypeParameterMatcher matcher;
|
||
|
||
/**
|
||
* Create a new instance which will try to detect the types to match out of the type parameter of the class.
|
||
*/
|
||
protected MessageToMessageDecoder() {
|
||
matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I");
|
||
}
|
||
|
||
/**
|
||
* Create a new instance
|
||
*
|
||
* @param inboundMessageType The type of messages to match and so decode
|
||
*/
|
||
protected MessageToMessageDecoder(Class<? extends I> inboundMessageType) {
|
||
matcher = TypeParameterMatcher.get(inboundMessageType);
|
||
}
|
||
|
||
/**
|
||
* Returns {@code true} if the given message should be handled. If {@code false} it will be passed to the next
|
||
* {@link ChannelHandler} in the {@link ChannelPipeline}.
|
||
*/
|
||
public boolean acceptInboundMessage(Object msg) throws Exception {
|
||
return matcher.match(msg);
|
||
}
|
||
|
||
@Override
|
||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||
try {
|
||
if (acceptInboundMessage(msg)) {
|
||
@SuppressWarnings("unchecked")
|
||
I cast = (I) msg;
|
||
try {
|
||
decode(ctx, cast);
|
||
} finally {
|
||
ReferenceCountUtil.release(cast);
|
||
}
|
||
} else {
|
||
ctx.fireChannelRead(msg);
|
||
}
|
||
} catch (DecoderException e) {
|
||
throw e;
|
||
} catch (Exception e) {
|
||
throw new DecoderException(e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Decode from one message to an other. This method will be called for each written message that can be handled
|
||
* by this decoder.
|
||
*
|
||
* @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to
|
||
* @param msg the message to decode to an other one
|
||
* @throws Exception is thrown if an error occurs
|
||
*/
|
||
protected abstract void decode(ChannelHandlerContext ctx, I msg) throws Exception;
|
||
}
|