diff --git a/src/main/java/org/jboss/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java b/src/main/java/org/jboss/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java index bfdd9fe28a..d144e377be 100644 --- a/src/main/java/org/jboss/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java +++ b/src/main/java/org/jboss/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java @@ -23,6 +23,7 @@ package org.jboss.netty.handler.execution; import java.util.LinkedList; +import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; @@ -68,6 +69,58 @@ import org.jboss.netty.util.internal.ConcurrentIdentityWeakKeyHashMap; * If you want the events associated with the same channel to be executed * simultaneously, please use {@link MemoryAwareThreadPoolExecutor} instead. * + *

Using a different key other than {@link Channel} to maintain event order

+ *

+ * {@link OrderedMemoryAwareThreadPoolExecutor} uses a {@link Channel} as a key + * that is used for maintaining the event execution order, as explained in the + * previous section. Alternatively, you can extend it to change its behavior. + * For example, you can change the key to the remote IP of the peer: + * + *

+ * public class RemoteAddressBasedOMATPE extends OrderedMemoryAwareThreadPoolExecutor {
+ *     ... Constructors ...
+ *
+ *     protected ConcurrentMap<Object, Executor> new ChildExecutorMap() {
+ *         // The default implementation returns a special ConcurrentMap that
+ *         // is optimized for the case where the key is Channel, so we need to
+ *         // provide more generic implementation.
+ *         return new ConcurrentHashMap<Object, Executor>
+ *     }
+ *
+ *     protected Object getChildExecutorKey(ChannelEvent e) {
+ *         // Use the IP of the remote peer as a key.
+ *         return ((InetSocketAddress) e.getChannel().getRemoteAddress()).getAddress();
+ *     }
+ *
+ *     // Make public so that you can call from anywhere.
+ *     public boolean removeChildExecutor(Object key) {
+ *         super.removeChildExecutor(key);
+ *     }
+ * }
+ * 
+ * + * Please be very careful of memory leak of the child executor map. You must + * call {@link #removeChildExecutor(Object)} when the life cycle of the key + * ends (e.g. all connections from the same IP were closed.) Also, please + * keep in mind that the key can appear again after calling {@link #removeChildExecutor(Object)} + * (e.g. a new connection could come in from the old IP.) If in doubt, prune + * the old unused keys from the child executor map periodically: + * + *
+ * RemoteAddressBasedOMATPE executor = ...;
+ *
+ * on every 3 seconds:
+ *
+ *     for (Object key: executor.getChildExecutorKeySet()) {
+ *         InetAddress ip = (InetAddress) key;
+ *         if (there is no active connection from 'ip' now &&
+ *             there has been no incoming connection from 'ip' for last 10 minutes) {
+ *
+ *             executor.removeChildExecutor(key);
+ *         }
+ *     }
+ * 
+ * * @author The Netty Project (netty-dev@lists.jboss.org) * @author Trustin Lee (tlee@redhat.com) * @author David M. Lloyd (david.lloyd@redhat.com) @@ -161,6 +214,10 @@ public class OrderedMemoryAwareThreadPoolExecutor extends return e.getChannel(); } + protected Set getChildExecutorKeySet() { + return childExecutors.keySet(); + } + protected boolean removeChildExecutor(Object key) { return childExecutors.remove(key) != null; }