diff --git a/src/main/java/org/jboss/netty/channel/group/ChannelGroup.java b/src/main/java/org/jboss/netty/channel/group/ChannelGroup.java index f0ba53424c..a153757d9c 100644 --- a/src/main/java/org/jboss/netty/channel/group/ChannelGroup.java +++ b/src/main/java/org/jboss/netty/channel/group/ChannelGroup.java @@ -25,9 +25,54 @@ package org.jboss.netty.channel.group; import java.net.SocketAddress; import java.util.Set; +import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ServerChannel; /** + * A thread-safe {@link Set} that contains open {@link Channel}s and provides + * various bulk operations on them. Using {@link ChannelGroup}, you can + * categorize {@link Channel}s into a meaningful group (e.g. on a per-service + * or per-state basis.) A closed {@link Channel} is automatically removed from + * the collection, so that you don't need to worry about the life cycle of the + * added {@link Channel}. A {@link Channel} can belong to more than one + * {@link ChannelGroup}. + * + *
+ * If both {@link ServerChannel}s and non-{@link ServerChannel}s exists in the + * same {@link ChannelGroup}, any requested I/O operations on the group are + * performed for the {@link ServerChannel}s first and then for the others. + *
+ * This rule is very useful when you shut down a server in one shot: + * + *
+ * ChannelGroup allChannels = new DefaultChannelGroup(); + * + * public static void main(String[] args) throws Exception { + * ServerBootstrap b = new ServerBootstrap(..); + * ... + * + * // Start the server + * b.getPipeline().addLast("handler", new MyHandler()); + * Channel serverChannel = b.bind(..); + * + * ... Wait until the shutdown signal reception ... + * + * // Close the serverChannel and then all accepted connections. + * allChannels.close().awaitUninterruptibly(); + * b.releaseExternalResources(); + * } + * + * public class MyHandler extends SimpleChannelUpstreamHandler { + * public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) { + * // Add all open channels to the global group so that they are + * // closed on shutdown. + * allChannels.add(e.getChannel()); + * } + * } + *+ * * @author The Netty Project (netty-dev@lists.jboss.org) * @author Trustin Lee (tlee@redhat.com) * @version $Rev$, $Date$ @@ -36,13 +81,89 @@ import org.jboss.netty.channel.Channel; * @apiviz.has org.jboss.netty.channel.group.ChannelGroupFuture oneway - - returns */ public interface ChannelGroup extends Set
+ * All I/O operations in {@link ChannelGroup} are asynchronous. It means any + * I/O calls will return immediately with no guarantee that the requested I/O + * operations have been completed at the end of the call. Instead, you will be + * returned with a {@link ChannelGroupFuture} instance which tells you when the + * requested I/O operations have succeeded, failed, or cancelled. + *
+ * Various methods are provided to let you check if the I/O operations has been + * completed, wait for the completion, and retrieve the result of the I/O + * operation. It also allows you to add more than one + * {@link ChannelGroupFutureListener} so you can get notified when the I/O + * operation have been completed. + * + *
+ * {@link #addListener(ChannelGroupFutureListener)} is non-blocking. It simply + * adds the specified {@link ChannelGroupFutureListener} to the + * {@link ChannelGroupFuture}, and I/O thread will notify the listeners when + * the I/O operations associated with the future is done. + * {@link ChannelGroupFutureListener} yields the best performance and resource + * utilization because it does not block at all, but it could be tricky to + * implement a sequential logic if you are not used to event-driven programming. + *
+ * By contrast, {@link #await()} is a blocking operation. Once called, the + * caller thread blocks until all I/O operations are done. It is easier to + * implement a sequential logic with {@link #await()}, but the caller thread + * blocks unnecessarily until all I/O operations are done and there's relatively + * expensive cost of inter-thread notification. Moreover, there's a chance of + * dead lock in a particular circumstance, which is described below. + * + *
+ * The event handler methods in {@link ChannelHandler} is often called by + * an I/O thread unless an {@link ExecutionHandler} is in the + * {@link ChannelPipeline}. If {@link #await()} is called by an event handler + * method, which is called by the I/O thread, the I/O operation it is waiting + * for might never be complete because {@link #await()} can block the I/O + * operation it is waiting for, which is a dead lock. + *
+ * // BAD - NEVER DO THIS + * public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { + * if (e.getMessage() instanceof ShutdownMessage) { + * ChannelGroup allChannels = MyServer.getAllChannels(); + * ChannelGroupFuture future = allChannels.close(); + * future.awaitUninterruptibly(); + * // Perform post-shutdown operation + * // ... + * } + * } + * + * // GOOD + * public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { + * if (e.getMessage() instanceof ShutdownMessage) { + * ChannelGroup allChannels = MyServer.getAllChannels(); + * ChannelGroupFuture future = allChannels.close(); + * future.addListener(new ChannelGroupFutureListener() { + * public void operationComplete(ChannelGroupFuture future) { + * // Perform post-closure operation + * // ... + * } + * }); + * } + * } + *+ * * @author The Netty Project (netty-dev@lists.jboss.org) * @author Trustin Lee (tlee@redhat.com) * @version $Rev$, $Date$ @@ -36,9 +111,28 @@ import org.jboss.netty.channel.ChannelFuture; */ public interface ChannelGroupFuture extends Iterable