io.vertx.core.impl.VertxImpl.java Source code

Java tutorial

Introduction

Here is the source code for io.vertx.core.impl.VertxImpl.java

Source

/*
 * Copyright (c) 2011-2013 The original author or authors
 * ------------------------------------------------------
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *     The Eclipse Public License is available at
 *     http://www.eclipse.org/legal/epl-v10.html
 *
 *     The Apache License v2.0 is available at
 *     http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */

package io.vertx.core.impl;

import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.resolver.AddressResolverGroup;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.concurrent.GenericFutureListener;
import io.vertx.core.AsyncResult;
import io.vertx.core.Closeable;
import io.vertx.core.Context;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.ServiceHelper;
import io.vertx.core.TimeoutStream;
import io.vertx.core.Verticle;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.datagram.DatagramSocket;
import io.vertx.core.datagram.DatagramSocketOptions;
import io.vertx.core.datagram.impl.DatagramSocketImpl;
import io.vertx.core.dns.DnsClient;
import io.vertx.core.dns.impl.DnsClientImpl;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.impl.EventBusImpl;
import io.vertx.core.eventbus.impl.clustered.ClusteredEventBus;
import io.vertx.core.file.FileSystem;
import io.vertx.core.file.impl.FileSystemImpl;
import io.vertx.core.file.impl.WindowsFileSystem;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.impl.HttpClientImpl;
import io.vertx.core.http.impl.HttpServerImpl;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.metrics.impl.DummyVertxMetrics;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetClientOptions;
import io.vertx.core.net.NetServer;
import io.vertx.core.net.NetServerOptions;
import io.vertx.core.net.impl.NetClientImpl;
import io.vertx.core.net.impl.NetServerBase;
import io.vertx.core.net.impl.NetServerImpl;
import io.vertx.core.net.impl.ServerID;
import io.vertx.core.shareddata.SharedData;
import io.vertx.core.shareddata.impl.SharedDataImpl;
import io.vertx.core.spi.VerticleFactory;
import io.vertx.core.spi.VertxMetricsFactory;
import io.vertx.core.spi.cluster.ClusterManager;
import io.vertx.core.spi.metrics.Metrics;
import io.vertx.core.spi.metrics.MetricsProvider;
import io.vertx.core.spi.metrics.PoolMetrics;
import io.vertx.core.spi.metrics.VertxMetrics;

import java.io.File;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author <a href="http://tfox.org">Tim Fox</a>
 */
public class VertxImpl implements VertxInternal, MetricsProvider {

    private static final Logger log = LoggerFactory.getLogger(VertxImpl.class);

    private static final String NETTY_IO_RATIO_PROPERTY_NAME = "vertx.nettyIORatio";
    private static final int NETTY_IO_RATIO = Integer.getInteger(NETTY_IO_RATIO_PROPERTY_NAME, 50);

    static {
        // Netty resource leak detection has a performance overhead and we do not need it in Vert.x
        ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
        // Use the JDK deflater/inflater by default
        System.setProperty("io.netty.noJdkZlibDecoder", "false");
    }

    private final FileSystem fileSystem = getFileSystem();
    private final SharedData sharedData;
    private final VertxMetrics metrics;
    private final ConcurrentMap<Long, InternalTimerHandler> timeouts = new ConcurrentHashMap<>();
    private final AtomicLong timeoutCounter = new AtomicLong(0);
    private final ClusterManager clusterManager;
    private final DeploymentManager deploymentManager;
    private final FileResolver fileResolver;
    private final Map<ServerID, HttpServerImpl> sharedHttpServers = new HashMap<>();
    private final Map<ServerID, NetServerBase> sharedNetServers = new HashMap<>();
    private final WorkerPool workerPool;
    private final WorkerPool internalBlockingPool;
    private final ThreadFactory eventLoopThreadFactory;
    private final NioEventLoopGroup eventLoopGroup;
    private final NioEventLoopGroup acceptorEventLoopGroup;
    private final BlockedThreadChecker checker;
    private final boolean haEnabled;
    private final AddressResolver addressResolver;
    private EventBus eventBus;
    private HAManager haManager;
    private boolean closed;
    private volatile Handler<Throwable> exceptionHandler;
    private final Map<String, SharedWorkerPool> namedWorkerPools;
    private final int defaultWorkerPoolSize;
    private final long defaultWorkerMaxExecTime;
    private final CloseHooks closeHooks;

    VertxImpl() {
        this(new VertxOptions());
    }

    VertxImpl(VertxOptions options) {
        this(options, null);
    }

    VertxImpl(VertxOptions options, Handler<AsyncResult<Vertx>> resultHandler) {
        // Sanity check
        if (Vertx.currentContext() != null) {
            log.warn("You're already on a Vert.x context, are you sure you want to create a new Vertx instance?");
        }
        closeHooks = new CloseHooks(log);
        checker = new BlockedThreadChecker(options.getBlockedThreadCheckInterval(),
                options.getWarningExceptionTime());
        eventLoopThreadFactory = new VertxThreadFactory("vert.x-eventloop-thread-", checker, false,
                options.getMaxEventLoopExecuteTime());
        eventLoopGroup = new NioEventLoopGroup(options.getEventLoopPoolSize(), eventLoopThreadFactory);
        eventLoopGroup.setIoRatio(NETTY_IO_RATIO);
        ThreadFactory acceptorEventLoopThreadFactory = new VertxThreadFactory("vert.x-acceptor-thread-", checker,
                false, options.getMaxEventLoopExecuteTime());
        // The acceptor event loop thread needs to be from a different pool otherwise can get lags in accepted connections
        // under a lot of load
        acceptorEventLoopGroup = new NioEventLoopGroup(1, acceptorEventLoopThreadFactory);
        acceptorEventLoopGroup.setIoRatio(100);

        metrics = initialiseMetrics(options);

        ExecutorService workerExec = Executors.newFixedThreadPool(options.getWorkerPoolSize(),
                new VertxThreadFactory("vert.x-worker-thread-", checker, true, options.getMaxWorkerExecuteTime()));
        PoolMetrics workerPoolMetrics = isMetricsEnabled()
                ? metrics.createMetrics(workerExec, "worker", "vert.x-worker-thread", options.getWorkerPoolSize())
                : null;
        ExecutorService internalBlockingExec = Executors.newFixedThreadPool(options.getInternalBlockingPoolSize(),
                new VertxThreadFactory("vert.x-internal-blocking-", checker, true,
                        options.getMaxWorkerExecuteTime()));
        PoolMetrics internalBlockingPoolMetrics = isMetricsEnabled() ? metrics.createMetrics(internalBlockingExec,
                "worker", "vert.x-internal-blocking", options.getInternalBlockingPoolSize()) : null;
        internalBlockingPool = new WorkerPool(internalBlockingExec, internalBlockingPoolMetrics);
        namedWorkerPools = new HashMap<>();
        workerPool = new WorkerPool(workerExec, workerPoolMetrics);
        defaultWorkerPoolSize = options.getWorkerPoolSize();
        defaultWorkerMaxExecTime = options.getMaxWorkerExecuteTime();

        this.fileResolver = new FileResolver(this);
        this.addressResolver = new AddressResolver(this, options.getAddressResolverOptions());
        this.deploymentManager = new DeploymentManager(this);
        this.haEnabled = options.isClustered() && options.isHAEnabled();
        if (options.isClustered()) {
            this.clusterManager = getClusterManager(options);
            this.clusterManager.setVertx(this);
            this.clusterManager.join(ar -> {
                if (ar.failed()) {
                    log.error("Failed to join cluster", ar.cause());
                    resultHandler.handle(Future.failedFuture(ar.cause()));
                } else {
                    // Provide a memory barrier as we are setting from a different thread
                    synchronized (VertxImpl.this) {
                        haManager = new HAManager(this, deploymentManager, clusterManager, options.getQuorumSize(),
                                options.getHAGroup(), haEnabled);
                        createAndStartEventBus(options, resultHandler);
                    }
                }
            });
        } else {
            this.clusterManager = null;
            createAndStartEventBus(options, resultHandler);
        }
        this.sharedData = new SharedDataImpl(this, clusterManager);
    }

    private void createAndStartEventBus(VertxOptions options, Handler<AsyncResult<Vertx>> resultHandler) {
        if (options.isClustered()) {
            eventBus = new ClusteredEventBus(this, options, clusterManager, haManager);
        } else {
            eventBus = new EventBusImpl(this);
        }
        eventBus.start(ar2 -> {
            if (ar2.succeeded()) {
                // If the metric provider wants to use the event bus, it cannot use it in its constructor as the event bus
                // may not be initialized yet. We invokes the eventBusInitialized so it can starts using the event bus.
                metrics.eventBusInitialized(eventBus);

                if (resultHandler != null) {
                    resultHandler.handle(Future.succeededFuture(this));
                }
            } else {
                log.error("Failed to start event bus", ar2.cause());
            }
        });
    }

    /**
     * @return The FileSystem implementation for the OS
     */
    protected FileSystem getFileSystem() {
        return Utils.isWindows() ? new WindowsFileSystem(this) : new FileSystemImpl(this);
    }

    @Override
    public DatagramSocket createDatagramSocket(DatagramSocketOptions options) {
        return new DatagramSocketImpl(this, options);
    }

    @Override
    public DatagramSocket createDatagramSocket() {
        return createDatagramSocket(new DatagramSocketOptions());
    }

    public NetServer createNetServer(NetServerOptions options) {
        return new NetServerImpl(this, options);
    }

    @Override
    public NetServer createNetServer() {
        return createNetServer(new NetServerOptions());
    }

    public NetClient createNetClient(NetClientOptions options) {
        return new NetClientImpl(this, options);
    }

    @Override
    public NetClient createNetClient() {
        return createNetClient(new NetClientOptions());
    }

    public FileSystem fileSystem() {
        return fileSystem;
    }

    public SharedData sharedData() {
        return sharedData;
    }

    public HttpServer createHttpServer(HttpServerOptions serverOptions) {
        return new HttpServerImpl(this, serverOptions);
    }

    @Override
    public HttpServer createHttpServer() {
        return createHttpServer(new HttpServerOptions());
    }

    public HttpClient createHttpClient(HttpClientOptions options) {
        return new HttpClientImpl(this, options);
    }

    @Override
    public HttpClient createHttpClient() {
        return createHttpClient(new HttpClientOptions());
    }

    public EventBus eventBus() {
        if (eventBus == null) {
            // If reading from different thread possibility that it's been set but not visible - so provide
            // memory barrier
            synchronized (this) {
                return eventBus;
            }
        }
        return eventBus;
    }

    public long setPeriodic(long delay, Handler<Long> handler) {
        return scheduleTimeout(getOrCreateContext(), handler, delay, true);
    }

    @Override
    public TimeoutStream periodicStream(long delay) {
        return new TimeoutStreamImpl(delay, true);
    }

    public long setTimer(long delay, Handler<Long> handler) {
        return scheduleTimeout(getOrCreateContext(), handler, delay, false);
    }

    @Override
    public TimeoutStream timerStream(long delay) {
        return new TimeoutStreamImpl(delay, false);
    }

    public void runOnContext(Handler<Void> task) {
        ContextImpl context = getOrCreateContext();
        context.runOnContext(task);
    }

    // The background pool is used for making blocking calls to legacy synchronous APIs
    public ExecutorService getWorkerPool() {
        return workerPool.executor();
    }

    public EventLoopGroup getEventLoopGroup() {
        return eventLoopGroup;
    }

    public EventLoopGroup getAcceptorEventLoopGroup() {
        return acceptorEventLoopGroup;
    }

    public ContextImpl getOrCreateContext() {
        ContextImpl ctx = getContext();
        if (ctx == null) {
            // We are running embedded - Create a context
            ctx = createEventLoopContext(null, null, new JsonObject(),
                    Thread.currentThread().getContextClassLoader());
        }
        return ctx;
    }

    public Map<ServerID, HttpServerImpl> sharedHttpServers() {
        return sharedHttpServers;
    }

    public Map<ServerID, NetServerBase> sharedNetServers() {
        return sharedNetServers;
    }

    @Override
    public boolean isMetricsEnabled() {
        return metrics != null && metrics.isEnabled();
    }

    @Override
    public Metrics getMetrics() {
        return metrics;
    }

    public boolean cancelTimer(long id) {
        InternalTimerHandler handler = timeouts.remove(id);
        if (handler != null) {
            handler.context.removeCloseHook(handler);
            return handler.cancel();
        } else {
            return false;
        }
    }

    public EventLoopContext createEventLoopContext(String deploymentID, WorkerPool workerPool, JsonObject config,
            ClassLoader tccl) {
        return new EventLoopContext(this, internalBlockingPool, workerPool != null ? workerPool : this.workerPool,
                deploymentID, config, tccl);
    }

    public ContextImpl createWorkerContext(boolean multiThreaded, String deploymentID, WorkerPool workerPool,
            JsonObject config, ClassLoader tccl) {
        if (workerPool == null) {
            workerPool = this.workerPool;
        }
        if (multiThreaded) {
            return new MultiThreadedWorkerContext(this, internalBlockingPool, workerPool, deploymentID, config,
                    tccl);
        } else {
            return new WorkerContext(this, internalBlockingPool, workerPool, deploymentID, config, tccl);
        }
    }

    @Override
    public DnsClient createDnsClient(int port, String host) {
        return new DnsClientImpl(this, port, host);
    }

    private VertxMetrics initialiseMetrics(VertxOptions options) {
        if (options.getMetricsOptions() != null && options.getMetricsOptions().isEnabled()) {
            VertxMetricsFactory factory = options.getMetricsOptions().getFactory();
            if (factory == null) {
                factory = ServiceHelper.loadFactoryOrNull(VertxMetricsFactory.class);
                if (factory == null) {
                    log.warn("Metrics has been set to enabled but no VertxMetricsFactory found on classpath");
                }
            }
            if (factory != null) {
                VertxMetrics metrics = factory.metrics(this, options);
                Objects.requireNonNull(metrics, "The metric instance created from " + factory + " cannot be null");
                return metrics;
            }
        }
        return DummyVertxMetrics.INSTANCE;
    }

    private ClusterManager getClusterManager(VertxOptions options) {
        if (options.isClustered()) {
            if (options.getClusterManager() != null) {
                return options.getClusterManager();
            } else {
                ClusterManager mgr;
                String clusterManagerClassName = System.getProperty("vertx.cluster.managerClass");
                if (clusterManagerClassName != null) {
                    // We allow specify a sys prop for the cluster manager factory which overrides ServiceLoader
                    try {
                        Class<?> clazz = Class.forName(clusterManagerClassName);
                        mgr = (ClusterManager) clazz.newInstance();
                    } catch (Exception e) {
                        throw new IllegalStateException("Failed to instantiate " + clusterManagerClassName, e);
                    }
                } else {
                    mgr = ServiceHelper.loadFactoryOrNull(ClusterManager.class);
                    if (mgr == null) {
                        throw new IllegalStateException("No ClusterManagerFactory instances found on classpath");
                    }
                }
                return mgr;
            }
        } else {
            return null;
        }
    }

    private long scheduleTimeout(ContextImpl context, Handler<Long> handler, long delay, boolean periodic) {
        if (delay < 1) {
            throw new IllegalArgumentException("Cannot schedule a timer with delay < 1 ms");
        }
        long timerId = timeoutCounter.getAndIncrement();
        InternalTimerHandler task = new InternalTimerHandler(timerId, handler, periodic, delay, context);
        timeouts.put(timerId, task);
        context.addCloseHook(task);
        return timerId;
    }

    public static Context context() {
        Thread current = Thread.currentThread();
        if (current instanceof VertxThread) {
            return ((VertxThread) current).getContext();
        }
        return null;
    }

    public ContextImpl getContext() {
        ContextImpl context = (ContextImpl) context();
        if (context != null && context.owner == this) {
            return context;
        }
        return null;
    }

    public ClusterManager getClusterManager() {
        return clusterManager;
    }

    @Override
    public void close() {
        close(null);
    }

    private void closeClusterManager(Handler<AsyncResult<Void>> completionHandler) {
        if (clusterManager != null) {
            // Workaround fo Hazelcast bug https://github.com/hazelcast/hazelcast/issues/5220
            if (clusterManager instanceof ExtendedClusterManager) {
                ExtendedClusterManager ecm = (ExtendedClusterManager) clusterManager;
                ecm.beforeLeave();
            }
            clusterManager.leave(ar -> {
                if (ar.failed()) {
                    log.error("Failed to leave cluster", ar.cause());
                }
                if (completionHandler != null) {
                    runOnContext(v -> completionHandler.handle(Future.succeededFuture()));
                }
            });
        } else if (completionHandler != null) {
            runOnContext(v -> completionHandler.handle(Future.succeededFuture()));
        }
    }

    @Override
    public synchronized void close(Handler<AsyncResult<Void>> completionHandler) {
        if (closed || eventBus == null) {
            // Just call the handler directly since pools shutdown
            if (completionHandler != null) {
                completionHandler.handle(Future.succeededFuture());
            }
            return;
        }
        closed = true;

        closeHooks.run(ar -> {
            deploymentManager.undeployAll(ar1 -> {
                if (haManager() != null) {
                    haManager().stop();
                }
                addressResolver.close(ar2 -> {
                    eventBus.close(ar3 -> {
                        closeClusterManager(ar4 -> {
                            // Copy set to prevent ConcurrentModificationException
                            Set<HttpServer> httpServers = new HashSet<>(sharedHttpServers.values());
                            Set<NetServerBase> netServers = new HashSet<>(sharedNetServers.values());
                            sharedHttpServers.clear();
                            sharedNetServers.clear();

                            int serverCount = httpServers.size() + netServers.size();

                            AtomicInteger serverCloseCount = new AtomicInteger();

                            Handler<AsyncResult<Void>> serverCloseHandler = res -> {
                                if (res.failed()) {
                                    log.error("Failure in shutting down server", res.cause());
                                }
                                if (serverCloseCount.incrementAndGet() == serverCount) {
                                    deleteCacheDirAndShutdown(completionHandler);
                                }
                            };

                            for (HttpServer server : httpServers) {
                                server.close(serverCloseHandler);
                            }
                            for (NetServerBase server : netServers) {
                                server.close(serverCloseHandler);
                            }
                            if (serverCount == 0) {
                                deleteCacheDirAndShutdown(completionHandler);
                            }
                        });
                    });
                });
            });
        });
    }

    @Override
    public void deployVerticle(Verticle verticle) {
        deployVerticle(verticle, new DeploymentOptions(), null);
    }

    @Override
    public void deployVerticle(Verticle verticle, Handler<AsyncResult<String>> completionHandler) {
        deployVerticle(verticle, new DeploymentOptions(), completionHandler);
    }

    @Override
    public void deployVerticle(String name, Handler<AsyncResult<String>> completionHandler) {
        deployVerticle(name, new DeploymentOptions(), completionHandler);
    }

    @Override
    public void deployVerticle(Verticle verticle, DeploymentOptions options) {
        deployVerticle(verticle, options, null);
    }

    @Override
    public void deployVerticle(Verticle verticle, DeploymentOptions options,
            Handler<AsyncResult<String>> completionHandler) {
        boolean closed;
        synchronized (this) {
            closed = this.closed;
        }
        if (closed) {
            if (completionHandler != null) {
                completionHandler.handle(Future.failedFuture("Vert.x closed"));
            }
        } else {
            deploymentManager.deployVerticle(verticle, options, completionHandler);
        }
    }

    @Override
    public void deployVerticle(String name) {
        deployVerticle(name, new DeploymentOptions(), null);
    }

    @Override
    public void deployVerticle(String name, DeploymentOptions options) {
        deployVerticle(name, options, null);
    }

    @Override
    public void deployVerticle(String name, DeploymentOptions options,
            Handler<AsyncResult<String>> completionHandler) {
        if (options.isHa() && haManager() != null && haManager().isEnabled()) {
            haManager().deployVerticle(name, options, completionHandler);
        } else {
            deploymentManager.deployVerticle(name, options, completionHandler);
        }
    }

    @Override
    public String getNodeID() {
        return clusterManager.getNodeID();
    }

    @Override
    public void undeploy(String deploymentID) {
        undeploy(deploymentID, res -> {
        });
    }

    @Override
    public void undeploy(String deploymentID, Handler<AsyncResult<Void>> completionHandler) {
        if (haManager() != null && haManager().isEnabled()) {
            haManager().removeFromHA(deploymentID);
        }
        deploymentManager.undeployVerticle(deploymentID, completionHandler);
    }

    @Override
    public Set<String> deploymentIDs() {
        return deploymentManager.deployments();
    }

    @Override
    public void registerVerticleFactory(VerticleFactory factory) {
        deploymentManager.registerVerticleFactory(factory);
    }

    @Override
    public void unregisterVerticleFactory(VerticleFactory factory) {
        deploymentManager.unregisterVerticleFactory(factory);
    }

    @Override
    public Set<VerticleFactory> verticleFactories() {
        return deploymentManager.verticleFactories();
    }

    @Override
    public <T> void executeBlockingInternal(Action<T> action, Handler<AsyncResult<T>> resultHandler) {
        ContextImpl context = getOrCreateContext();

        context.executeBlocking(action, resultHandler);
    }

    @Override
    public <T> void executeBlocking(Handler<Future<T>> blockingCodeHandler, boolean ordered,
            Handler<AsyncResult<T>> asyncResultHandler) {
        ContextImpl context = getOrCreateContext();
        context.executeBlocking(blockingCodeHandler, ordered, asyncResultHandler);
    }

    @Override
    public <T> void executeBlocking(Handler<Future<T>> blockingCodeHandler,
            Handler<AsyncResult<T>> asyncResultHandler) {
        executeBlocking(blockingCodeHandler, true, asyncResultHandler);
    }

    @Override
    public boolean isClustered() {
        return clusterManager != null;
    }

    @Override
    public EventLoopGroup nettyEventLoopGroup() {
        return eventLoopGroup;
    }

    // For testing
    public void simulateKill() {
        if (haManager() != null) {
            haManager().simulateKill();
        }
    }

    @Override
    public Deployment getDeployment(String deploymentID) {
        return deploymentManager.getDeployment(deploymentID);
    }

    @Override
    public synchronized void failoverCompleteHandler(FailoverCompleteHandler failoverCompleteHandler) {
        if (haManager() != null) {
            haManager().setFailoverCompleteHandler(failoverCompleteHandler);
        }
    }

    @Override
    public boolean isKilled() {
        return haManager().isKilled();
    }

    @Override
    public void failDuringFailover(boolean fail) {
        if (haManager() != null) {
            haManager().failDuringFailover(fail);
        }
    }

    @Override
    public VertxMetrics metricsSPI() {
        return metrics;
    }

    @Override
    public File resolveFile(String fileName) {
        return fileResolver.resolveFile(fileName);
    }

    @Override
    public void resolveAddress(String hostname, Handler<AsyncResult<InetAddress>> resultHandler) {
        addressResolver.resolveHostname(hostname, resultHandler);
    }

    @Override
    public AddressResolver addressResolver() {
        return addressResolver;
    }

    @Override
    public AddressResolverGroup<InetSocketAddress> nettyAddressResolverGroup() {
        return addressResolver.nettyAddressResolverGroup();
    }

    @SuppressWarnings("unchecked")
    private void deleteCacheDirAndShutdown(Handler<AsyncResult<Void>> completionHandler) {
        fileResolver.close(res -> {

            workerPool.close();
            internalBlockingPool.close();
            new ArrayList<>(namedWorkerPools.values()).forEach(WorkerPool::close);

            acceptorEventLoopGroup.shutdownGracefully(0, 10, TimeUnit.SECONDS)
                    .addListener(new GenericFutureListener() {
                        @Override
                        public void operationComplete(io.netty.util.concurrent.Future future) throws Exception {
                            if (!future.isSuccess()) {
                                log.warn("Failure in shutting down acceptor event loop group", future.cause());
                            }
                            eventLoopGroup.shutdownGracefully(0, 10, TimeUnit.SECONDS)
                                    .addListener(new GenericFutureListener() {
                                        @Override
                                        public void operationComplete(io.netty.util.concurrent.Future future)
                                                throws Exception {
                                            if (!future.isSuccess()) {
                                                log.warn("Failure in shutting down event loop group",
                                                        future.cause());
                                            }
                                            if (metrics != null) {
                                                metrics.close();
                                            }

                                            checker.close();

                                            if (completionHandler != null) {
                                                eventLoopThreadFactory.newThread(() -> {
                                                    completionHandler.handle(Future.succeededFuture());
                                                }).start();
                                            }
                                        }
                                    });
                        }
                    });
        });
    }

    private HAManager haManager() {
        // If reading from different thread possibility that it's been set but not visible - so provide
        // memory barrier
        if (haManager == null && haEnabled) {
            synchronized (this) {
                return haManager;
            }
        } else {
            return haManager;
        }
    }

    private class InternalTimerHandler implements Handler<Void>, Closeable {
        final Handler<Long> handler;
        final boolean periodic;
        final long timerID;
        final ContextImpl context;
        final java.util.concurrent.Future<?> future;
        final AtomicBoolean cancelled;

        boolean cancel() {
            if (cancelled.compareAndSet(false, true)) {
                metrics.timerEnded(timerID, true);
                future.cancel(false);
                return true;
            } else {
                return false;
            }
        }

        InternalTimerHandler(long timerID, Handler<Long> runnable, boolean periodic, long delay,
                ContextImpl context) {
            this.context = context;
            this.timerID = timerID;
            this.handler = runnable;
            this.periodic = periodic;
            this.cancelled = new AtomicBoolean();
            EventLoop el = context.nettyEventLoop();
            Runnable toRun = () -> context.runOnContext(this);
            if (periodic) {
                future = el.scheduleAtFixedRate(toRun, delay, delay, TimeUnit.MILLISECONDS);
            } else {
                future = el.schedule(toRun, delay, TimeUnit.MILLISECONDS);
            }
            metrics.timerCreated(timerID);
        }

        public void handle(Void v) {
            if (!cancelled.get()) {
                try {
                    handler.handle(timerID);
                } finally {
                    if (!periodic) {
                        // Clean up after it's fired
                        cleanupNonPeriodic();
                    }
                }
            }
        }

        private void cleanupNonPeriodic() {
            VertxImpl.this.timeouts.remove(timerID);
            metrics.timerEnded(timerID, false);
            ContextImpl context = getContext();
            if (context != null) {
                context.removeCloseHook(this);
            }
        }

        // Called via Context close hook when Verticle is undeployed
        public void close(Handler<AsyncResult<Void>> completionHandler) {
            VertxImpl.this.timeouts.remove(timerID);
            cancel();
            completionHandler.handle(Future.succeededFuture());
        }

    }

    /*
     *
     * This class is optimised for performance when used on the same event loop that is was passed to the handler with.
     * However it can be used safely from other threads.
     *
     * The internal state is protected using the synchronized keyword. If always used on the same event loop, then
     * we benefit from biased locking which makes the overhead of synchronized near zero.
     *
     */
    private class TimeoutStreamImpl implements TimeoutStream, Handler<Long> {

        private final long delay;
        private final boolean periodic;

        private boolean paused;
        private Long id;
        private Handler<Long> handler;
        private Handler<Void> endHandler;

        public TimeoutStreamImpl(long delay, boolean periodic) {
            this.delay = delay;
            this.periodic = periodic;
        }

        @Override
        public synchronized void handle(Long event) {
            try {
                if (!paused) {
                    handler.handle(event);
                }
            } finally {
                if (!periodic && endHandler != null) {
                    endHandler.handle(null);
                }
            }
        }

        @Override
        public TimeoutStream exceptionHandler(Handler<Throwable> handler) {
            return this;
        }

        @Override
        public void cancel() {
            if (id != null) {
                VertxImpl.this.cancelTimer(id);
            }
        }

        @Override
        public synchronized TimeoutStream handler(Handler<Long> handler) {
            if (handler != null) {
                if (id != null) {
                    throw new IllegalStateException();
                }
                this.handler = handler;
                id = scheduleTimeout(getOrCreateContext(), this, delay, periodic);
            } else {
                cancel();
            }
            return this;
        }

        @Override
        public synchronized TimeoutStream pause() {
            this.paused = true;
            return this;
        }

        @Override
        public synchronized TimeoutStream resume() {
            this.paused = false;
            return this;
        }

        @Override
        public synchronized TimeoutStream endHandler(Handler<Void> endHandler) {
            this.endHandler = endHandler;
            return this;
        }
    }

    class SharedWorkerPool extends WorkerPool {

        private final String name;
        private int refCount = 1;

        SharedWorkerPool(String name, ExecutorService workerExec, PoolMetrics workerMetrics) {
            super(workerExec, workerMetrics);
            this.name = name;
        }

        @Override
        void close() {
            synchronized (VertxImpl.this) {
                if (refCount > 0) {
                    refCount = 0;
                    super.close();
                }
            }
        }

        void release() {
            synchronized (VertxImpl.this) {
                if (--refCount == 0) {
                    releaseWorkerExecutor(name);
                    super.close();
                }
            }
        }
    }

    @Override
    public WorkerExecutorImpl createSharedWorkerExecutor(String name) {
        return createSharedWorkerExecutor(name, defaultWorkerPoolSize);
    }

    @Override
    public WorkerExecutorImpl createSharedWorkerExecutor(String name, int poolSize) {
        return createSharedWorkerExecutor(name, poolSize, defaultWorkerMaxExecTime);
    }

    @Override
    public synchronized WorkerExecutorImpl createSharedWorkerExecutor(String name, int poolSize,
            long maxExecuteTime) {
        if (poolSize < 1) {
            throw new IllegalArgumentException("poolSize must be > 0");
        }
        if (maxExecuteTime < 1) {
            throw new IllegalArgumentException("maxExecuteTime must be > 0");
        }
        SharedWorkerPool sharedWorkerPool = namedWorkerPools.get(name);
        if (sharedWorkerPool == null) {
            ExecutorService workerExec = Executors.newFixedThreadPool(poolSize,
                    new VertxThreadFactory(name + "-", checker, true, maxExecuteTime));
            PoolMetrics workerMetrics = isMetricsEnabled()
                    ? metrics.createMetrics(workerExec, "worker", name, poolSize)
                    : null;
            namedWorkerPools.put(name, sharedWorkerPool = new SharedWorkerPool(name, workerExec, workerMetrics));
        } else {
            sharedWorkerPool.refCount++;
        }
        ContextImpl context = getOrCreateContext();
        WorkerExecutorImpl namedExec = new WorkerExecutorImpl(this, sharedWorkerPool, true);
        context.addCloseHook(namedExec);
        return namedExec;
    }

    synchronized void releaseWorkerExecutor(String name) {
        namedWorkerPools.remove(name);
    }

    @Override
    public Vertx exceptionHandler(Handler<Throwable> handler) {
        exceptionHandler = handler;
        return this;
    }

    @Override
    public Handler<Throwable> exceptionHandler() {
        return exceptionHandler;
    }

    @Override
    public void addCloseHook(Closeable hook) {
        closeHooks.add(hook);
    }

    @Override
    public void removeCloseHook(Closeable hook) {
        closeHooks.remove(hook);
    }
}