/* * Copyright 2011 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 org.jboss.netty.channel; import java.net.SocketAddress; import java.util.Map; import org.jboss.netty.util.internal.ConversionUtil; /** * A helper class which provides various convenience methods related with * {@link Channel}, {@link ChannelHandler}, and {@link ChannelPipeline}. * * <h3>Factory methods</h3> * <p> * It is always recommended to use the factory methods provided by * {@link Channels} rather than calling the constructor of the implementation * types. * <ul> * <li>{@link #pipeline()}</li> * <li>{@link #pipeline(ChannelPipeline)}</li> * <li>{@link #pipelineFactory(ChannelPipeline)}</li> * <li>{@link #succeededFuture(Channel)}</li> * <li>{@link #failedFuture(Channel, Throwable)}</li> * </ul> * * <h3>Upstream and downstream event generation</h3> * <p> * Various event generation methods are provided to simplify the generation of * upstream events and downstream events. It is always recommended to use the * event generation methods provided by {@link Channels} rather than calling * {@link ChannelHandlerContext#sendUpstream(ChannelEvent)} or * {@link ChannelHandlerContext#sendDownstream(ChannelEvent)} by yourself. * @apiviz.landmark */ public final class Channels { // pipeline factory methods /** * Creates a new {@link ChannelPipeline}. */ public static ChannelPipeline pipeline() { return new DefaultChannelPipeline(); } /** * Creates a new {@link ChannelPipeline} which contains the specified * {@link ChannelHandler}s. The names of the specified handlers are * generated automatically; the first handler's name is {@code "0"}, * the second handler's name is {@code "1"}, the third handler's name is * {@code "2"}, and so on. */ public static ChannelPipeline pipeline(ChannelHandler... handlers) { if (handlers == null) { throw new NullPointerException("handlers"); } ChannelPipeline newPipeline = pipeline(); for (int i = 0; i < handlers.length; i ++) { ChannelHandler h = handlers[i]; if (h == null) { break; } newPipeline.addLast(ConversionUtil.toString(i), h); } return newPipeline; } /** * Creates a new {@link ChannelPipeline} which contains the same entries * with the specified {@code pipeline}. Please note that only the names * and the references of the {@link ChannelHandler}s will be copied; a new * {@link ChannelHandler} instance will never be created. */ public static ChannelPipeline pipeline(ChannelPipeline pipeline) { ChannelPipeline newPipeline = pipeline(); for (Map.Entry<String, ChannelHandler> e: pipeline.toMap().entrySet()) { newPipeline.addLast(e.getKey(), e.getValue()); } return newPipeline; } /** * Creates a new {@link ChannelPipelineFactory} which creates a new * {@link ChannelPipeline} which contains the same entries with the * specified {@code pipeline}. Please note that only the names and the * references of the {@link ChannelHandler}s will be copied; a new * {@link ChannelHandler} instance will never be created. */ public static ChannelPipelineFactory pipelineFactory( final ChannelPipeline pipeline) { return new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { return pipeline(pipeline); } }; } // future factory methods /** * Creates a new non-cancellable {@link ChannelFuture} for the specified * {@link Channel}. */ public static ChannelFuture future(Channel channel) { return future(channel, false); } /** * Creates a new {@link ChannelFuture} for the specified {@link Channel}. * * @param cancellable {@code true} if and only if the returned future * can be canceled by {@link ChannelFuture#cancel()} */ public static ChannelFuture future(Channel channel, boolean cancellable) { return new DefaultChannelFuture(channel, cancellable); } /** * Creates a new {@link ChannelFuture} which is already succeeded for the * specified {@link Channel}. */ public static ChannelFuture succeededFuture(Channel channel) { if (channel instanceof AbstractChannel) { return ((AbstractChannel) channel).getSucceededFuture(); } else { return new SucceededChannelFuture(channel); } } /** * Creates a new {@link ChannelFuture} which has failed already for the * specified {@link Channel}. * * @param cause the cause of the failure */ public static ChannelFuture failedFuture(Channel channel, Throwable cause) { return new FailedChannelFuture(channel, cause); } // event emission methods /** * Sends a {@code "channelOpen"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. If the specified channel has a parent, * a {@code "childChannelOpen"} event will be sent, too. */ public static void fireChannelOpen(Channel channel) { // Notify the parent handler. if (channel.getParent() != null) { fireChildChannelStateChanged(channel.getParent(), channel); } channel.getPipeline().sendUpstream( new UpstreamChannelStateEvent( channel, ChannelState.OPEN, Boolean.TRUE)); } /** * Sends a {@code "channelOpen"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. * <p> * Please note that this method does not trigger a * {@code "childChannelOpen"} event unlike {@link #fireChannelOpen(Channel)} * method. */ public static void fireChannelOpen(ChannelHandlerContext ctx) { ctx.sendUpstream(new UpstreamChannelStateEvent( ctx.getChannel(), ChannelState.OPEN, Boolean.TRUE)); } /** * Sends a {@code "channelBound"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param localAddress * the local address where the specified channel is bound */ public static void fireChannelBound(Channel channel, SocketAddress localAddress) { channel.getPipeline().sendUpstream( new UpstreamChannelStateEvent( channel, ChannelState.BOUND, localAddress)); } /** * Sends a {@code "channelBound"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param localAddress * the local address where the specified channel is bound */ public static void fireChannelBound(ChannelHandlerContext ctx, SocketAddress localAddress) { ctx.sendUpstream(new UpstreamChannelStateEvent( ctx.getChannel(), ChannelState.BOUND, localAddress)); } /** * Sends a {@code "channelConnected"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param remoteAddress * the remote address where the specified channel is connected */ public static void fireChannelConnected(Channel channel, SocketAddress remoteAddress) { channel.getPipeline().sendUpstream( new UpstreamChannelStateEvent( channel, ChannelState.CONNECTED, remoteAddress)); } /** * Sends a {@code "channelConnected"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param remoteAddress * the remote address where the specified channel is connected */ public static void fireChannelConnected(ChannelHandlerContext ctx, SocketAddress remoteAddress) { ctx.sendUpstream(new UpstreamChannelStateEvent( ctx.getChannel(), ChannelState.CONNECTED, remoteAddress)); } /** * Sends a {@code "messageReceived"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param message the received message */ public static void fireMessageReceived(Channel channel, Object message) { fireMessageReceived(channel, message, null); } /** * Sends a {@code "messageReceived"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel} belongs. * * @param message the received message * @param remoteAddress the remote address where the received message * came from */ public static void fireMessageReceived(Channel channel, Object message, SocketAddress remoteAddress) { channel.getPipeline().sendUpstream( new UpstreamMessageEvent(channel, message, remoteAddress)); } /** * Sends a {@code "messageReceived"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param message the received message */ public static void fireMessageReceived(ChannelHandlerContext ctx, Object message) { ctx.sendUpstream(new UpstreamMessageEvent(ctx.getChannel(), message, null)); } /** * Sends a {@code "messageReceived"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param message the received message * @param remoteAddress the remote address where the received message * came from */ public static void fireMessageReceived( ChannelHandlerContext ctx, Object message, SocketAddress remoteAddress) { ctx.sendUpstream(new UpstreamMessageEvent( ctx.getChannel(), message, remoteAddress)); } /** * Sends a {@code "writeComplete"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel} in the next io-thread. */ public static ChannelFuture fireWriteCompleteLater(final Channel channel, final long amount) { return channel.getPipeline().execute(new Runnable() { public void run() { fireWriteComplete(channel, amount); } }); } /** * Sends a {@code "writeComplete"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. */ public static void fireWriteComplete(Channel channel, long amount) { if (amount == 0) { return; } channel.getPipeline().sendUpstream( new DefaultWriteCompletionEvent(channel, amount)); } /** * Sends a {@code "writeComplete"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. */ public static void fireWriteComplete(ChannelHandlerContext ctx, long amount) { ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount)); } /** * Sends a {@code "channelInterestChanged"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel} once the io-thread runs again. */ public static ChannelFuture fireChannelInterestChangedLater(final Channel channel) { return channel.getPipeline().execute(new Runnable() { public void run() { fireChannelInterestChanged(channel); } }); } /** * Sends a {@code "channelInterestChanged"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. */ public static void fireChannelInterestChanged(Channel channel) { channel.getPipeline().sendUpstream( new UpstreamChannelStateEvent( channel, ChannelState.INTEREST_OPS, Channel.OP_READ)); } /** * Sends a {@code "channelInterestChanged"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. */ public static void fireChannelInterestChanged( ChannelHandlerContext ctx) { ctx.sendUpstream( new UpstreamChannelStateEvent( ctx.getChannel(), ChannelState.INTEREST_OPS, Channel.OP_READ)); } /** * Sends a {@code "channelDisconnected"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel} once the io-thread runs again. */ public static ChannelFuture fireChannelDisconnectedLater(final Channel channel) { return channel.getPipeline().execute(new Runnable() { public void run() { fireChannelDisconnected(channel); } }); } /** * Sends a {@code "channelDisconnected"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. */ public static void fireChannelDisconnected(Channel channel) { channel.getPipeline().sendUpstream( new UpstreamChannelStateEvent( channel, ChannelState.CONNECTED, null)); } /** * Sends a {@code "channelDisconnected"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. */ public static void fireChannelDisconnected(ChannelHandlerContext ctx) { ctx.sendUpstream(new UpstreamChannelStateEvent( ctx.getChannel(), ChannelState.CONNECTED, null)); } /** * Sends a {@code "channelUnbound"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel} once the io-thread runs again. */ public static ChannelFuture fireChannelUnboundLater(final Channel channel) { return channel.getPipeline().execute(new Runnable() { public void run() { fireChannelUnbound(channel); } }); } /** * Sends a {@code "channelUnbound"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. */ public static void fireChannelUnbound(Channel channel) { channel.getPipeline().sendUpstream(new UpstreamChannelStateEvent( channel, ChannelState.BOUND, null)); } /** * Sends a {@code "channelUnbound"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. */ public static void fireChannelUnbound(ChannelHandlerContext ctx) { ctx.sendUpstream(new UpstreamChannelStateEvent( ctx.getChannel(), ChannelState.BOUND, null)); } /** * Sends a {@code "channelClosed"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel} once the io-thread runs again. */ public static ChannelFuture fireChannelClosedLater(final Channel channel) { return channel.getPipeline().execute(new Runnable() { public void run() { fireChannelClosed(channel); } }); } /** * Sends a {@code "channelClosed"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. */ public static void fireChannelClosed(Channel channel) { channel.getPipeline().sendUpstream( new UpstreamChannelStateEvent( channel, ChannelState.OPEN, Boolean.FALSE)); // Notify the parent handler. if (channel.getParent() != null) { fireChildChannelStateChanged(channel.getParent(), channel); } } /** * Sends a {@code "channelClosed"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. */ public static void fireChannelClosed(ChannelHandlerContext ctx) { ctx.sendUpstream( new UpstreamChannelStateEvent( ctx.getChannel(), ChannelState.OPEN, Boolean.FALSE)); } /** * Sends a {@code "exceptionCaught"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel} once the io-thread runs again. */ public static ChannelFuture fireExceptionCaughtLater(final Channel channel, final Throwable cause) { return channel.getPipeline().execute(new Runnable() { public void run() { fireExceptionCaught(channel, cause); } }); } /** * Sends a {@code "exceptionCaught"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext} once the io-thread runs again. */ public static ChannelFuture fireExceptionCaughtLater(final ChannelHandlerContext ctx, final Throwable cause) { return ctx.getPipeline().execute(new Runnable() { public void run() { fireExceptionCaught(ctx, cause); } }); } /** * Sends a {@code "exceptionCaught"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. */ public static void fireExceptionCaught(Channel channel, Throwable cause) { channel.getPipeline().sendUpstream( new DefaultExceptionEvent(channel, cause)); } /** * Sends a {@code "exceptionCaught"} event to the * {@link ChannelUpstreamHandler} which is placed in the closest upstream * from the handler associated with the specified * {@link ChannelHandlerContext}. */ public static void fireExceptionCaught(ChannelHandlerContext ctx, Throwable cause) { ctx.sendUpstream(new DefaultExceptionEvent(ctx.getChannel(), cause)); } private static void fireChildChannelStateChanged( Channel channel, Channel childChannel) { channel.getPipeline().sendUpstream( new DefaultChildChannelStateEvent(channel, childChannel)); } /** * Sends a {@code "bind"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param channel the channel to bind * @param localAddress the local address to bind to * * @return the {@link ChannelFuture} which will be notified when the * bind operation is done */ public static ChannelFuture bind(Channel channel, SocketAddress localAddress) { if (localAddress == null) { throw new NullPointerException("localAddress"); } ChannelFuture future = future(channel); channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent( channel, future, ChannelState.BOUND, localAddress)); return future; } /** * Sends a {@code "bind"} request to the * {@link ChannelDownstreamHandler} which is placed in the closest * downstream from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param ctx the context * @param future the future which will be notified when the bind * operation is done * @param localAddress the local address to bind to */ public static void bind( ChannelHandlerContext ctx, ChannelFuture future, SocketAddress localAddress) { if (localAddress == null) { throw new NullPointerException("localAddress"); } ctx.sendDownstream(new DownstreamChannelStateEvent( ctx.getChannel(), future, ChannelState.BOUND, localAddress)); } /** * Sends a {@code "unbind"} request to the * {@link ChannelDownstreamHandler} which is placed in the closest * downstream from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param ctx the context * @param future the future which will be notified when the unbind * operation is done */ public static void unbind(ChannelHandlerContext ctx, ChannelFuture future) { ctx.sendDownstream(new DownstreamChannelStateEvent( ctx.getChannel(), future, ChannelState.BOUND, null)); } /** * Sends a {@code "unbind"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param channel the channel to unbind * * @return the {@link ChannelFuture} which will be notified when the * unbind operation is done */ public static ChannelFuture unbind(Channel channel) { ChannelFuture future = future(channel); channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent( channel, future, ChannelState.BOUND, null)); return future; } /** * Sends a {@code "connect"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param channel the channel to attempt a connection * @param remoteAddress the remote address to connect to * * @return the {@link ChannelFuture} which will be notified when the * connection attempt is done */ public static ChannelFuture connect(Channel channel, SocketAddress remoteAddress) { if (remoteAddress == null) { throw new NullPointerException("remoteAddress"); } ChannelFuture future = future(channel, true); channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent( channel, future, ChannelState.CONNECTED, remoteAddress)); return future; } /** * Sends a {@code "connect"} request to the * {@link ChannelDownstreamHandler} which is placed in the closest * downstream from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param ctx the context * @param future the future which will be notified when the connection * attempt is done * @param remoteAddress the remote address to connect to */ public static void connect( ChannelHandlerContext ctx, ChannelFuture future, SocketAddress remoteAddress) { if (remoteAddress == null) { throw new NullPointerException("remoteAddress"); } ctx.sendDownstream(new DownstreamChannelStateEvent( ctx.getChannel(), future, ChannelState.CONNECTED, remoteAddress)); } /** * Sends a {@code "write"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param channel the channel to write a message * @param message the message to write to the channel * * @return the {@link ChannelFuture} which will be notified when the * write operation is done */ public static ChannelFuture write(Channel channel, Object message) { return write(channel, message, null); } /** * Sends a {@code "write"} request to the * {@link ChannelDownstreamHandler} which is placed in the closest * downstream from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param ctx the context * @param future the future which will be notified when the write * operation is done */ public static void write( ChannelHandlerContext ctx, ChannelFuture future, Object message) { write(ctx, future, message, null); } /** * Sends a {@code "write"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param channel the channel to write a message * @param message the message to write to the channel * @param remoteAddress the destination of the message. * {@code null} to use the default remote address * * @return the {@link ChannelFuture} which will be notified when the * write operation is done */ public static ChannelFuture write(Channel channel, Object message, SocketAddress remoteAddress) { ChannelFuture future = future(channel); channel.getPipeline().sendDownstream( new DownstreamMessageEvent(channel, future, message, remoteAddress)); return future; } /** * Sends a {@code "write"} request to the * {@link ChannelDownstreamHandler} which is placed in the closest * downstream from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param ctx the context * @param future the future which will be notified when the write * operation is done * @param message the message to write to the channel * @param remoteAddress the destination of the message. * {@code null} to use the default remote address. */ public static void write( ChannelHandlerContext ctx, ChannelFuture future, Object message, SocketAddress remoteAddress) { ctx.sendDownstream( new DownstreamMessageEvent(ctx.getChannel(), future, message, remoteAddress)); } /** * Sends a {@code "setInterestOps"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param channel the channel to change its interestOps * @param interestOps the new interestOps * * @return the {@link ChannelFuture} which will be notified when the * interestOps is changed */ public static ChannelFuture setInterestOps(Channel channel, int interestOps) { validateInterestOps(interestOps); interestOps = filterDownstreamInterestOps(interestOps); ChannelFuture future = future(channel); channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent( channel, future, ChannelState.INTEREST_OPS, Integer.valueOf(interestOps))); return future; } /** * Sends a {@code "setInterestOps"} request to the * {@link ChannelDownstreamHandler} which is placed in the closest * downstream from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param ctx the context * @param future the future which will be notified when the interestOps is * changed. */ public static void setInterestOps( ChannelHandlerContext ctx, ChannelFuture future, int interestOps) { validateInterestOps(interestOps); interestOps = filterDownstreamInterestOps(interestOps); ctx.sendDownstream( new DownstreamChannelStateEvent( ctx.getChannel(), future, ChannelState.INTEREST_OPS, Integer.valueOf(interestOps))); } /** * Sends a {@code "disconnect"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param channel the channel to disconnect * * @return the {@link ChannelFuture} which will be notified on disconnection */ public static ChannelFuture disconnect(Channel channel) { ChannelFuture future = future(channel); channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent( channel, future, ChannelState.CONNECTED, null)); return future; } /** * Sends a {@code "disconnect"} request to the * {@link ChannelDownstreamHandler} which is placed in the closest * downstream from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param ctx the context * @param future the future which will be notified on disconnection */ public static void disconnect( ChannelHandlerContext ctx, ChannelFuture future) { ctx.sendDownstream(new DownstreamChannelStateEvent( ctx.getChannel(), future, ChannelState.CONNECTED, null)); } /** * Sends a {@code "close"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of * the specified {@link Channel}. * * @param channel the channel to close * * @return the {@link ChannelFuture} which will be notified on closure */ public static ChannelFuture close(Channel channel) { ChannelFuture future = channel.getCloseFuture(); channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent( channel, future, ChannelState.OPEN, Boolean.FALSE)); return future; } /** * Sends a {@code "close"} request to the * {@link ChannelDownstreamHandler} which is placed in the closest * downstream from the handler associated with the specified * {@link ChannelHandlerContext}. * * @param ctx the context * @param future the future which will be notified on closure */ public static void close( ChannelHandlerContext ctx, ChannelFuture future) { ctx.sendDownstream(new DownstreamChannelStateEvent( ctx.getChannel(), future, ChannelState.OPEN, Boolean.FALSE)); } private static void validateInterestOps(int interestOps) { switch (interestOps) { case Channel.OP_NONE: case Channel.OP_READ: case Channel.OP_WRITE: case Channel.OP_READ_WRITE: break; default: throw new IllegalArgumentException( "Invalid interestOps: " + interestOps); } } private static int filterDownstreamInterestOps(int interestOps) { return interestOps & ~Channel.OP_WRITE; } private Channels() { // Unused } }