io.netty.handler.codec.MessageAggregator Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* 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:
*
* https://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.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.CompositeByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.util.ReferenceCountUtil;
import java.util.List;
import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
/**
* An abstract {@link ChannelHandler} that aggregates a series of message objects into a single aggregated message.
*
* 'A series of messages' is composed of the following:
*
* - a single start message which optionally contains the first part of the content, and
* - 1 or more content messages.
*
* The content of the aggregated message will be the merged content of the start message and its following content
* messages. If this aggregator encounters a content message where {@link #isLastContentMessage(ByteBufHolder)}
* return {@code true} for, the aggregator will finish the aggregation and produce the aggregated message and expect
* another start message.
*
*
* @param the type that covers both start message and content message
* @param the type of the start message
* @param the type of the content message (must be a subtype of {@link ByteBufHolder})
* @param the type of the aggregated message (must be a subtype of {@code S} and {@link ByteBufHolder})
*/
public abstract class MessageAggregator
extends MessageToMessageDecoder {
private static final int DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS = 1024;
private final int maxContentLength;
private O currentMessage;
private boolean handlingOversizedMessage;
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
private ChannelHandlerContext ctx;
private ChannelFutureListener continueResponseWriteListener;
private boolean aggregating;
private boolean handleIncompleteAggregateDuringClose = true;
/**
* Creates a new instance.
*
* @param maxContentLength
* the maximum length of the aggregated content.
* If the length of the aggregated content exceeds this value,
* {@link #handleOversizedMessage(ChannelHandlerContext, Object)} will be called.
*/
protected MessageAggregator(int maxContentLength) {
validateMaxContentLength(maxContentLength);
this.maxContentLength = maxContentLength;
}
protected MessageAggregator(int maxContentLength, Class extends I> inboundMessageType) {
super(inboundMessageType);
validateMaxContentLength(maxContentLength);
this.maxContentLength = maxContentLength;
}
private static void validateMaxContentLength(int maxContentLength) {
checkPositiveOrZero(maxContentLength, "maxContentLength");
}
@Override
public boolean acceptInboundMessage(Object msg) throws Exception {
// No need to match last and full types because they are subset of first and middle types.
if (!super.acceptInboundMessage(msg)) {
return false;
}
@SuppressWarnings("unchecked")
I in = (I) msg;
if (isAggregated(in)) {
return false;
}
// NOTE: It's tempting to make this check only if aggregating is false. There are however
// side conditions in decode(...) in respect to large messages.
if (isStartMessage(in)) {
return true;
} else {
return aggregating && isContentMessage(in);
}
}
/**
* Returns {@code true} if and only if the specified message is a start message. Typically, this method is
* implemented as a single {@code return} statement with {@code instanceof}:
*
* return msg instanceof MyStartMessage;
*
*/
protected abstract boolean isStartMessage(I msg) throws Exception;
/**
* Returns {@code true} if and only if the specified message is a content message. Typically, this method is
* implemented as a single {@code return} statement with {@code instanceof}:
*
* return msg instanceof MyContentMessage;
*
*/
protected abstract boolean isContentMessage(I msg) throws Exception;
/**
* Returns {@code true} if and only if the specified message is the last content message. Typically, this method is
* implemented as a single {@code return} statement with {@code instanceof}:
*
* return msg instanceof MyLastContentMessage;
*
* or with {@code instanceof} and boolean field check:
*
* return msg instanceof MyContentMessage && msg.isLastFragment();
*
*/
protected abstract boolean isLastContentMessage(C msg) throws Exception;
/**
* Returns {@code true} if and only if the specified message is already aggregated. If this method returns
* {@code true}, this handler will simply forward the message to the next handler as-is.
*/
protected abstract boolean isAggregated(I msg) throws Exception;
/**
* Returns the maximum allowed length of the aggregated message in bytes.
*/
public final int maxContentLength() {
return maxContentLength;
}
/**
* Returns the maximum number of components in the cumulation buffer. If the number of
* the components in the cumulation buffer exceeds this value, the components of the
* cumulation buffer are consolidated into a single component, involving memory copies.
* The default value of this property is {@value #DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS}.
*/
public final int maxCumulationBufferComponents() {
return maxCumulationBufferComponents;
}
/**
* Sets the maximum number of components in the cumulation buffer. If the number of
* the components in the cumulation buffer exceeds this value, the components of the
* cumulation buffer are consolidated into a single component, involving memory copies.
* The default value of this property is {@value #DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS}
* and its minimum allowed value is {@code 2}.
*/
public final void setMaxCumulationBufferComponents(int maxCumulationBufferComponents) {
if (maxCumulationBufferComponents < 2) {
throw new IllegalArgumentException(
"maxCumulationBufferComponents: " + maxCumulationBufferComponents +
" (expected: >= 2)");
}
if (ctx == null) {
this.maxCumulationBufferComponents = maxCumulationBufferComponents;
} else {
throw new IllegalStateException(
"decoder properties cannot be changed once the decoder is added to a pipeline.");
}
}
/**
* @deprecated This method will be removed in future releases.
*/
@Deprecated
public final boolean isHandlingOversizedMessage() {
return handlingOversizedMessage;
}
protected final ChannelHandlerContext ctx() {
if (ctx == null) {
throw new IllegalStateException("not added to a pipeline yet");
}
return ctx;
}
@Override
protected void decode(final ChannelHandlerContext ctx, I msg, List