com.mastfrog.acteur.server.ServerModule.java Source code

Java tutorial

Introduction

Here is the source code for com.mastfrog.acteur.server.ServerModule.java

Source

/*
 * The MIT License
 *
 * Copyright 2013 Tim Boudreau.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.mastfrog.acteur.server;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import com.mastfrog.acteur.Acteur;
import com.mastfrog.acteur.Application;
import com.mastfrog.acteur.BuiltInPageAnnotationHandler;
import com.mastfrog.acteur.Closables;
import com.mastfrog.acteur.Event;
import com.mastfrog.acteur.HttpEvent;
import com.mastfrog.acteur.ImplicitBindings;
import com.mastfrog.acteur.Page;
import com.mastfrog.acteur.ResponseHeaders;
import com.mastfrog.acteur.errors.Err;
import com.mastfrog.acteur.errors.ErrorResponse;
import com.mastfrog.acteur.errors.ExceptionEvaluator;
import com.mastfrog.acteur.errors.ExceptionEvaluatorRegistry;
import com.mastfrog.acteur.headers.Method;
import com.mastfrog.acteur.server.ServerModule.TF;
import com.mastfrog.acteur.spi.ApplicationControl;
import com.mastfrog.acteur.sse.EventChannelName;
import com.mastfrog.acteur.util.BasicCredentials;
import com.mastfrog.acteur.util.HttpMethod;
import com.mastfrog.acteur.util.RequestID;
import com.mastfrog.acteur.util.Server;
import com.mastfrog.acteur.util.ServerControl;
import com.mastfrog.acteurbase.ActeurBaseModule;
import com.mastfrog.acteurbase.Chain;
import com.mastfrog.giulius.Dependencies;
import com.mastfrog.guicy.annotations.Defaults;
import com.mastfrog.guicy.scope.ReentrantScope;
import com.mastfrog.parameters.KeysValues;
import com.mastfrog.settings.MutableSettings;
import com.mastfrog.settings.Settings;
import com.mastfrog.settings.SettingsBuilder;
import com.mastfrog.url.Path;
import com.mastfrog.util.Codec;
import com.mastfrog.util.ConfigurationError;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.Cookie;
import io.netty.handler.codec.http.CookieDecoder;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.util.CharsetUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import javax.inject.Inject;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.netbeans.validation.api.InvalidInputException;

/**
 * Guice module for creating a server; also defines settings keys which can
 * affect behavior.
 *
 * @author Tim Boudreau
 */
@Defaults("realm=Users")
public class ServerModule<A extends Application> extends AbstractModule {

    /**
     * Name of the &#064;Named parameter that should be used in an annotation if
     * you want Guice to inject the specific thread pool used for processing
     * requests.
     */
    public static final String BACKGROUND_THREAD_POOL_NAME = "background";
    /**
     * Name of the &#064;Named parameter that should be used in an annotation if
     * you want Guice to inject the specific thread pool used for processing
     * requests.
     */
    public static final String WORKER_THREAD_POOL_NAME = "workers";
    /**
     * Name of the &#064;Named parameter that should be used in an annotation if
     * you want Guice to inject the specific thread pool used for processing
     * requests, but wrappered so that all runnables are run within the
     * application's request scope and have whatever context they were submitted
     * with.
     */
    public static final String SCOPED_WORKER_THREAD_POOL_NAME = "scopedWorkers";
    /**
     * Name of the &#064;Named parameter that should be used in an annotation if
     * you want Guice to inject the specific thread pool used for processing
     * requests, but wrappered so that all runnables are run within the
     * application's request scope and have whatever context they were submitted
     * with.
     */
    public static final String SCOPED_BACKGROUND_THREAD_POOL_NAME = "scopedBackground";

    /**
     * Property name for setting which byte buffer allocator Netty uses (heap,
     * direct, pooled).
     */
    public static final String BYTEBUF_ALLOCATOR_SETTINGS_KEY = "acteur.bytebuf.allocator";
    /**
     * Property value for telling the server to use the direct byte buffer
     * allocator (non-heap).
     */
    public static final String DIRECT_ALLOCATOR = "direct";
    /**
     * Property value for telling the server to use the heap byte buffer
     * allocator.
     */
    public static final String HEAP_ALLOCATOR = "heap";
    /**
     * Property value for telling the server to use the pooled byte buffer
     * allocator.
     */
    public static final String POOLED_ALLOCATOR = "pooled";
    /**
     * Property value for telling the server to use the heap or direct byte
     * buffer allocator as decided by Netty's PlatformDependent class.
     */
    public static final String DIRECT_OR_HEAP_BY_PLATFORM = "directOrHeap";
    /**
     * The default allocator to use if none is specified
     */
    public static final String DEFAULT_ALLOCATOR = POOLED_ALLOCATOR;
    /**
     * Settings key for the nnumber of worker threads to use.
     */
    public static final String WORKER_THREADS = "workerThreads";
    /**
     * Number of event threads
     */
    public static final String EVENT_THREADS = "eventThreads";
    /**
     * Number of background thread pool threads. The background thread pool is
     * used by a few things which chunk responses.
     */
    public static final String BACKGROUND_THREADS = "backgroundThreads";
    /**
     * The port to run on
     */
    public static final String PORT = "port";

    /**
     * Settings key for enabling HTTP compression.
     */
    public static final String HTTP_COMPRESSION = "httpCompression";

    /**
     * Settings key for the maximum content length.
     */
    public static final String MAX_CONTENT_LENGTH = "maxContentLength";
    /**
     * Guice binding for
     * <code>&#064;Named(DELAY_EXECUTOR) ScheduledExecutorService</code> to get
     * a scheduled executor service which shares a ThreadFactory with the worker
     * thread pool.
     */
    public static final String DELAY_EXECUTOR = "delayExecutor";
    /**
     * Number of threads to process delayed responses (see Acteur.setDelay()).
     * These threads are typically not busy and can be 1-2 threads.
     */
    public static final String SETTINGS_KEY_DELAY_THREAD_POOL_THREADS = "delay.response.threads";
    /**
     * The default number of delay threads.
     */
    private static final int DEFAULT_DELAY_THREADS = 2;

    /**
     * If true, the return value of Event.getRemoteAddress() will prefer the
     * headers X-Forwarded-For or X-Real-IP if present, so that running an
     * acteur application behind a reverse proxy does not mask the actual IP
     * address.
     */
    public static final String SETTINGS_KEY_DECODE_REAL_IP = "decodeRealIP";
    /**
     * Settings key if true, do CORS responses on OPTIONS requests
     */
    public static final String SETTINGS_KEY_CORS_ENABLED = "cors.enabled";
    /**
     * If true (the default), a ForkJoinPool will be used for dispatching work
     * to acteurs;  if not, a fixed thread ExecutorService will be used.
     * The default is correct for most appliations; applications which require
     * an extremely small memory footprint (7-10Mb) will reduce their memory
     * requirements under load by turning this off.
     */
    public static final String SETTINGS_KEY_USE_FORK_JOIN_POOL = "acteur.fork.join";

    /**
     * If the default support for CORS requests is enabled, this is the max age
     * in minutes that the browser should regard the response as valid.
     */
    public static final String SETTINGS_KEY_CORS_MAX_AGE_MINUTES = "cors.max.age.minutes";
    /**
     * If the default support for CORS requests is enabled, this is the value
     * of what hosts the response is valid for (what sites can use scripts from
     * this server without the browser blocking them).  The default is *.
     */
    public static final String SETTINGS_KEY_CORS_ALLOW_ORIGIN = "cors.allow.origin";
    /**
     * Default value for @link(ServerModule.SETTINGS_KEY_CORS_ENABLED}
     */
    public static final boolean DEFAULT_CORS_ENABLED = true;
    /**
     * Default value for @link(ServerModule.SETTINGS_KEY_CORS_MAX_AGE_MINUTES}
     */
    public static final long DEFAULT_CORS_MAX_AGE_MINUTES = 5;
    /**
     * Default value for @link(ServerModule.SETTINGS_KEY_CORS_ALLOW_ORIGIN}
     */
    public static final String DEFAULT_CORS_ALLOW_ORIGIN = "*";

    /**
     * Determine if the application should exit if an exception is thrown when binding
     * the server socket (usually because the port is in use).  The default is
     * true, but in cases where multiple servers are started in one JVM and the failure
     * of one should not cause the JVM to exit, it can be set to false and the JVM
     * will continue running if there are any live non-daemon threads.
     */
    public static final String SETTINGS_KEY_SYSTEM_EXIT_ON_BIND_FAILURE = "system.exit.on.bind.failure";

    protected final Class<A> appType;
    protected final ReentrantScope scope;
    private final int eventThreads;
    private final int workerThreads;
    private final int backgroundThreads;
    private final List<Module> otherModules = new ArrayList<>();

    public ServerModule(Class<A> appType, int workerThreadCount, int eventThreadCount, int backgroundThreadCount) {
        this(new ReentrantScope(), appType, workerThreadCount, eventThreadCount, backgroundThreadCount);
    }

    public ServerModule(ReentrantScope scope, Class<A> appType, int workerThreadCount, int eventThreadCount,
            int backgroundThreadCount) {
        if (!Application.class.isAssignableFrom(appType)) {
            throw new ClassCastException(
                    appType.getName() + " is not a subclass of " + Application.class.getName());
        }
        this.appType = appType;
        this.workerThreads = workerThreadCount;
        this.eventThreads = eventThreadCount;
        this.backgroundThreads = backgroundThreadCount;
        this.scope = scope;
    }

    public ServerModule(Class<A> appType) {
        this(appType, -1, -1, -1);
    }

    /**
     * Get the Guice scope used for injecting dynamic request-related
     * objects into Acteur constructors.
     * @return The scope
     */
    public final ReentrantScope applicationScope() {
        return scope;
    }

    @Override
    @SuppressWarnings("deprecation")
    protected void configure() {
        bind(Server.class).to(ServerImpl.class);
        bind(ReentrantScope.class).toInstance(scope);
        bind(Application.class).to(appType).asEagerSingleton();
        bind(ChannelHandler.class).to(UpstreamHandlerImpl.class);
        bind(new CISC()).to(PipelineFactoryImpl.class);
        bind(ServerBootstrap.class).toProvider(new ServerBootstrapProvider(binder().getProvider(Settings.class),
                binder().getProvider(ByteBufAllocator.class)));

        scope.bindTypes(binder(), Event.class, HttpEvent.class, RequestID.class, Page.class, BasicCredentials.class,
                Closables.class);

        ImplicitBindings implicit = appType.getAnnotation(ImplicitBindings.class);
        if (implicit != null) {
            scope.bindTypes(binder(), implicit.value());
        }
        scope.bindTypesAllowingNulls(binder(), EventChannelName.class);
        // Acteurs can ask for a Deferral to pause execution while some
        // other operation completes, such as making an external HTTP request
        // to another server
        install(new ActeurBaseModule(scope));

        Provider<ApplicationControl> appControlProvider = binder().getProvider(ApplicationControl.class);
        Provider<Settings> set = binder().getProvider(Settings.class);

        TF eventThreadFactory = new TF(EVENT_THREADS, appControlProvider);
        TF workerThreadFactory = new TF(WORKER_THREADS, appControlProvider);
        TF backgroundThreadFactory = new TF(BACKGROUND_THREAD_POOL_NAME, appControlProvider);

        bind(ThreadGroup.class).annotatedWith(Names.named(BACKGROUND_THREAD_POOL_NAME))
                .toInstance(backgroundThreadFactory.tg);
        bind(ThreadGroup.class).annotatedWith(Names.named(WORKER_THREADS)).toInstance(workerThreadFactory.tg);
        bind(ThreadGroup.class).annotatedWith(Names.named(EVENT_THREADS)).toInstance(eventThreadFactory.tg);

        ThreadCount workerThreadCount = new ThreadCount(set, 8, workerThreads, WORKER_THREADS);
        ThreadCount eventThreadCount = new ThreadCount(set, 8, eventThreads, EVENT_THREADS);
        ThreadCount backgroundThreadCount = new ThreadCount(set, 128, backgroundThreads, BACKGROUND_THREADS);

        bind(ThreadCount.class).annotatedWith(Names.named(EVENT_THREADS)).toInstance(eventThreadCount);
        bind(ThreadCount.class).annotatedWith(Names.named(WORKER_THREADS)).toInstance(workerThreadCount);
        bind(ThreadCount.class).annotatedWith(Names.named(BACKGROUND_THREAD_POOL_NAME))
                .toInstance(backgroundThreadCount);

        bind(ThreadFactory.class).annotatedWith(Names.named(WORKER_THREADS)).toInstance(workerThreadFactory);
        bind(ThreadFactory.class).annotatedWith(Names.named(EVENT_THREADS)).toInstance(eventThreadFactory);
        bind(ThreadFactory.class).annotatedWith(Names.named(BACKGROUND_THREAD_POOL_NAME))
                .toInstance(backgroundThreadFactory);

        Provider<ExecutorService> workerProvider = new ExecutorServiceProvider(workerThreadFactory,
                workerThreadCount, set);
        Provider<ExecutorService> backgroundProvider = new ExecutorServiceProvider(backgroundThreadFactory,
                backgroundThreadCount, set);

        bind(ExecutorService.class).annotatedWith(Names.named(WORKER_THREAD_POOL_NAME)).toProvider(workerProvider);

        bind(ExecutorService.class).annotatedWith(Names.named(BACKGROUND_THREAD_POOL_NAME))
                .toProvider(backgroundProvider);

        bind(ExecutorService.class).annotatedWith(Names.named(SCOPED_WORKER_THREAD_POOL_NAME))
                .toProvider(new WrappedWorkerThreadPoolProvider(workerProvider, scope));

        bind(ExecutorService.class).annotatedWith(Names.named(SCOPED_BACKGROUND_THREAD_POOL_NAME))
                .toProvider(new WrappedWorkerThreadPoolProvider(backgroundProvider, scope));

        bind(DateTime.class).toInstance(DateTime.now());
        bind(Duration.class).toProvider(UptimeProvider.class);
        bind(new CKTL()).toProvider(CookiesProvider.class);

        //XXX anything using this?
        bind(String.class).annotatedWith(Names.named("application")).toInstance(this.appType.getSimpleName());

        bind(ServerImpl.class).asEagerSingleton();
        for (Module m : otherModules) {
            install(m);
        }
        bind(Charset.class).toProvider(CharsetProvider.class);
        bind(ByteBufAllocator.class).toProvider(ByteBufAllocatorProvider.class);
        bind(new ETL()).toProvider(EventProvider.class).in(scope);
        bind(Codec.class).to(CodecImpl.class);
        bind(ApplicationControl.class).toProvider(ApplicationControlProvider.class).in(Scopes.SINGLETON);
        bind(ExceptionEvaluatorRegistry.class).asEagerSingleton();
        bind(KeysValues.class).toProvider(KeysValuesProvider.class);
        bind(InvalidInputExceptionEvaluator.class).asEagerSingleton();
        bind(Channel.class).toProvider(ChannelProvider.class);
        bind(HttpMethod.class).toProvider(MethodProvider.class);
        bind(Method.class).toProvider(MethodProvider2.class);
        bind(Path.class).toProvider(PathProvider.class);
        bind(BuiltInPageAnnotationHandler.class).asEagerSingleton();
        bind(ResponseHeaders.class).toProvider(ResponseHeadersProvider.class);
        bind(ScheduledExecutorService.class).annotatedWith(Names.named(DELAY_EXECUTOR))
                .toProvider(DelayExecutorProvider.class);
        // allow Chain<Acteur> to be injected
        bind(new CL()).toProvider(ChainProvider.class);
    }

    static class CL extends TypeLiteral<Chain<Acteur>> {

    }

    static class ChainProvider implements Provider<Chain<Acteur>> {

        @SuppressWarnings("unchecked")
        private final Provider<Chain> chain;

        @SuppressWarnings("unchecked")
        @Inject
        ChainProvider(Provider<Chain> chain) {
            this.chain = chain;
        }

        @Override
        @SuppressWarnings("unchecked")
        public Chain<Acteur> get() {
            return chain.get();
        }

    }

    @Singleton
    private static final class DelayExecutorProvider implements Provider<ScheduledExecutorService> {

        private final ThreadFactory workerThreadFactory;
        private final int count;
        private volatile ScheduledExecutorService exe;

        @Inject
        DelayExecutorProvider(@Named(ServerModule.WORKER_THREADS) ThreadFactory workerThreadFactory,
                Settings settings) {
            this.workerThreadFactory = workerThreadFactory;
            count = settings.getInt(SETTINGS_KEY_DELAY_THREAD_POOL_THREADS, DEFAULT_DELAY_THREADS);
        }

        @Override
        public ScheduledExecutorService get() {
            if (exe == null) {
                synchronized (this) {
                    if (exe == null) {
                        exe = Executors.newScheduledThreadPool(count, workerThreadFactory);
                    }
                }
            }
            return exe;
        }
    }

    @Singleton
    private static final class ResponseHeadersProvider implements Provider<ResponseHeaders> {

        private final Provider<Page> page;

        @Inject
        public ResponseHeadersProvider(Provider<Page> page) {
            this.page = page;
        }

        @Override
        public ResponseHeaders get() {
            return page.get().getResponseHeaders();
        }
    }

    private static final class PathProvider implements Provider<Path> {

        private final Provider<HttpEvent> evt;

        @Inject
        PathProvider(Provider<HttpEvent> evt) {
            this.evt = evt;
        }

        @Override
        public Path get() {
            return evt.get().getPath();
        }
    }

    private static final class MethodProvider implements Provider<HttpMethod> {

        private final Provider<HttpEvent> evt;

        @Inject
        MethodProvider(Provider<HttpEvent> evt) {
            this.evt = evt;
        }

        @Override
        public HttpMethod get() {
            return evt.get().getMethod();
        }

    }

    private static final class MethodProvider2 implements Provider<Method> {

        private final Provider<HttpEvent> evt;

        @Inject
        MethodProvider2(Provider<HttpEvent> evt) {
            this.evt = evt;
        }

        @Override
        public Method get() {
            HttpEvent e = evt.get();
            if (e == null) {
                return null;
            }
            return e == null || !(e.getMethod() instanceof Method) ? null : (Method) evt.get().getMethod();
        }

    }

    private static final class ChannelProvider implements Provider<Channel> {

        private final Provider<HttpEvent> evt;

        @Inject
        ChannelProvider(Provider<HttpEvent> evt) {
            this.evt = evt;
        }

        @Override
        public Channel get() {
            return evt.get().getChannel();
        }
    }

    private static final class KeysValuesProvider implements Provider<KeysValues> {

        private final Provider<HttpEvent> evt;

        @Inject
        public KeysValuesProvider(Provider<HttpEvent> evt) {
            this.evt = evt;
        }

        @Override
        public KeysValues get() {
            return new KeysValues.MapAdapter(evt.get().getParametersAsMap());
        }
    }

    private static final class InvalidInputExceptionEvaluator extends ExceptionEvaluator {

        @Inject
        public InvalidInputExceptionEvaluator(ExceptionEvaluatorRegistry registry) {
            super(registry);
        }

        @Override
        public ErrorResponse evaluate(Throwable t, Acteur acteur, Page page, HttpEvent evt) {
            if (t instanceof InvalidInputException) {
                InvalidInputException iie = (InvalidInputException) t;
                return Err.badRequest(iie.getProblems().toString());
            }
            return null;
        }
    }

    private static final class EventProvider implements Provider<Event<?>> {

        @SuppressWarnings("unchecked")
        private final Provider<Event> eventProvider;

        @SuppressWarnings("unchecked")
        @Inject
        public EventProvider(Provider<Event> eventProvider) {
            this.eventProvider = eventProvider;
        }

        @Override
        public Event<?> get() {
            return eventProvider.get();
        }
    }

    private static final class ETL extends TypeLiteral<Event<?>> {

    }

    static class ApplicationControlProvider implements Provider<ApplicationControl> {

        private final ApplicationControl control;

        @Inject
        public ApplicationControlProvider(Provider<Application> app) throws NoSuchMethodException,
                IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            // In order to separate the API and SPI of Application, so Application
            // is not polluted with methods a subclasser should never call,
            // we do this:
            Application a = app.get();
            java.lang.reflect.Method method = Application.class.getDeclaredMethod("control");
            method.setAccessible(true);
            control = (ApplicationControl) method.invoke(a);
        }

        @Override
        public ApplicationControl get() {
            return control;
        }
    }

    static class CodecImpl implements Codec {

        private final Provider<ObjectMapper> mapper;

        @Inject
        public CodecImpl(Provider<ObjectMapper> mapper) {
            this.mapper = mapper;
        }

        @Override
        public <T> String writeValueAsString(T object) throws IOException {
            return mapper.get().writeValueAsString(object);
        }

        @Override
        public <T> void writeValue(T object, OutputStream out) throws IOException {
            mapper.get().writeValue(out, object);
        }

        @Override
        public <T> T readValue(InputStream byteBufInputStream, Class<T> type) throws IOException {
            return mapper.get().readValue(byteBufInputStream, type);
        }

        @Override
        public <T> byte[] writeValueAsBytes(T object) throws IOException {
            return mapper.get().writeValueAsBytes(object);
        }
    }

    @Singleton
    private static final class CharsetProvider implements Provider<Charset> {

        private final Charset charset;

        @Inject
        CharsetProvider(Settings settings) {
            String set = settings.getString("charset");
            if (set == null) {
                charset = CharsetUtil.UTF_8;
            } else {
                charset = Charset.forName(set);
            }
        }

        @Override
        public Charset get() {
            return charset;
        }
    }

    /**
     * Add another module to be installed with this one
     *
     * @param module
     */
    public ServerModule<A> add(Module module) {
        otherModules.add(module);
        return this;
    }

    /**
     * Override to configure options on the bootstrap which will start the
     * server. The default implementation sets up the allocator.
     *
     * @param bootstrap The server bootstrap
     * @param settings The application settings
     * @return The same bootstrap or optionally another one
     */
    protected ServerBootstrap configureServerBootstrap(ServerBootstrap bootstrap, Settings settings) {
        return bootstrap;
    }

    @Singleton
    private static final class ByteBufAllocatorProvider implements Provider<ByteBufAllocator> {

        private final Settings settings;
        private volatile ByteBufAllocator allocator;

        @Inject
        public ByteBufAllocatorProvider(Settings settings) {
            this.settings = settings;
        }

        public ByteBufAllocator get() {
            String s = settings.getString(BYTEBUF_ALLOCATOR_SETTINGS_KEY, DEFAULT_ALLOCATOR);
            ByteBufAllocator result = this.allocator;
            if (result == null) {
                synchronized (this) {
                    result = this.allocator;
                    if (result == null) {
                        switch (s) {
                        case DIRECT_OR_HEAP_BY_PLATFORM:
                            result = UnpooledByteBufAllocator.DEFAULT;
                            break;
                        case DIRECT_ALLOCATOR:
                            result = new UnpooledByteBufAllocator(true);
                            break;
                        case HEAP_ALLOCATOR:
                            result = new UnpooledByteBufAllocator(false);
                            break;
                        case POOLED_ALLOCATOR:
                            result = PooledByteBufAllocator.DEFAULT;
                            break;
                        default:
                            throw new ConfigurationError("Unknown value for " + BYTEBUF_ALLOCATOR_SETTINGS_KEY
                                    + " '" + s + "'; valid values are " + DIRECT_ALLOCATOR + ", " + HEAP_ALLOCATOR
                                    + ", " + POOLED_ALLOCATOR);
                        }
                        this.allocator = result;
                    }
                }
            }
            return this.allocator;
        }
    }

    private final class ServerBootstrapProvider implements Provider<ServerBootstrap> {

        private final Provider<Settings> settings;
        private final Provider<ByteBufAllocator> allocator;

        public ServerBootstrapProvider(Provider<Settings> settings, Provider<ByteBufAllocator> allocator) {
            this.settings = settings;
            this.allocator = allocator;
        }

        @Override
        public ServerBootstrap get() {
            ServerBootstrap result = new ServerBootstrap();
            result = result.childOption(ChannelOption.ALLOCATOR, allocator.get());
            return configureServerBootstrap(result, settings.get());
        }
    }

    private static final class WrappedWorkerThreadPoolProvider implements Provider<ExecutorService> {

        private final Provider<ExecutorService> svc;
        private final ReentrantScope scope;

        public WrappedWorkerThreadPoolProvider(Provider<ExecutorService> svc, ReentrantScope scope) {
            this.svc = svc;
            this.scope = scope;
        }

        @Override
        public ExecutorService get() {
            return scope.wrapThreadPool(svc.get());
        }
    }

    private static final class CookiesProvider implements Provider<Set<Cookie>> {

        private final Provider<HttpEvent> ev;

        @Inject
        public CookiesProvider(Provider<HttpEvent> ev) {
            this.ev = ev;
        }

        @Override
        public Set<Cookie> get() {
            HttpEvent evt = ev.get();
            String h = evt.getHeader(HttpHeaders.Names.COOKIE);
            if (h != null) {
                Set<Cookie> result = CookieDecoder.decode(h);
                if (result != null) {
                    return result;
                }
            }
            return Collections.emptySet();
        }
    }

    private static final class ExecutorServiceProvider implements Provider<ExecutorService> {

        final TF tf;
        private volatile ExecutorService svc;
        private final ThreadCount count;
        private final Provider<Settings> settings;

        public ExecutorServiceProvider(TF tf, ThreadCount count, Provider<Settings> settings) {
            this.tf = tf;
            this.count = count;
            this.settings = settings;
        }

        private ExecutorService create() {
            boolean useForkJoin = settings.get().getBoolean(SETTINGS_KEY_USE_FORK_JOIN_POOL, true);
            switch (tf.name()) {
            case BACKGROUND_THREAD_POOL_NAME:
                if (useForkJoin) {
                    return new ForkJoinPool(count.get(), tf, tf, true);
                } else {
                    return Executors.newCachedThreadPool(tf);
                }
            default:
                if (useForkJoin) {
                    return new ForkJoinPool(count.get(), tf, tf, true);
                } else {
                    return Executors.newFixedThreadPool(count.get(), tf);
                }
            }
        }

        @Override
        public ExecutorService get() {
            if (svc == null) {
                synchronized (this) {
                    if (svc == null) {
                        svc = create();
                    }
                }
            }
            return svc;
        }
    }

    static final class TF
            implements ThreadFactory, UncaughtExceptionHandler, ForkJoinPool.ForkJoinWorkerThreadFactory {

        private final String name;
        private final Provider<ApplicationControl> app;
        private final AtomicInteger count = new AtomicInteger();
        private final ThreadGroup tg;

        public TF(String name, Provider<ApplicationControl> app) {
            this.name = name;
            this.app = app;
            tg = new ThreadGroup(Thread.currentThread().getThreadGroup(), name + "s");
            tg.setDaemon(true);
        }

        public String name() {
            return name;
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            if ("event".equals(tg.getName())) {
                t.setPriority(Thread.MAX_PRIORITY);
            } else {
                t.setPriority(Thread.NORM_PRIORITY);
            }
            t.setUncaughtExceptionHandler(this);
            String nm = name + "-" + count.getAndIncrement();
            t.setName(nm);
            return t;
        }

        @Override
        public void uncaughtException(Thread on, Throwable error) {
            app.get().internalOnError(error);
        }

        public String toString() {
            return "ThreadFactory " + name;
        }

        @Override
        public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
            FWT t = new FWT(pool);
            if ("event".equals(tg.getName())) {
                t.setPriority(Thread.MAX_PRIORITY);
            } else {
                t.setPriority(Thread.NORM_PRIORITY - 1);
            }
            t.setUncaughtExceptionHandler(this);
            String nm = name + "-" + count.getAndIncrement();
            t.setName(nm);
            return t;
        }

        static class FWT extends java.util.concurrent.ForkJoinWorkerThread {

            public FWT(ForkJoinPool pool) {
                super(pool);
            }

        }
    }

    private static class UptimeProvider implements Provider<Duration> {

        private final DateTime dt;

        @Inject
        UptimeProvider(DateTime dt) {
            this.dt = dt;
        }

        @Override
        public Duration get() {
            return new Duration(dt, new DateTime());
        }
    }

    protected void onInit(Settings settings) {
    }

    protected void onBeforeStart(Server server, Dependencies deps) {
    }

    protected void onAfterStart(Server server, Dependencies deps) {
    }

    /**
     * Start a server
     *
     * @return an object to wait on, which can be used to shut down the server
     * @throws IOException if something goes wrong
     * @throws InterruptedException if something goes wrong
     * @deprecated Use ServerBuilder instead
     */
    @Deprecated
    public ServerControl start() throws IOException, InterruptedException {
        return start(null);
    }

    /**
     * Start a server
     *
     * @param port The port to start on
     * @return an object to wait on, which can be used to shut down the server
     * @throws IOException if something goes wrong
     * @throws InterruptedException if something goes wrong
     * @deprecated Use ServerBuilder instead
     */
    @Deprecated
    public ServerControl start(Integer port) throws IOException, InterruptedException {
        MutableSettings settings = SettingsBuilder.createDefault().buildMutableSettings();
        if (port != null) {
            settings.setInt("port", port);
        }
        Integer pt = settings.getInt("port");

        if (pt == null) {
            settings.setInt("port", port == null ? 8080 : port);
            pt = 8080;
        }
        onInit(settings);
        Dependencies dependencies = new Dependencies(settings, this);
        Server server = dependencies.getInstance(Server.class);
        onBeforeStart(server, dependencies);

        ServerControl result = server.start(pt);
        onAfterStart(server, dependencies);
        return result;
    }

    private static class CISC extends TypeLiteral<ChannelInitializer<SocketChannel>> {
    }

    private static class CKTL extends TypeLiteral<Set<Cookie>> {
    }
}