All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xnio.XnioWorker Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 *
 * Copyright 2011 Red Hat, Inc. and/or its affiliates, and individual
 * contributors as indicated by the @author tags.
 *
 * Licensed 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.xnio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

import org.jboss.threads.EnhancedQueueExecutor;
import org.wildfly.common.Assert;
import org.wildfly.common.context.ContextManager;
import org.wildfly.common.context.Contextual;
import org.wildfly.common.net.CidrAddress;
import org.wildfly.common.net.CidrAddressTable;
import org.xnio._private.Messages;
import org.xnio.channels.AcceptingChannel;
import org.xnio.channels.AssembledConnectedMessageChannel;
import org.xnio.channels.AssembledConnectedStreamChannel;
import org.xnio.channels.BoundChannel;
import org.xnio.channels.Configurable;
import org.xnio.channels.ConnectedMessageChannel;
import org.xnio.channels.ConnectedStreamChannel;
import org.xnio.channels.MulticastMessageChannel;
import org.xnio.channels.StreamChannel;
import org.xnio.channels.StreamSinkChannel;
import org.xnio.channels.StreamSourceChannel;
import org.xnio.conduits.ConduitStreamSinkChannel;
import org.xnio.conduits.ConduitStreamSourceChannel;
import org.xnio.conduits.DeflatingStreamSinkConduit;
import org.xnio.conduits.InflatingStreamSourceConduit;
import org.xnio.conduits.StreamSinkChannelWrappingConduit;
import org.xnio.conduits.StreamSourceChannelWrappingConduit;
import org.xnio.management.XnioServerMXBean;
import org.xnio.management.XnioWorkerMXBean;

import static java.lang.Math.max;
import static java.security.AccessController.doPrivileged;
import org.jboss.logging.Logger;
import static org.xnio.IoUtils.safeClose;
import static org.xnio._private.Messages.msg;

/**
 * A worker for I/O channel notification.
 *
 * @author David M. Lloyd
 *
 * @since 3.0
 */
@SuppressWarnings("unused")
public abstract class XnioWorker extends AbstractExecutorService implements Configurable, ExecutorService, XnioIoFactory, Contextual {

    private final Xnio xnio;
    private final TaskPool taskPool;
    private final String name;
    private final Runnable terminationTask;
    private final CidrAddressTable bindAddressTable;

    private volatile int taskSeq;

    private static final AtomicIntegerFieldUpdater taskSeqUpdater = AtomicIntegerFieldUpdater.newUpdater(XnioWorker.class, "taskSeq");

    private static final AtomicInteger seq = new AtomicInteger(1);

    private static final RuntimePermission CREATE_WORKER_PERMISSION = new RuntimePermission("createXnioWorker");

    private int getNextSeq() {
        return taskSeqUpdater.incrementAndGet(this);
    }

    private static final Logger log = Logger.getLogger("org.xnio");

    /**
     * Construct a new instance.  Intended to be called only from implementations.
     *
     * @param builder the worker builder
     */
    protected XnioWorker(final Builder builder) {
        this.xnio = builder.xnio;
        this.terminationTask = builder.terminationTask;
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(CREATE_WORKER_PERMISSION);
        }
        String workerName = builder.getWorkerName();
        if (workerName == null) {
            workerName = "XNIO-" + seq.getAndIncrement();
        }
        name = workerName;
        final boolean markThreadAsDaemon = builder.isDaemon();
        bindAddressTable = builder.getBindAddressConfigurations();
        final Runnable terminationTask = new Runnable() {
            public void run() {
                taskPoolTerminated();
            }
        };
        final ExecutorService executorService = builder.getExternalExecutorService();
        if (executorService != null) {
            if (executorService instanceof EnhancedQueueExecutor) {
                taskPool = new ExternalTaskPool(
                        new EnhancedQueueExecutorTaskPool((EnhancedQueueExecutor) executorService));
            } else if (executorService instanceof ThreadPoolExecutor) {
                taskPool = new ExternalTaskPool(new ThreadPoolExecutorTaskPool((ThreadPoolExecutor) executorService));
            } else {
                taskPool = new ExternalTaskPool(new ExecutorServiceTaskPool(executorService));
            }
        } else if (EnhancedQueueExecutor.DISABLE_HINT) {
            final int poolSize = max(builder.getMaxWorkerPoolSize(), builder.getCoreWorkerPoolSize());
            taskPool = new ThreadPoolExecutorTaskPool(new DefaultThreadPoolExecutor(
                poolSize,
                poolSize,
                builder.getWorkerKeepAlive(), TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<>(),
                new WorkerThreadFactory(builder.getThreadGroup(), builder.getWorkerStackSize(), markThreadAsDaemon),
                terminationTask));
        } else {
            taskPool = new EnhancedQueueExecutorTaskPool(new EnhancedQueueExecutor.Builder()
                .setCorePoolSize(builder.getCoreWorkerPoolSize())
                .setMaximumPoolSize(builder.getMaxWorkerPoolSize())
                .setKeepAliveTime(builder.getWorkerKeepAlive(), TimeUnit.MILLISECONDS)
                .setThreadFactory(new WorkerThreadFactory(builder.getThreadGroup(), builder.getWorkerStackSize(), markThreadAsDaemon))
                .setTerminationTask(terminationTask)
                .setRegisterMBean(true)
                .setMBeanName(workerName)
                .build()
            );
        }
    }

    //==================================================
    //
    // Context methods
    //
    //==================================================

    private static final ContextManager CONTEXT_MANAGER = doPrivileged((PrivilegedAction>) () -> new ContextManager(XnioWorker.class, "org.xnio.worker"));

    static {
        doPrivileged((PrivilegedAction) () -> {
            CONTEXT_MANAGER.setGlobalDefaultSupplier(() -> DefaultXnioWorkerHolder.INSTANCE);
            return null;
        });
    }

    /**
     * Get the context manager for XNIO workers.
     *
     * @return the context manager (not {@code null})
     */
    public static ContextManager getContextManager() {
        return CONTEXT_MANAGER;
    }

    /**
     * Get the instance context manager for XNIO workers by delegating to {@link #getContextManager()}.
     *
     * @return the context manager (not {@code null})
     */
    public ContextManager getInstanceContextManager() {
        return getContextManager();
    }

    //==================================================
    //
    // Stream methods
    //
    //==================================================

    // Servers

    /**
     * Create a stream server, for TCP or UNIX domain servers.  The type of server is determined by the bind address.
     *
     * @param bindAddress the address to bind to
     * @param acceptListener the initial accept listener
     * @param optionMap the initial configuration for the server
     * @return the acceptor
     * @throws IOException if the server could not be created
     */
    @Deprecated
    public AcceptingChannel createStreamServer(SocketAddress bindAddress, ChannelListener> acceptListener, OptionMap optionMap) throws IOException {
        final AcceptingChannel server = createStreamConnectionServer(bindAddress, null, optionMap);
        final AcceptingChannel acceptingChannel = new AcceptingChannel() {
            public ConnectedStreamChannel accept() throws IOException {
                final StreamConnection connection = server.accept();
                return connection == null ? null : new AssembledConnectedStreamChannel(connection, connection.getSourceChannel(), connection.getSinkChannel());
            }

            public ChannelListener.Setter> getAcceptSetter() {
                return ChannelListeners.getDelegatingSetter(server.getAcceptSetter(), this);
            }

            public ChannelListener.Setter> getCloseSetter() {
                return ChannelListeners.getDelegatingSetter(server.getCloseSetter(), this);
            }

            public SocketAddress getLocalAddress() {
                return server.getLocalAddress();
            }

            public  A getLocalAddress(final Class type) {
                return server.getLocalAddress(type);
            }

            public void suspendAccepts() {
                server.suspendAccepts();
            }

            public void resumeAccepts() {
                server.resumeAccepts();
            }

            public boolean isAcceptResumed() {
                return server.isAcceptResumed();
            }

            public void wakeupAccepts() {
                server.wakeupAccepts();
            }

            public void awaitAcceptable() throws IOException {
                server.awaitAcceptable();
            }

            public void awaitAcceptable(final long time, final TimeUnit timeUnit) throws IOException {
                server.awaitAcceptable(time, timeUnit);
            }

            public XnioWorker getWorker() {
                return server.getWorker();
            }

            @Deprecated
            public XnioExecutor getAcceptThread() {
                return server.getAcceptThread();
            }

            public XnioIoThread getIoThread() {
                return server.getIoThread();
            }

            public void close() throws IOException {
                server.close();
            }

            public boolean isOpen() {
                return server.isOpen();
            }

            public boolean supportsOption(final Option option) {
                return server.supportsOption(option);
            }

            public  T getOption(final Option option) throws IOException {
                return server.getOption(option);
            }

            public  T setOption(final Option option, final T value) throws IllegalArgumentException, IOException {
                return server.setOption(option, value);
            }
        };
        acceptingChannel.getAcceptSetter().set(acceptListener);
        return acceptingChannel;
    }

    /**
     * Create a stream server, for TCP or UNIX domain servers.  The type of server is determined by the bind address.
     *
     * @param bindAddress the address to bind to
     * @param acceptListener the initial accept listener
     * @param optionMap the initial configuration for the server
     * @return the acceptor
     * @throws IOException if the server could not be created
     */
    public AcceptingChannel createStreamConnectionServer(SocketAddress bindAddress, ChannelListener> acceptListener, OptionMap optionMap) throws IOException {
        Assert.checkNotNullParam("bindAddress", bindAddress);
        if (bindAddress instanceof InetSocketAddress) {
            return createTcpConnectionServer((InetSocketAddress) bindAddress, acceptListener, optionMap);
        } else if (bindAddress instanceof LocalSocketAddress) {
            return createLocalStreamConnectionServer((LocalSocketAddress) bindAddress, acceptListener, optionMap);
        } else {
            throw msg.badSockType(bindAddress.getClass());
        }
    }

    /**
     * Implementation helper method to create a TCP stream server.
     *
     * @param bindAddress the address to bind to
     * @param acceptListener the initial accept listener
     * @param optionMap the initial configuration for the server
     * @return the acceptor
     * @throws IOException if the server could not be created
     */
    protected AcceptingChannel createTcpConnectionServer(InetSocketAddress bindAddress, ChannelListener> acceptListener, OptionMap optionMap) throws IOException {
        throw msg.unsupported("createTcpConnectionServer");
    }

    /**
     * Implementation helper method to create a UNIX domain stream server.
     *
     * @param bindAddress the address to bind to
     * @param acceptListener the initial accept listener
     * @param optionMap the initial configuration for the server
     * @return the acceptor
     * @throws IOException if the server could not be created
     */
    protected AcceptingChannel createLocalStreamConnectionServer(LocalSocketAddress bindAddress, ChannelListener> acceptListener, OptionMap optionMap) throws IOException {
        throw msg.unsupported("createLocalStreamConnectionServer");
    }

    // Connectors

    /**
     * Connect to a remote stream server.  The protocol family is determined by the type of the socket address given.
     *
     * @param destination the destination address
     * @param openListener the listener which will be notified when the channel is open, or {@code null} for none
     * @param optionMap the option map
     * @return the future result of this operation
     */
    @Deprecated
    public IoFuture connectStream(SocketAddress destination, ChannelListener openListener, OptionMap optionMap) {
        final FutureResult futureResult = new FutureResult();
        final ChannelListener nestedOpenListener = new StreamConnectionWrapListener(futureResult, openListener);
        final IoFuture future = openStreamConnection(destination, nestedOpenListener, optionMap);
        future.addNotifier(STREAM_WRAPPING_HANDLER, futureResult);
        futureResult.addCancelHandler(future);
        return futureResult.getIoFuture();
    }

    /**
     * Connect to a remote stream server.  The protocol family is determined by the type of the socket address given.
     *
     * @param destination the destination address
     * @param openListener the listener which will be notified when the channel is open, or {@code null} for none
     * @param bindListener the listener which will be notified when the channel is bound, or {@code null} for none
     * @param optionMap the option map
     * @return the future result of this operation
     */
    @Deprecated
    public IoFuture connectStream(SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        final FutureResult futureResult = new FutureResult();
        final ChannelListener nestedOpenListener = new StreamConnectionWrapListener(futureResult, openListener);
        final IoFuture future = openStreamConnection(destination, nestedOpenListener, bindListener, optionMap);
        future.addNotifier(STREAM_WRAPPING_HANDLER, futureResult);
        futureResult.addCancelHandler(future);
        return futureResult.getIoFuture();
    }

    /**
     * Connect to a remote stream server.  The protocol family is determined by the type of the socket addresses given
     * (which must match).
     *
     * @param bindAddress the local address to bind to
     * @param destination the destination address
     * @param openListener the listener which will be notified when the channel is open, or {@code null} for none
     * @param bindListener the listener which will be notified when the channel is bound, or {@code null} for none
     * @param optionMap the option map
     * @return the future result of this operation
     */
    @Deprecated
    public IoFuture connectStream(SocketAddress bindAddress, SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        final FutureResult futureResult = new FutureResult();
        final ChannelListener nestedOpenListener = new StreamConnectionWrapListener(futureResult, openListener);
        final IoFuture future = openStreamConnection(bindAddress, destination, nestedOpenListener, bindListener, optionMap);
        future.addNotifier(STREAM_WRAPPING_HANDLER, futureResult);
        futureResult.addCancelHandler(future);
        return futureResult.getIoFuture();
    }

    public IoFuture openStreamConnection(SocketAddress destination, ChannelListener openListener, OptionMap optionMap) {
        return chooseThread().openStreamConnection(destination, openListener, optionMap);
    }

    public IoFuture openStreamConnection(SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        return chooseThread().openStreamConnection(destination, openListener, bindListener, optionMap);
    }

    public IoFuture openStreamConnection(SocketAddress bindAddress, SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        return chooseThread().openStreamConnection(bindAddress, destination, openListener, bindListener, optionMap);
    }

    // Acceptors

    /**
     * Accept a stream connection at a destination address.  If a wildcard address is specified, then a destination address
     * is chosen in a manner specific to the OS and/or channel type.
     *
     * @param destination the destination (bind) address
     * @param openListener the listener which will be notified when the channel is open, or {@code null} for none
     * @param bindListener the listener which will be notified when the acceptor is bound, or {@code null} for none
     * @param optionMap the option map
     * @return the future connection
     */
    @Deprecated
    public IoFuture acceptStream(SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        final FutureResult futureResult = new FutureResult();
        final ChannelListener nestedOpenListener = new StreamConnectionWrapListener(futureResult, openListener);
        final IoFuture future = acceptStreamConnection(destination, nestedOpenListener, bindListener, optionMap);
        future.addNotifier(STREAM_WRAPPING_HANDLER, futureResult);
        futureResult.addCancelHandler(future);
        return futureResult.getIoFuture();
    }

    public IoFuture acceptStreamConnection(SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        return chooseThread().acceptStreamConnection(destination, openListener, bindListener, optionMap);
    }

    //==================================================
    //
    // Message (datagram) channel methods
    //
    //==================================================

    /**
     * Connect to a remote datagram server.  The protocol family is determined by the type of the socket address given.
     *
     * @param destination the destination address
     * @param openListener the listener which will be notified when the channel is open, or {@code null} for none
     * @param bindListener the listener which will be notified when the channel is bound, or {@code null} for none
     * @param optionMap the option map
     * @return the future result of this operation
     */
    @Deprecated
    // FIXME XNIO-192 invoke bind listener
    public IoFuture connectDatagram(SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        final FutureResult futureResult = new FutureResult();
        final ChannelListener nestedOpenListener = new MessageConnectionWrapListener(futureResult, openListener);
        final IoFuture future = openMessageConnection(destination, nestedOpenListener, optionMap);
        future.addNotifier(MESSAGE_WRAPPING_HANDLER, futureResult);
        futureResult.addCancelHandler(future);
        return futureResult.getIoFuture();
    }

    /**
     * Connect to a remote datagram server.  The protocol family is determined by the type of the socket addresses given
     * (which must match).
     *
     * @param bindAddress the local address to bind to
     * @param destination the destination address
     * @param openListener the listener which will be notified when the channel is open, or {@code null} for none
     * @param bindListener the listener which will be notified when the channel is bound, or {@code null} for none
     * @param optionMap the option map
     * @return the future result of this operation
     */
    @Deprecated
    // FIXME bindAddress is now ignored
    public IoFuture connectDatagram(SocketAddress bindAddress, SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        final FutureResult futureResult = new FutureResult();
        final ChannelListener nestedOpenListener = new MessageConnectionWrapListener(futureResult, openListener);
        final IoFuture future = openMessageConnection(destination, nestedOpenListener, optionMap);
        future.addNotifier(MESSAGE_WRAPPING_HANDLER, futureResult);
        futureResult.addCancelHandler(future);
        return futureResult.getIoFuture();
    }

    public IoFuture openMessageConnection(final SocketAddress destination, final ChannelListener openListener, final OptionMap optionMap) {
        return chooseThread().openMessageConnection(destination, openListener, optionMap);
    }

    // Acceptors

    /**
     * Accept a message connection at a destination address.  If a wildcard address is specified, then a destination address
     * is chosen in a manner specific to the OS and/or channel type.
     *
     * @param destination the destination (bind) address
     * @param openListener the listener which will be notified when the channel is open, or {@code null} for none
     * @param bindListener the listener which will be notified when the acceptor is bound, or {@code null} for none
     * @param optionMap the option map
     * @return the future connection
     */
    @Deprecated
    public IoFuture acceptDatagram(SocketAddress destination, ChannelListener openListener, ChannelListener bindListener, OptionMap optionMap) {
        final FutureResult futureResult = new FutureResult();
        final ChannelListener nestedOpenListener = new MessageConnectionWrapListener(futureResult, openListener);
        final IoFuture future = acceptMessageConnection(destination, nestedOpenListener, bindListener, optionMap);
        future.addNotifier(MESSAGE_WRAPPING_HANDLER, futureResult);
        futureResult.addCancelHandler(future);
        return futureResult.getIoFuture();
    }

    public IoFuture acceptMessageConnection(final SocketAddress destination, final ChannelListener openListener, final ChannelListener bindListener, final OptionMap optionMap) {
        return chooseThread().acceptMessageConnection(destination, openListener, bindListener, optionMap);
    }

    //==================================================
    //
    // UDP methods
    //
    //==================================================

    /**
     * Create a UDP server.  The UDP server can be configured to be multicast-capable; this should only be
     * done if multicast is needed, since some providers have a performance penalty associated with multicast.
     * The provider's default executor will be used to execute listener methods.
     *
     * @param bindAddress the bind address
     * @param bindListener the initial open-connection listener
     * @param optionMap the initial configuration for the server
     * @return the UDP server channel
     * @throws java.io.IOException if the server could not be created
     *
     * @since 3.0
     */
    public MulticastMessageChannel createUdpServer(InetSocketAddress bindAddress, ChannelListener bindListener, OptionMap optionMap) throws IOException {
        throw msg.unsupported("createUdpServer");
    }

    /**
     * Create a UDP server.  The UDP server can be configured to be multicast-capable; this should only be
     * done if multicast is needed, since some providers have a performance penalty associated with multicast.
     * The provider's default executor will be used to execute listener methods.
     *
     * @param bindAddress the bind address
     * @param optionMap the initial configuration for the server
     * @return the UDP server channel
     * @throws java.io.IOException if the server could not be created
     *
     * @since 3.0
     */
    public MulticastMessageChannel createUdpServer(InetSocketAddress bindAddress, OptionMap optionMap) throws IOException {
        return createUdpServer(bindAddress, ChannelListeners.nullChannelListener(), optionMap);
    }

    //==================================================
    //
    // Stream pipe methods
    //
    //==================================================

    /**
     * Open a bidirectional stream pipe.
     *
     * @param leftOpenListener the left-hand open listener
     * @param rightOpenListener the right-hand open listener
     * @param optionMap the pipe channel configuration
     * @throws java.io.IOException if the pipe could not be created
     * @deprecated Users should prefer the simpler {@link #createFullDuplexPipe()} instead.
     */
    @Deprecated
    public void createPipe(ChannelListener leftOpenListener, ChannelListener rightOpenListener, final OptionMap optionMap) throws IOException {
        final ChannelPipe pipe = createFullDuplexPipe();
        final boolean establishWriting = optionMap.get(Options.WORKER_ESTABLISH_WRITING, false);
        final StreamChannel left = pipe.getLeftSide();
        XnioExecutor leftExec = establishWriting ? left.getWriteThread() : left.getReadThread();
        final StreamChannel right = pipe.getRightSide();
        XnioExecutor rightExec = establishWriting ? right.getWriteThread() : right.getReadThread();
        // not unsafe - http://youtrack.jetbrains.net/issue/IDEA-59290
        //noinspection unchecked
        leftExec.execute(ChannelListeners.getChannelListenerTask(left, leftOpenListener));
        // not unsafe - http://youtrack.jetbrains.net/issue/IDEA-59290
        //noinspection unchecked
        rightExec.execute(ChannelListeners.getChannelListenerTask(right, rightOpenListener));
    }

    /**
     * Open a unidirectional stream pipe.
     *
     * @param sourceListener the source open listener
     * @param sinkListener the sink open listener
     * @param optionMap the pipe channel configuration
     * @throws java.io.IOException if the pipe could not be created
     * @deprecated Users should prefer the simpler {@link #createHalfDuplexPipe()} instead.
     */
    @Deprecated
    public void createOneWayPipe(ChannelListener sourceListener, ChannelListener sinkListener, final OptionMap optionMap) throws IOException {
        final ChannelPipe pipe = createHalfDuplexPipe();
        final StreamSourceChannel left = pipe.getLeftSide();
        XnioExecutor leftExec = left.getReadThread();
        final StreamSinkChannel right = pipe.getRightSide();
        XnioExecutor rightExec = right.getWriteThread();
        // not unsafe - http://youtrack.jetbrains.net/issue/IDEA-59290
        //noinspection unchecked
        leftExec.execute(ChannelListeners.getChannelListenerTask(left, sourceListener));
        // not unsafe - http://youtrack.jetbrains.net/issue/IDEA-59290
        //noinspection unchecked
        rightExec.execute(ChannelListeners.getChannelListenerTask(right, sinkListener));
    }

    //==================================================
    //
    // Compression methods
    //
    //==================================================

    /**
     * Create a stream channel that decompresses the source data according to the configuration in the given option map.
     *
     * @param delegate the compressed channel
     * @param options the configuration options for the channel
     * @return a decompressed channel
     * @throws IOException if the channel could not be constructed
     */
    public StreamSourceChannel getInflatingChannel(final StreamSourceChannel delegate, OptionMap options) throws IOException {
        final boolean nowrap;
        switch (options.get(Options.COMPRESSION_TYPE, CompressionType.DEFLATE)) {
            case DEFLATE: nowrap = false; break;
            case GZIP: nowrap = true; break;
            default: throw msg.badCompressionFormat();
        }
        return getInflatingChannel(delegate, new Inflater(nowrap));
    }

    /**
     * Create a stream channel that decompresses the source data according to the configuration in the given inflater.
     *
     * @param delegate the compressed channel
     * @param inflater the inflater to use
     * @return a decompressed channel
     * @throws IOException if the channel could not be constructed
     */
    protected StreamSourceChannel getInflatingChannel(final StreamSourceChannel delegate, final Inflater inflater) throws IOException {
        return new ConduitStreamSourceChannel(Configurable.EMPTY, new InflatingStreamSourceConduit(new StreamSourceChannelWrappingConduit(delegate), inflater));
    }

    /**
     * Create a stream channel that compresses to the destination according to the configuration in the given option map.
     *
     * @param delegate the channel to compress to
     * @param options the configuration options for the channel
     * @return a compressed channel
     * @throws IOException if the channel could not be constructed
     */
    public StreamSinkChannel getDeflatingChannel(final StreamSinkChannel delegate, final OptionMap options) throws IOException {
        final int level = options.get(Options.COMPRESSION_LEVEL, -1);
        final boolean nowrap;
        switch (options.get(Options.COMPRESSION_TYPE, CompressionType.DEFLATE)) {
            case DEFLATE: nowrap = false; break;
            case GZIP: nowrap = true; break;
            default: throw msg.badCompressionFormat();
        }
        return getDeflatingChannel(delegate, new Deflater(level, nowrap));
    }

    /**
     * Create a stream channel that compresses to the destination according to the configuration in the given inflater.
     *
     * @param delegate the channel to compress to
     * @param deflater the deflater to use
     * @return a compressed channel
     * @throws IOException if the channel could not be constructed
     */
    protected StreamSinkChannel getDeflatingChannel(final StreamSinkChannel delegate, final Deflater deflater) throws IOException {
        return new ConduitStreamSinkChannel(Configurable.EMPTY, new DeflatingStreamSinkConduit(new StreamSinkChannelWrappingConduit(delegate), deflater));
    }

    public ChannelPipe createFullDuplexPipe() throws IOException {
        return chooseThread().createFullDuplexPipe();
    }

    public ChannelPipe createFullDuplexPipeConnection() throws IOException {
        return chooseThread().createFullDuplexPipeConnection();
    }

    public ChannelPipe createHalfDuplexPipe() throws IOException {
        return chooseThread().createHalfDuplexPipe();
    }

    public ChannelPipe createFullDuplexPipeConnection(final XnioIoFactory peer) throws IOException {
        return chooseThread().createFullDuplexPipeConnection(peer);
    }

    public ChannelPipe createHalfDuplexPipe(final XnioIoFactory peer) throws IOException {
        return chooseThread().createHalfDuplexPipe(peer);
    }

    //==================================================
    //
    // State methods
    //
    //==================================================

    /**
     * Shut down this worker.  This method returns immediately.  Upon return worker shutdown will have
     * commenced but not necessarily completed.  When worker shutdown is complete, the termination task (if one was
     * defined) will be executed.
     */
    public abstract void shutdown();

    /**
     * Immediately terminate the worker.  Any outstanding tasks are collected and returned in a list.  Upon return
     * worker shutdown will have commenced but not necessarily completed; however the worker will only complete its
     * current tasks instead of completing all tasks.
     *
     * @return the list of outstanding tasks
     */
    public abstract List shutdownNow();

    /**
     * Determine whether the worker has been shut down.  Will return {@code true} once either shutdown method has
     * been called.
     *
     * @return {@code true} the worker has been shut down
     */
    public abstract boolean isShutdown();

    /**
     * Determine whether the worker has terminated.  Will return {@code true} once all worker threads are exited
     * (with the possible exception of the thread running the termination task, if any).
     *
     * @return {@code true} if the worker is terminated
     */
    public abstract boolean isTerminated();

    /**
     * Wait for termination.
     *
     * @param timeout the amount of time to wait
     * @param unit the unit of time
     * @return {@code true} if termination completed before the timeout expired
     * @throws InterruptedException if the operation was interrupted
     */
    public abstract boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException;

    /**
     * Wait for termination.
     *
     * @throws InterruptedException if the operation was interrupted
     */
    public abstract void awaitTermination() throws InterruptedException;

    //==================================================
    //
    // Thread pool methods
    //
    //==================================================

    /**
     * Get an I/O thread from this worker.  The thread may be chosen based on arbitrary rules.
     *
     * @return the I/O thread
     */
    public final XnioIoThread getIoThread() {
        return chooseThread();
    }

    /**
     * Get an I/O thread from this worker.  The thread is chosen based on the given hash code.
     *
     * @param hashCode the hash code
     * @return the thread
     */
    public abstract XnioIoThread getIoThread(int hashCode);

    /**
     * Get the user task to run once termination is complete.
     *
     * @return the termination task
     */
    protected Runnable getTerminationTask() {
        return terminationTask;
    }

    /**
     * Callback to indicate that the task thread pool has terminated.  Not called if the task pool is external.
     */
    protected void taskPoolTerminated() {}

    /**
     * Initiate shutdown of the task thread pool.  When all the tasks and threads have completed,
     * the {@link #taskPoolTerminated()} method is called.
     */
    protected void shutDownTaskPool() {
        if (isTaskPoolExternal()) {
            taskPoolTerminated();
        } else {
            doPrivileged(new PrivilegedAction() {
                public Object run() {
                    taskPool.shutdown();
                    return null;
                }
            });
        }
    }

    /**
     * Shut down the task thread pool immediately and return its pending tasks.
     *
     * @return the pending task list
     */
    protected List shutDownTaskPoolNow() {
        if (! isTaskPoolExternal()) return doPrivileged(new PrivilegedAction>() {
            public List run() {
                return taskPool.shutdownNow();
            }
        });
        return Collections.emptyList();
    }

    /**
     * Determine whether the worker task pool is managed externally.  Externally managed task pools will never
     * respond to shut down requests.
     *
     * @return {@code true} if the task pool is externally managed, {@code false} otherwise
     */
    protected boolean isTaskPoolExternal() {
        return taskPool instanceof ExternalTaskPool;
    }

    /**
     * Execute a command in the task pool.
     *
     * @param command the command to run
     */
    public void execute(final Runnable command) {
        taskPool.execute(command);
    }

    /**
     * Get the number of I/O threads configured on this worker.
     *
     * @return the number of I/O threads configured on this worker
     */
    public abstract int getIoThreadCount();

    //==================================================
    //
    // Configuration methods
    //
    //==================================================

    private final static Set> OPTIONS = Option.setBuilder()
            .add(Options.WORKER_TASK_CORE_THREADS)
            .add(Options.WORKER_TASK_MAX_THREADS)
            .add(Options.WORKER_TASK_KEEPALIVE)
            .create();

    private final static Set> EXTERNAL_POOL_OPTIONS = Option.setBuilder()
            .create();

    public boolean supportsOption(final Option option) {
        return taskPool instanceof ExternalTaskPool ? EXTERNAL_POOL_OPTIONS.contains(option) : OPTIONS.contains(option);
    }

    public  T getOption(final Option option) throws IOException {
        if (! supportsOption(option)) {
            return null;
        } else if (option.equals(Options.WORKER_TASK_CORE_THREADS)) {
            return option.cast(Integer.valueOf(taskPool.getCorePoolSize()));
        } else if (option.equals(Options.WORKER_TASK_MAX_THREADS)) {
            return option.cast(Integer.valueOf(taskPool.getMaximumPoolSize()));
        } else if (option.equals(Options.WORKER_TASK_KEEPALIVE)) {
            return option.cast(Integer.valueOf((int) Math.min((long) Integer.MAX_VALUE, taskPool.getKeepAliveTime(TimeUnit.MILLISECONDS))));
        } else {
            return null;
        }
    }

    public  T setOption(final Option option, final T value) throws IllegalArgumentException, IOException {
        if (! supportsOption(option)) {
            return null;
        } else if (option.equals(Options.WORKER_TASK_CORE_THREADS)) {
            final int old = taskPool.getCorePoolSize();
            taskPool.setCorePoolSize(Options.WORKER_TASK_CORE_THREADS.cast(value).intValue());
            return option.cast(Integer.valueOf(old));
        } else if (option.equals(Options.WORKER_TASK_MAX_THREADS)) {
            final int old = taskPool.getMaximumPoolSize();
            taskPool.setMaximumPoolSize(Options.WORKER_TASK_MAX_THREADS.cast(value).intValue());
            return option.cast(Integer.valueOf(old));
        } else if (option.equals(Options.WORKER_TASK_KEEPALIVE)) {
            final long old = taskPool.getKeepAliveTime(TimeUnit.MILLISECONDS);
            taskPool.setKeepAliveTime(Options.WORKER_TASK_KEEPALIVE.cast(value).intValue(), TimeUnit.MILLISECONDS);
            return option.cast(Integer.valueOf((int) Math.min((long) Integer.MAX_VALUE, old)));
        } else {
            return null;
        }
    }

    //==================================================
    //
    // Accessor methods
    //
    //==================================================

    /**
     * Get the XNIO provider which produced this worker.
     *
     * @return the XNIO provider
     */
    public Xnio getXnio() {
        return xnio;
    }

    /**
     * Get the name of this worker.
     *
     * @return the name of the worker
     */
    public String getName() {
        return name;
    }

    //==================================================
    //
    // SPI methods
    //
    //==================================================

    /**
     * Choose a thread randomly from this worker.
     *
     * @return the thread
     */
    protected abstract XnioIoThread chooseThread();

    /**
     * Get the core worker pool size.
     *
     * @return the core worker pool size
     */
    protected final int getCoreWorkerPoolSize() {
        return taskPool.getCorePoolSize();
    }

    /**
     * Get an estimate of the number of busy threads in the worker pool.
     *
     * @return the estimated number of busy threads in the worker pool
     */
    protected final int getBusyWorkerThreadCount() {
        return taskPool.getActiveCount();
    }

    /**
     * Get an estimate of the number of threads in the worker pool.
     *
     * @return the estimated number of threads in the worker pool
     */
    protected final int getWorkerPoolSize() {
        return taskPool.getPoolSize();
    }

    /**
     * Get the maximum worker pool size.
     *
     * @return the maximum worker pool size
     */
    protected final int getMaxWorkerPoolSize() {
        return taskPool.getMaximumPoolSize();
    }

    /**
     * Get an estimate of the number of tasks in the worker queue.
     *
     * @return the estimated number of tasks
     */
    protected final int getWorkerQueueSize() {
        return taskPool.getQueueSize();
    }

    //==================================================
    //
    // Source address
    //
    //==================================================

    /**
     * Get the bind address table.
     *
     * @return the bind address table
     */
    protected CidrAddressTable getBindAddressTable() {
        return bindAddressTable;
    }

    /**
     * Get the expected bind address for the given destination, if any.
     *
     * @return the expected bind address for the given destination, or {@code null} if no explicit bind will be done
     */
    public InetSocketAddress getBindAddress(InetAddress destination) {
        return bindAddressTable.get(destination);
    }

    //==================================================
    //
    // JMX
    //
    //==================================================

    public abstract XnioWorkerMXBean getMXBean();

    protected abstract ManagementRegistration registerServerMXBean(XnioServerMXBean metrics);

    //==================================================
    //
    // Builder
    //
    //==================================================

    /**
     * A builder which allows workers to be programmatically configured.
     */
    public static class Builder {
        private final Xnio xnio;
        private ExecutorService externalExecutorService;
        private Runnable terminationTask;
        private String workerName;
        private int coreWorkerPoolSize = 4;
        private int maxWorkerPoolSize = 16;
        private ThreadGroup threadGroup;
        private boolean daemon;
        private int workerKeepAlive = 60_000;
        private int workerIoThreads = 1;
        private long workerStackSize = 0L;
        private CidrAddressTable bindAddressConfigurations = new CidrAddressTable<>();

        /**
         * Construct a new instance.
         *
         * @param xnio the XNIO instance (must not be {@code null})
         */
        protected Builder(final Xnio xnio) {
            this.xnio = xnio;
        }

        public Xnio getXnio() {
            return xnio;
        }

        public Builder populateFromOptions(OptionMap optionMap) {
            setWorkerName(optionMap.get(Options.WORKER_NAME));
            setCoreWorkerPoolSize(optionMap.get(Options.WORKER_TASK_CORE_THREADS, coreWorkerPoolSize));
            setMaxWorkerPoolSize(optionMap.get(Options.WORKER_TASK_MAX_THREADS, maxWorkerPoolSize));
            setDaemon(optionMap.get(Options.THREAD_DAEMON, daemon));
            setWorkerKeepAlive(optionMap.get(Options.WORKER_TASK_KEEPALIVE, workerKeepAlive));
            if (optionMap.contains(Options.WORKER_IO_THREADS)) {
                setWorkerIoThreads(optionMap.get(Options.WORKER_IO_THREADS, 1));
            } else if (optionMap.contains(Options.WORKER_READ_THREADS) || optionMap.contains(Options.WORKER_WRITE_THREADS)) {
                setWorkerIoThreads(max(optionMap.get(Options.WORKER_READ_THREADS, 1), optionMap.get(Options.WORKER_WRITE_THREADS, 1)));
            }
            setWorkerStackSize(optionMap.get(Options.STACK_SIZE, workerStackSize));
            return this;
        }

        public Builder addBindAddressConfiguration(CidrAddress cidrAddress, InetAddress bindAddress) {
            return addBindAddressConfiguration(cidrAddress, new InetSocketAddress(bindAddress, 0));
        }

        public Builder addBindAddressConfiguration(CidrAddress cidrAddress, InetSocketAddress bindAddress) {
            final Class networkAddrClass = cidrAddress.getNetworkAddress().getClass();
            if (bindAddress.isUnresolved()) {
                throw Messages.msg.addressUnresolved(bindAddress);
            }
            if (networkAddrClass != bindAddress.getAddress().getClass()) {
                throw Messages.msg.mismatchAddressType(networkAddrClass, bindAddress.getAddress().getClass());
            }
            bindAddressConfigurations.put(cidrAddress, bindAddress);
            return this;
        }

        public Builder setBindAddressConfigurations(CidrAddressTable newTable) {
            bindAddressConfigurations = newTable;
            return this;
        }

        public CidrAddressTable getBindAddressConfigurations() {
            return bindAddressConfigurations;
        }

        public Runnable getTerminationTask() {
            return terminationTask;
        }

        public Builder setTerminationTask(final Runnable terminationTask) {
            this.terminationTask = terminationTask;
            return this;
        }

        public String getWorkerName() {
            return workerName;
        }

        public Builder setWorkerName(final String workerName) {
            this.workerName = workerName;
            return this;
        }

        public int getCoreWorkerPoolSize() {
            return coreWorkerPoolSize;
        }

        public Builder setCoreWorkerPoolSize(final int coreWorkerPoolSize) {
            Assert.checkMinimumParameter("coreWorkerPoolSize", 0, coreWorkerPoolSize);
            this.coreWorkerPoolSize = coreWorkerPoolSize;
            return this;
        }

        public int getMaxWorkerPoolSize() {
            return maxWorkerPoolSize;
        }

        public Builder setMaxWorkerPoolSize(final int maxWorkerPoolSize) {
            Assert.checkMinimumParameter("maxWorkerPoolSize", 0, maxWorkerPoolSize);
            this.maxWorkerPoolSize = maxWorkerPoolSize;
            return this;
        }

        public ThreadGroup getThreadGroup() {
            return threadGroup;
        }

        public Builder setThreadGroup(final ThreadGroup threadGroup) {
            this.threadGroup = threadGroup;
            return this;
        }

        public boolean isDaemon() {
            return daemon;
        }

        public Builder setDaemon(final boolean daemon) {
            this.daemon = daemon;
            return this;
        }

        public long getWorkerKeepAlive() {
            return workerKeepAlive;
        }

        public Builder setWorkerKeepAlive(final int workerKeepAlive) {
            Assert.checkMinimumParameter("workerKeepAlive", 0, workerKeepAlive);
            this.workerKeepAlive = workerKeepAlive;
            return this;
        }

        public int getWorkerIoThreads() {
            return workerIoThreads;
        }

        public Builder setWorkerIoThreads(final int workerIoThreads) {
            Assert.checkMinimumParameter("workerIoThreads", 0, workerIoThreads);
            this.workerIoThreads = workerIoThreads;
            return this;
        }

        public long getWorkerStackSize() {
            return workerStackSize;
        }

        public Builder setWorkerStackSize(final long workerStackSize) {
            Assert.checkMinimumParameter("workerStackSize", 0, workerStackSize);
            this.workerStackSize = workerStackSize;
            return this;
        }

        public ExecutorService getExternalExecutorService() {
            return externalExecutorService;
        }

        public Builder setExternalExecutorService(final ExecutorService executorService) {
            this.externalExecutorService = executorService;
            return this;
        }

        public XnioWorker build() {
            return xnio.build(this);
        }
    }

    //==================================================
    //
    // Private
    //
    //==================================================

    static class StreamConnectionWrapListener implements ChannelListener {

        private final FutureResult futureResult;
        private final ChannelListener openListener;

        public StreamConnectionWrapListener(final FutureResult futureResult, final ChannelListener openListener) {
            this.futureResult = futureResult;
            this.openListener = openListener;
        }

        public void handleEvent(final StreamConnection channel) {
            final AssembledConnectedStreamChannel assembledChannel = new AssembledConnectedStreamChannel(channel, channel.getSourceChannel(), channel.getSinkChannel());
            if (!futureResult.setResult(assembledChannel)) {
                safeClose(assembledChannel);
            } else {
                ChannelListeners.invokeChannelListener(assembledChannel, openListener);
            }
        }
    }

    static class MessageConnectionWrapListener implements ChannelListener {

        private final FutureResult futureResult;
        private final ChannelListener openListener;

        public MessageConnectionWrapListener(final FutureResult futureResult, final ChannelListener openListener) {
            this.futureResult = futureResult;
            this.openListener = openListener;
        }

        public void handleEvent(final MessageConnection channel) {
            final AssembledConnectedMessageChannel assembledChannel = new AssembledConnectedMessageChannel(channel, channel.getSourceChannel(), channel.getSinkChannel());
            if (!futureResult.setResult(assembledChannel)) {
                safeClose(assembledChannel);
            } else {
                ChannelListeners.invokeChannelListener(assembledChannel, openListener);
            }
        }
    }

    private static final IoFuture.HandlingNotifier> STREAM_WRAPPING_HANDLER = new IoFuture.HandlingNotifier>() {
        public void handleCancelled(final FutureResult attachment) {
            attachment.setCancelled();
        }

        public void handleFailed(final IOException exception, final FutureResult attachment) {
            attachment.setException(exception);
        }
    };

    private static final IoFuture.HandlingNotifier> MESSAGE_WRAPPING_HANDLER = new IoFuture.HandlingNotifier>() {
        public void handleCancelled(final FutureResult attachment) {
            attachment.setCancelled();
        }

        public void handleFailed(final IOException exception, final FutureResult attachment) {
            attachment.setException(exception);
        }
    };

    class WorkerThreadFactory implements ThreadFactory {

        private final ThreadGroup threadGroup;
        private final long stackSize;
        private final boolean markThreadAsDaemon;

        WorkerThreadFactory(final ThreadGroup threadGroup, final long stackSize, final boolean markThreadAsDaemon) {
            this.threadGroup = threadGroup;
            this.stackSize = stackSize;
            this.markThreadAsDaemon = markThreadAsDaemon;
        }

        public Thread newThread(final Runnable r) {
            return doPrivileged(new PrivilegedAction() {
                public Thread run() {
                    final Thread taskThread = new Thread(threadGroup, r, name + " task-" + getNextSeq(), stackSize);
                    // Mark the thread as daemon if the Options.THREAD_DAEMON has been set
                    if (markThreadAsDaemon) {
                        taskThread.setDaemon(true);
                    }
                    return taskThread;
                }
            });
        }
    }

    interface TaskPool {

        void shutdown();

        List shutdownNow();

        void execute(Runnable command);

        int getCorePoolSize();

        int getMaximumPoolSize();

        long getKeepAliveTime(TimeUnit unit);

        void setCorePoolSize(int size);

        void setMaximumPoolSize(int size);

        void setKeepAliveTime(long time, TimeUnit unit);

        int getActiveCount();

        int getPoolSize();

        int getQueueSize();
    }

    static class DefaultThreadPoolExecutor extends ThreadPoolExecutor {
        private final Runnable terminationTask;

        DefaultThreadPoolExecutor(final int corePoolSize, final int maximumPoolSize, final long keepAliveTime, final TimeUnit unit, final BlockingQueue workQueue, final ThreadFactory threadFactory, final Runnable terminationTask) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
            this.terminationTask = terminationTask;
        }

        protected void terminated() {
            terminationTask.run();
        }

        public void setCorePoolSize(final int size) {
            setMaximumPoolSize(size);
        }

        public void setMaximumPoolSize(final int size) {
            if (size > getCorePoolSize()) {
                super.setMaximumPoolSize(size);
                super.setCorePoolSize(size);
            } else {
                super.setCorePoolSize(size);
                super.setMaximumPoolSize(size);
            }
        }
    }

    static class ThreadPoolExecutorTaskPool implements TaskPool {
        private final ThreadPoolExecutor delegate;

        ThreadPoolExecutorTaskPool(final ThreadPoolExecutor delegate) {
            this.delegate = delegate;
        }

        @Override
        public void shutdown() {
            delegate.shutdown();
        }

        @Override
        public List shutdownNow() {
            return delegate.shutdownNow();
        }

        @Override
        public void execute(final Runnable command) {
            delegate.execute(command);
        }

        @Override
        public int getCorePoolSize() {
            return delegate.getCorePoolSize();
        }

        @Override
        public int getMaximumPoolSize() {
            return delegate.getMaximumPoolSize();
        }

        @Override
        public long getKeepAliveTime(final TimeUnit unit) {
            return delegate.getKeepAliveTime(unit);
        }

        @Override
        public void setCorePoolSize(final int size) {
            delegate.setCorePoolSize(size);
        }

        @Override
        public void setMaximumPoolSize(final int size) {
            delegate.setMaximumPoolSize(size);
        }

        @Override
        public void setKeepAliveTime(final long time, final TimeUnit unit) {
            delegate.setKeepAliveTime(time, unit);
        }

        @Override
        public int getActiveCount() {
            return delegate.getActiveCount();
        }

        @Override
        public int getPoolSize() {
            return delegate.getPoolSize();
        }

        @Override
        public int getQueueSize() {
            return delegate.getQueue().size();
        }
    }

    static class EnhancedQueueExecutorTaskPool implements TaskPool {
        private final EnhancedQueueExecutor executor;

        EnhancedQueueExecutorTaskPool(final EnhancedQueueExecutor executor) {
            this.executor = executor;
        }

        public void shutdown() {
            executor.shutdown();
        }

        public List shutdownNow() {
            return executor.shutdownNow();
        }

        public void execute(final Runnable command) {
            executor.execute(command);
        }

        public int getCorePoolSize() {
            return executor.getCorePoolSize();
        }

        public int getMaximumPoolSize() {
            return executor.getMaximumPoolSize();
        }

        public long getKeepAliveTime(final TimeUnit unit) {
            return executor.getKeepAliveTime(unit);
        }

        public void setCorePoolSize(final int size) {
            executor.setCorePoolSize(size);
        }

        public void setMaximumPoolSize(final int size) {
            executor.setMaximumPoolSize(size);
        }

        public void setKeepAliveTime(final long time, final TimeUnit unit) {
            executor.setKeepAliveTime(time, unit);
        }

        public int getActiveCount() {
            return executor.getActiveCount();
        }

        public int getPoolSize() {
            return executor.getPoolSize();
        }

        public int getQueueSize() {
            return executor.getQueueSize();
        }
    }

    static class ExecutorServiceTaskPool implements TaskPool {
        private final ExecutorService delegate;

        ExecutorServiceTaskPool(final ExecutorService delegate) {
            this.delegate = delegate;
        }

        public void shutdown() {
            delegate.shutdown();
        }

        public List shutdownNow() {
            return delegate.shutdownNow();
        }

        public void execute(final Runnable command) {
            delegate.execute(command);
        }

        public int getCorePoolSize() {
            return -1;
        }

        public int getMaximumPoolSize() {
            return -1;
        }

        public long getKeepAliveTime(final TimeUnit unit) {
            return -1;
        }

        public void setCorePoolSize(final int size) {
        }

        public void setMaximumPoolSize(final int size) {
        }

        public void setKeepAliveTime(final long time, final TimeUnit unit) {
        }

        public int getActiveCount() {
            return -1;
        }

        public int getPoolSize() {
            return -1;
        }

        public int getQueueSize() {
            return -1;
        }
    }

    static class ExternalTaskPool implements TaskPool {
        private final TaskPool delegate;

        ExternalTaskPool(final TaskPool delegate) {
            this.delegate = delegate;
        }

        @Override
        public void shutdown() {
            // no operation
        }

        @Override
        public List shutdownNow() {
            return Collections.emptyList();
        }

        @Override
        public void execute(final Runnable command) {
            delegate.execute(command);
        }

        @Override
        public int getCorePoolSize() {
            return delegate.getCorePoolSize();
        }

        @Override
        public int getMaximumPoolSize() {
            return delegate.getMaximumPoolSize();
        }

        @Override
        public long getKeepAliveTime(final TimeUnit unit) {
            return delegate.getKeepAliveTime(unit);
        }

        @Override
        public void setCorePoolSize(final int size) {
            delegate.setCorePoolSize(size);
        }

        @Override
        public void setMaximumPoolSize(final int size) {
            delegate.setMaximumPoolSize(size);
        }

        @Override
        public void setKeepAliveTime(final long time, final TimeUnit unit) {
            delegate.setKeepAliveTime(time, unit);
        }

        @Override
        public int getActiveCount() {
            return delegate.getActiveCount();
        }

        @Override
        public int getPoolSize() {
            return delegate.getPoolSize();
        }

        @Override
        public int getQueueSize() {
            return delegate.getQueueSize();
        }
    }
}