2013-03-05 12:52:05 +09:00
|
|
|
/*
|
|
|
|
* Copyright 2013 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.channel;
|
|
|
|
|
2013-03-08 14:05:40 +01:00
|
|
|
import io.netty.buffer.BufType;
|
2013-03-05 12:52:05 +09:00
|
|
|
import io.netty.buffer.BufUtil;
|
2013-03-08 08:20:46 +01:00
|
|
|
import io.netty.buffer.ByteBuf;
|
2013-03-05 12:52:05 +09:00
|
|
|
import io.netty.buffer.MessageBuf;
|
|
|
|
import io.netty.util.Signal;
|
|
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
|
|
|
2013-03-10 21:07:19 +01:00
|
|
|
/**
|
|
|
|
* Utility methods for use within your {@link ChannelHandler} implementation.
|
|
|
|
*/
|
2013-03-05 12:52:05 +09:00
|
|
|
public final class ChannelHandlerUtil {
|
|
|
|
|
|
|
|
public static final Signal ABORT = new Signal(ChannelHandlerUtil.class.getName() + ".ABORT");
|
|
|
|
|
|
|
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelHandlerUtil.class);
|
|
|
|
|
|
|
|
public static <T> void handleInboundBufferUpdated(
|
|
|
|
ChannelHandlerContext ctx, SingleInboundMessageHandler<T> handler) throws Exception {
|
|
|
|
MessageBuf<Object> in = ctx.inboundMessageBuffer();
|
|
|
|
if (in.isEmpty() || !handler.beginMessageReceived(ctx)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageBuf<Object> out = ctx.nextInboundMessageBuffer();
|
|
|
|
int oldOutSize = out.size();
|
|
|
|
try {
|
|
|
|
for (;;) {
|
|
|
|
Object msg = in.poll();
|
|
|
|
if (msg == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handler.acceptInboundMessage(msg)) {
|
|
|
|
out.add(msg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
T imsg = (T) msg;
|
|
|
|
try {
|
|
|
|
handler.messageReceived(ctx, imsg);
|
|
|
|
} finally {
|
|
|
|
BufUtil.release(imsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Signal abort) {
|
|
|
|
abort.expect(ABORT);
|
|
|
|
} finally {
|
|
|
|
if (oldOutSize != out.size()) {
|
|
|
|
ctx.fireInboundBufferUpdated();
|
|
|
|
}
|
|
|
|
|
|
|
|
handler.endMessageReceived(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static <T> void handleFlush(
|
|
|
|
ChannelHandlerContext ctx, ChannelPromise promise,
|
|
|
|
SingleOutboundMessageHandler<T> handler) throws Exception {
|
|
|
|
|
|
|
|
handleFlush(ctx, promise, true, handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static <T> void handleFlush(
|
|
|
|
ChannelHandlerContext ctx, ChannelPromise promise, boolean closeOnFailedFlush,
|
|
|
|
SingleOutboundMessageHandler<T> handler) throws Exception {
|
|
|
|
|
|
|
|
MessageBuf<Object> in = ctx.outboundMessageBuffer();
|
|
|
|
final int inSize = in.size();
|
|
|
|
if (inSize == 0) {
|
|
|
|
ctx.flush(promise);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int processed = 0;
|
|
|
|
try {
|
2013-03-12 15:20:46 +09:00
|
|
|
if (!handler.beginFlush(ctx)) {
|
|
|
|
throw new IncompleteFlushException(
|
|
|
|
"beginFlush(..) rejected the flush request by returning false. " +
|
|
|
|
"none of " + inSize + " message(s) fulshed.");
|
|
|
|
}
|
2013-03-05 12:52:05 +09:00
|
|
|
for (;;) {
|
|
|
|
Object msg = in.poll();
|
|
|
|
if (msg == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handler.acceptOutboundMessage(msg)) {
|
2013-03-08 14:05:40 +01:00
|
|
|
addToNextOutboundBuffer(ctx, msg);
|
2013-03-05 12:52:05 +09:00
|
|
|
processed ++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
T imsg = (T) msg;
|
|
|
|
try {
|
|
|
|
handler.flush(ctx, imsg);
|
|
|
|
processed ++;
|
|
|
|
} finally {
|
|
|
|
BufUtil.release(imsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Throwable t) {
|
2013-03-12 15:20:46 +09:00
|
|
|
IncompleteFlushException pfe;
|
|
|
|
if (t instanceof IncompleteFlushException) {
|
|
|
|
pfe = (IncompleteFlushException) t;
|
2013-03-05 12:52:05 +09:00
|
|
|
} else {
|
2013-03-12 15:20:46 +09:00
|
|
|
String msg = processed + " out of " + inSize + " message(s) flushed";
|
|
|
|
if (t instanceof Signal) {
|
|
|
|
Signal abort = (Signal) t;
|
|
|
|
abort.expect(ABORT);
|
|
|
|
pfe = new IncompleteFlushException("aborted: " + msg);
|
|
|
|
} else {
|
|
|
|
pfe = new IncompleteFlushException(msg, t);
|
|
|
|
}
|
2013-03-05 12:52:05 +09:00
|
|
|
}
|
|
|
|
fail(ctx, promise, closeOnFailedFlush, pfe);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
handler.endFlush(ctx);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
if (promise.isDone()) {
|
|
|
|
logger.warn("endFlush() raised a masked exception due to failed flush().", t);
|
|
|
|
} else {
|
|
|
|
fail(ctx, promise, closeOnFailedFlush, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!promise.isDone()) {
|
|
|
|
ctx.flush(promise);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void fail(
|
|
|
|
ChannelHandlerContext ctx, ChannelPromise promise, boolean closeOnFailedFlush, Throwable cause) {
|
|
|
|
promise.setFailure(cause);
|
|
|
|
if (closeOnFailedFlush) {
|
|
|
|
ctx.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-08 08:20:46 +01:00
|
|
|
/**
|
|
|
|
* Allocate a {@link ByteBuf} taking the {@link ChannelConfig#getDefaultHandlerByteBufType()}
|
|
|
|
* setting into account.
|
|
|
|
*/
|
|
|
|
public static ByteBuf allocate(ChannelHandlerContext ctx) {
|
|
|
|
switch(ctx.channel().config().getDefaultHandlerByteBufType()) {
|
|
|
|
case DIRECT:
|
|
|
|
return ctx.alloc().directBuffer();
|
|
|
|
case PREFER_DIRECT:
|
|
|
|
return ctx.alloc().ioBuffer();
|
|
|
|
case HEAP:
|
|
|
|
return ctx.alloc().heapBuffer();
|
|
|
|
default:
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate a {@link ByteBuf} taking the {@link ChannelConfig#getDefaultHandlerByteBufType()}
|
|
|
|
* setting into account.
|
|
|
|
*/
|
|
|
|
public static ByteBuf allocate(ChannelHandlerContext ctx, int initialCapacity) {
|
|
|
|
switch(ctx.channel().config().getDefaultHandlerByteBufType()) {
|
|
|
|
case DIRECT:
|
|
|
|
return ctx.alloc().directBuffer(initialCapacity);
|
|
|
|
case PREFER_DIRECT:
|
|
|
|
return ctx.alloc().ioBuffer(initialCapacity);
|
|
|
|
case HEAP:
|
|
|
|
return ctx.alloc().heapBuffer(initialCapacity);
|
|
|
|
default:
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allocate a {@link ByteBuf} taking the {@link ChannelConfig#getDefaultHandlerByteBufType()}
|
|
|
|
* setting into account.
|
|
|
|
*/
|
|
|
|
public static ByteBuf allocate(ChannelHandlerContext ctx, int initialCapacity, int maxCapacity) {
|
|
|
|
switch(ctx.channel().config().getDefaultHandlerByteBufType()) {
|
|
|
|
case DIRECT:
|
|
|
|
return ctx.alloc().directBuffer(initialCapacity, maxCapacity);
|
|
|
|
case PREFER_DIRECT:
|
|
|
|
return ctx.alloc().ioBuffer(initialCapacity, maxCapacity);
|
|
|
|
case HEAP:
|
|
|
|
return ctx.alloc().heapBuffer(initialCapacity, maxCapacity);
|
|
|
|
default:
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
}
|
2013-03-08 14:05:40 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the msg to the next outbound buffer in the {@link ChannelPipeline}. This takes special care of
|
|
|
|
* msgs that are of type {@link ByteBuf}.
|
|
|
|
*/
|
|
|
|
public static boolean addToNextOutboundBuffer(ChannelHandlerContext ctx, Object msg) {
|
|
|
|
if (msg instanceof ByteBuf) {
|
|
|
|
if (ctx.nextOutboundBufferType() == BufType.BYTE) {
|
|
|
|
ctx.nextOutboundByteBuffer().writeBytes((ByteBuf) msg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2013-04-03 11:32:33 +02:00
|
|
|
return ctx.nextOutboundMessageBuffer().add(msg);
|
2013-03-08 14:05:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the msg to the next inbound buffer in the {@link ChannelPipeline}. This takes special care of
|
|
|
|
* msgs that are of type {@link ByteBuf}.
|
|
|
|
*/
|
|
|
|
public static boolean addToNextInboundBuffer(ChannelHandlerContext ctx, Object msg) {
|
|
|
|
if (msg instanceof ByteBuf) {
|
|
|
|
if (ctx.nextInboundBufferType() == BufType.BYTE) {
|
|
|
|
ctx.nextInboundByteBuffer().writeBytes((ByteBuf) msg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2013-04-03 11:32:33 +02:00
|
|
|
return ctx.nextInboundMessageBuffer().add(msg);
|
2013-03-08 14:05:40 +01:00
|
|
|
}
|
|
|
|
|
2013-03-05 12:52:05 +09:00
|
|
|
private ChannelHandlerUtil() { }
|
|
|
|
|
|
|
|
public interface SingleInboundMessageHandler<T> {
|
|
|
|
/**
|
|
|
|
* Returns {@code true} if and only if the specified message can be handled by this handler.
|
|
|
|
*
|
|
|
|
* @param msg the message
|
|
|
|
*/
|
|
|
|
boolean acceptInboundMessage(Object msg) throws Exception;
|
|
|
|
|
|
|
|
/**
|
2013-03-08 08:20:46 +01:00
|
|
|
* Will get notified once {@link ChannelStateHandler#inboundBufferUpdated(ChannelHandlerContext)} was called.
|
2013-03-05 12:52:05 +09:00
|
|
|
*
|
|
|
|
* If this method returns {@code false} no further processing of the {@link MessageBuf}
|
2013-03-08 08:20:46 +01:00
|
|
|
* will be done until the next call of {@link ChannelStateHandler#inboundBufferUpdated(ChannelHandlerContext)}.
|
2013-03-05 12:52:05 +09:00
|
|
|
*
|
|
|
|
* This will return {@code true} by default, and may get overriden by sub-classes for
|
|
|
|
* special handling.
|
|
|
|
*
|
|
|
|
* @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to
|
|
|
|
*/
|
|
|
|
boolean beginMessageReceived(ChannelHandlerContext ctx) throws Exception;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is called once a message was received.
|
|
|
|
*
|
|
|
|
* @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to
|
|
|
|
* @param msg the message to handle
|
|
|
|
*/
|
|
|
|
void messageReceived(ChannelHandlerContext ctx, T msg) throws Exception;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is called when {@link #messageReceived(ChannelHandlerContext, Object)} returns.
|
|
|
|
*
|
|
|
|
* Super-classes may-override this for special handling.
|
|
|
|
*
|
|
|
|
* @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to
|
|
|
|
*/
|
|
|
|
void endMessageReceived(ChannelHandlerContext ctx) throws Exception;
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface SingleOutboundMessageHandler<T> {
|
|
|
|
/**
|
|
|
|
* Returns {@code true} if and only if the specified message can be handled by this handler.
|
|
|
|
*
|
|
|
|
* @param msg the message
|
|
|
|
*/
|
|
|
|
boolean acceptOutboundMessage(Object msg) throws Exception;
|
|
|
|
|
|
|
|
/**
|
2013-03-08 08:20:46 +01:00
|
|
|
* Will get notified once {@link ChannelOperationHandler#flush(ChannelHandlerContext, ChannelPromise)}
|
|
|
|
* was called.
|
2013-03-05 12:52:05 +09:00
|
|
|
*
|
|
|
|
* @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to
|
2013-03-12 15:20:46 +09:00
|
|
|
*
|
|
|
|
* @return {@code true} to accept the flush request. {@code false} to reject the flush request and
|
|
|
|
* to fail the promise associated with the flush request with {@link IncompleteFlushException}.
|
2013-03-05 12:52:05 +09:00
|
|
|
*/
|
2013-03-12 15:20:46 +09:00
|
|
|
boolean beginFlush(ChannelHandlerContext ctx) throws Exception;
|
2013-03-05 12:52:05 +09:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Is called once a message is being flushed.
|
|
|
|
*
|
|
|
|
* @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to
|
|
|
|
* @param msg the message to handle
|
|
|
|
*/
|
|
|
|
void flush(ChannelHandlerContext ctx, T msg) throws Exception;
|
|
|
|
|
|
|
|
/**
|
2013-03-08 08:20:46 +01:00
|
|
|
* Is called when {@link ChannelOperationHandler#flush(ChannelHandlerContext, ChannelPromise)} returns.
|
2013-03-05 12:52:05 +09:00
|
|
|
*
|
|
|
|
* Super-classes may-override this for special handling.
|
|
|
|
*
|
|
|
|
* @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to
|
|
|
|
*/
|
|
|
|
void endFlush(ChannelHandlerContext ctx) throws Exception;
|
|
|
|
}
|
|
|
|
}
|