io.netty.handler.codec.MessageAggregator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisson-all Show documentation
Show all versions of redisson-all Show documentation
Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC
/*
* 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
© 2015 - 2024 Weber Informatics LLC | Privacy Policy