Package io.vertx.core.impl

Source Code of io.vertx.core.impl.VertxImpl

/*
* 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.util.ResourceLeakDetector;
import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Handler;
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.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.impl.LoggerFactory;
import io.vertx.core.metrics.impl.DummyVertxMetrics;
import io.vertx.core.metrics.spi.VertxMetrics;
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.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.Action;
import io.vertx.core.spi.cluster.AsyncMultiMap;
import io.vertx.core.spi.cluster.ClusterManager;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.ServiceLoader;
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.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

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

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

  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, NetServerImpl> sharedNetServers = new HashMap<>();
  private final ExecutorService workerPool;
  private final ExecutorService internalBlockingPool;
  private final OrderedExecutorFactory workerOrderedFact;
  private final OrderedExecutorFactory internalOrderedFact;
  private final EventLoopGroup eventLoopGroup;
  private final BlockedThreadChecker checker;
  private final boolean haEnabled;
  private EventBusImpl eventBus;
  private HAManager haManager;
  private boolean closed;

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

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

  VertxImpl(VertxOptions options, Handler<AsyncResult<Vertx>> resultHandler) {
    checker = new BlockedThreadChecker(options.getBlockedThreadCheckPeriod(), options.getMaxEventLoopExecuteTime(),
                                       options.getMaxWorkerExecuteTime());
    eventLoopGroup = new NioEventLoopGroup(options.getEventLoopPoolSize(),
                                           new VertxThreadFactory("vert.x-eventloop-thread-", checker, false));
    workerPool = Executors.newFixedThreadPool(options.getWorkerPoolSize(),
                                              new VertxThreadFactory("vert.x-worker-thread-", checker, true));
    internalBlockingPool = Executors.newFixedThreadPool(options.getInternalBlockingPoolSize(),
                                                        new VertxThreadFactory("vert.x-internal-blocking-", checker, true));
    workerOrderedFact = new OrderedExecutorFactory(workerPool);
    internalOrderedFact = new OrderedExecutorFactory(internalBlockingPool);
    this.fileResolver = new FileResolver(this);
    this.deploymentManager = new DeploymentManager(this);
    this.metrics = initialiseMetrics(options);
    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());
        }
        if (options.isHAEnabled()) {
          // 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());
          }
        }
        clusterManager.<String, ServerID>getAsyncMultiMap("subs", null, ar2 -> {
          if (ar2.succeeded()) {
            AsyncMultiMap<String, ServerID> subs = ar2.result();
            NetServer server = createNetServer(new NetServerOptions().setPort(options.getClusterPort()).setHost(options.getClusterHost()));
            EventBusImpl.EventBusNetServer ebServer = new EventBusImpl.EventBusNetServer(server);
            server.listen(asyncResult -> {
              if (asyncResult.succeeded()) {
                // Obtain system configured public host/port
                int publicPort = Integer.getInteger("vertx.cluster.public.port", -1);
                String publicHost = System.getProperty("vertx.cluster.public.host", null);

                // If using a wilcard port (0) then we ask the server for the actual port:
                int serverPort = publicPort == -1 ? server.actualPort() : publicPort;
                String serverHost = publicHost == null ? options.getClusterHost() : publicHost;
                ServerID serverID = new ServerID(serverPort, serverHost);
                // Provide a memory barrier as we are setting from a different thread
                synchronized (VertxImpl.this) {
                  eventBus = new EventBusImpl(this, options.getClusterPingInterval(), options.getClusterPingReplyInterval(),
                    clusterManager, subs, serverID, ebServer);
                }
                if (resultHandler != null) {
                  resultHandler.handle(Future.completedFuture(this));
                }
              } else {
                if (resultHandler != null) {
                  resultHandler.handle(Future.completedFuture(ar.cause()));
                } else {
                  log.error(ar.cause());
                }
             }
            });
          } else {
            if (resultHandler != null) {
              resultHandler.handle(Future.completedFuture(ar.cause()));
            } else {
              log.error(ar.cause());
            }
          }
        });
      });
      this.sharedData = new SharedDataImpl(this, clusterManager);
    } else {
      this.clusterManager = null;
      this.sharedData = new SharedDataImpl(this, null);
      this.eventBus = new EventBusImpl(this);
      if (resultHandler != null) {
        resultHandler.handle(Future.completedFuture(this));
      }
    }
  }

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

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

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

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

  public FileSystem fileSystem() {
    return fileSystem;
  }

  public SharedData sharedData() {
    return sharedData;
  }

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

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

  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);
  }

  public Context context() {
    return getOrCreateContext();
  }

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

  public EventLoopGroup getEventLoopGroup() {
    return eventLoopGroup;
  }

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

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

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

  @Override
  public String metricBaseName() {
    return metrics.baseName();
  }

  @Override
  public Map<String, JsonObject> metrics() {
    String name = metricBaseName();
    return metrics.metrics().entrySet().stream()
      .filter(e -> e.getKey().startsWith(name))
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
  }

  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, JsonObject config, ClassLoader tccl) {
    return new EventLoopContext(this, workerOrderedFact.getExecutor(), deploymentID, config, tccl);
  }

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

  private VertxMetrics initialiseMetrics(VertxOptions options) {
    if (options.isMetricsEnabled()) {
      ServiceLoader<VertxMetricsFactory> factories = ServiceLoader.load(VertxMetricsFactory.class);
      if (factories.iterator().hasNext()) {
        VertxMetricsFactory factory = factories.iterator().next();
        return factory.metrics(this, options);
      } else {
        log.warn("Metrics has been set to enabled but no VertxMetricsFactory found on classpath");
        return new DummyVertxMetrics();
      }
    } else {
      return new DummyVertxMetrics();
    }
  }

  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 {
          ServiceLoader<ClusterManager> mgrs = ServiceLoader.load(ClusterManager.class);
          if (!mgrs.iterator().hasNext()) {
            throw new IllegalStateException("No ClusterManagerFactory instances found on classpath");
          }
          mgr = mgrs.iterator().next();
        }
        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 ContextImpl createWorkerContext(boolean multiThreaded, String deploymentID, JsonObject config,
                                         ClassLoader tccl) {
    if (multiThreaded) {
      return new MultiThreadedWorkerContext(this, internalOrderedFact.getExecutor(), workerPool, deploymentID, config, tccl);
    } else {
      return new WorkerContext(this, internalOrderedFact.getExecutor(), workerOrderedFact.getExecutor(), deploymentID,
                               config, tccl);
    }
  }

  public void setContext(ContextImpl context) {
    Thread current = Thread.currentThread();
    if (current instanceof VertxThread) {
      ((VertxThread)current).setContext(context);
    }
    if (context != null) {
      context.setTCCL();
    } else {
      Thread.currentThread().setContextClassLoader(null);
    }
  }

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

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

  @Override
  public synchronized void close(Handler<AsyncResult<Void>> completionHandler) {
    if (closed || eventBus == null) {
      runOnContext(v -> {
        completionHandler.handle(Future.completedFuture());
      });
    }
    closed = true;
    deploymentManager.undeployAll(ar -> {
      if (haManager() != null) {
        haManager().stop();
      }
      eventBus.close(ar2 -> {

        if (sharedHttpServers != null) {
          // Copy set to prevent ConcurrentModificationException
          for (HttpServer server : new HashSet<>(sharedHttpServers.values())) {
            server.close();
          }
          sharedHttpServers.clear();
        }

        if (sharedNetServers != null) {
          // Copy set to prevent ConcurrentModificationException
          for (NetServer server : new HashSet<>(sharedNetServers.values())) {
            server.close();
          }
          sharedNetServers.clear();
        }

        fileResolver.deleteCacheDir(res -> {

          workerPool.shutdownNow();
          internalBlockingPool.shutdownNow();
          eventLoopGroup.shutdownNow();

          if (metrics != null) {
            metrics.close();
          }

          checker.close();

          setContext(null);

          if (completionHandler != null) {
            // Call directly - we have no context
            completionHandler.handle(Future.completedFuture());
          }
        });

      });
    });
  }

  @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 identifier, Handler<AsyncResult<String>> completionHandler) {
    deployVerticle(identifier, 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) {
    deploymentManager.deployVerticle(verticle, options, completionHandler);
  }

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

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

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

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

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

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

  @Override
  public Set<String> deployments() {
    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 executeBlocking(Action<T> action, Handler<AsyncResult<T>> resultHandler) {
    ContextImpl context = getOrCreateContext();
    context.executeBlocking(action, resultHandler);
  }

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

  @Override
  public void simulateEventBusUnresponsive() {
    eventBus.simulateUnresponsive();
  }

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

  @Override
  public void failoverCompleteHandler(Handler<Boolean> failoverCompleteHandler) {
    if (haManager() != null) {
      haManager().failoverCompleteHandler(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);
  }

  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;

    boolean cancel() {
      metrics.timerEnded(timerID, true);
      return future.cancel(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;
      EventLoop el = context.getEventLoop();
      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) {
      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();
      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.completedFuture());
    }

  }

  /*
   *
   * 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() {
      handler(null);
    }

    @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 {
        if (id != null) {
          VertxImpl.this.cancelTimer(id);
          if (endHandler != null) {
            runOnContext(endHandler);
          }
        }
      }
      return this;
    }

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

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

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

Related Classes of io.vertx.core.impl.VertxImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.