Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.vertx.core.impl.DeploymentManager Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package io.vertx.core.impl;
import io.vertx.core.*;
import io.vertx.core.json.JsonObject;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
/**
* @author Tim Fox
*/
public class DeploymentManager {
private static final Logger log = LoggerFactory.getLogger(DeploymentManager.class);
private final VertxImpl vertx;
private final Map deployments = new ConcurrentHashMap<>();
public DeploymentManager(VertxImpl vertx) {
this.vertx = vertx;
}
private String generateDeploymentID() {
return UUID.randomUUID().toString();
}
public Future deployVerticle(Callable verticleSupplier, DeploymentOptions options) {
if (options.getInstances() < 1) {
throw new IllegalArgumentException("Can't specify < 1 instances to deploy");
}
options.checkIsolationNotDefined();
ContextInternal currentContext = vertx.getOrCreateContext();
ClassLoader cl = options.getClassLoader();
if (cl == null) {
cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = getClass().getClassLoader();
}
}
return doDeploy(options, v -> "java:" + v.getClass().getName(), currentContext, currentContext, cl, verticleSupplier)
.map(Deployment::deploymentID);
}
public Future undeployVerticle(String deploymentID) {
Deployment deployment = deployments.get(deploymentID);
Context currentContext = vertx.getOrCreateContext();
if (deployment == null) {
return ((ContextInternal) currentContext).failedFuture(new IllegalStateException("Unknown deployment"));
} else {
return deployment.doUndeploy(vertx.getOrCreateContext());
}
}
public Set deployments() {
return Collections.unmodifiableSet(deployments.keySet());
}
public Deployment getDeployment(String deploymentID) {
return deployments.get(deploymentID);
}
public Future undeployAll() {
// TODO timeout if it takes too long - e.g. async stop verticle fails to call future
// We only deploy the top level verticles as the children will be undeployed when the parent is
Set deploymentIDs = new HashSet<>();
for (Map.Entry entry: deployments.entrySet()) {
if (!entry.getValue().isChild()) {
deploymentIDs.add(entry.getKey());
}
}
List> completionList = new ArrayList<>();
if (!deploymentIDs.isEmpty()) {
for (String deploymentID : deploymentIDs) {
Promise promise = Promise.promise();
completionList.add(promise.future());
undeployVerticle(deploymentID).onComplete(ar -> {
if (ar.failed()) {
// Log but carry on regardless
log.error("Undeploy failed", ar.cause());
}
promise.handle(ar);
});
}
Promise promise = vertx.getOrCreateContext().promise();
Future.join(completionList).mapEmpty().onComplete(promise);
return promise.future();
} else {
return vertx.getOrCreateContext().succeededFuture();
}
}
private void reportFailure(Throwable t, Context context, Handler> completionHandler) {
if (completionHandler != null) {
reportResult(context, completionHandler, Future.failedFuture(t));
} else {
log.error(t.getMessage(), t);
}
}
private void reportResult(Context context, Handler> completionHandler, AsyncResult result) {
context.runOnContext(v -> {
try {
completionHandler.handle(result);
} catch (Throwable t) {
log.error("Failure in calling handler", t);
throw t;
}
});
}
Future doDeploy(DeploymentOptions options,
Function identifierProvider,
ContextInternal parentContext,
ContextInternal callingContext,
ClassLoader tccl, Callable verticleSupplier) {
int nbInstances = options.getInstances();
Set verticles = Collections.newSetFromMap(new IdentityHashMap<>());
for (int i = 0; i < nbInstances; i++) {
Verticle verticle;
try {
verticle = verticleSupplier.call();
} catch (Exception e) {
return Future.failedFuture(e);
}
if (verticle == null) {
return Future.failedFuture("Supplied verticle is null");
}
verticles.add(verticle);
}
if (verticles.size() != nbInstances) {
return Future.failedFuture("Same verticle supplied more than once");
}
Verticle[] verticlesArray = verticles.toArray(new Verticle[0]);
return doDeploy(identifierProvider.apply(verticlesArray[0]), options, parentContext, callingContext, tccl, verticlesArray);
}
private Future doDeploy(String identifier,
DeploymentOptions options,
ContextInternal parentContext,
ContextInternal callingContext,
ClassLoader tccl, Verticle... verticles) {
Promise promise = callingContext.promise();
Deployment parent = parentContext.getDeployment();
String deploymentID = generateDeploymentID();
AtomicInteger deployCount = new AtomicInteger();
AtomicBoolean failureReported = new AtomicBoolean();
WorkerPool workerPool = null;
ThreadingModel mode = options.getThreadingModel();
if (mode == null) {
mode = ThreadingModel.EVENT_LOOP;
}
if (mode != ThreadingModel.VIRTUAL_THREAD) {
if (options.getWorkerPoolName() != null) {
workerPool = vertx.createSharedWorkerPool(options.getWorkerPoolName(), options.getWorkerPoolSize(), options.getMaxWorkerExecuteTime(), options.getMaxWorkerExecuteTimeUnit());
}
} else {
if (!vertx.isVirtualThreadAvailable()) {
return callingContext.failedFuture("This Java runtime does not support virtual threads");
}
}
DeploymentImpl deployment = new DeploymentImpl(parent, workerPool, deploymentID, identifier, options);
for (Verticle verticle: verticles) {
CloseFuture closeFuture = new CloseFuture(log);
ContextImpl context;
switch (mode) {
default:
context = vertx.createEventLoopContext(deployment, closeFuture, workerPool, tccl);
break;
case WORKER:
context = vertx.createWorkerContext(deployment, closeFuture, workerPool, tccl);
break;
case VIRTUAL_THREAD:
context = vertx.createVirtualThreadContext(deployment, closeFuture, tccl);
break;
}
VerticleHolder holder = new VerticleHolder(verticle, context, closeFuture);
deployment.addVerticle(holder);
context.runOnContext(v -> {
try {
verticle.init(vertx, context);
Promise startPromise = context.promise();
Future startFuture = startPromise.future();
verticle.start(startPromise);
startFuture.onComplete(ar -> {
if (ar.succeeded()) {
if (parent != null) {
if (parent.addChild(deployment)) {
deployment.child = true;
} else {
// Orphan
deployment.doUndeploy(vertx.getOrCreateContext()).onComplete(ar2 -> promise.fail("Verticle deployment failed.Could not be added as child of parent verticle"));
return;
}
}
deployments.put(deploymentID, deployment);
if (deployCount.incrementAndGet() == verticles.length) {
promise.complete(deployment);
}
} else if (failureReported.compareAndSet(false, true)) {
deployment.rollback(callingContext, promise, context, holder, ar.cause());
}
});
} catch (Throwable t) {
if (failureReported.compareAndSet(false, true))
deployment.rollback(callingContext, promise, context, holder, t);
}
});
}
return promise.future();
}
static class VerticleHolder {
final Verticle verticle;
final ContextImpl context;
final CloseFuture closeFuture;
VerticleHolder(Verticle verticle, ContextImpl context, CloseFuture closeFuture) {
this.verticle = verticle;
this.context = context;
this.closeFuture = closeFuture;
}
void close(Handler> completionHandler) {
closeFuture.close().onComplete(completionHandler);
}
}
private class DeploymentImpl implements Deployment {
private static final int ST_DEPLOYED = 0, ST_UNDEPLOYING = 1, ST_UNDEPLOYED = 2;
private final Deployment parent;
private final String deploymentID;
private final JsonObject conf;
private final String verticleIdentifier;
private final List verticles = new CopyOnWriteArrayList<>();
private final Set children = new ConcurrentHashSet<>();
private final WorkerPool workerPool;
private final DeploymentOptions options;
private Handler undeployHandler;
private int status = ST_DEPLOYED;
private volatile boolean child;
private DeploymentImpl(Deployment parent, WorkerPool workerPool, String deploymentID, String verticleIdentifier, DeploymentOptions options) {
this.parent = parent;
this.deploymentID = deploymentID;
this.conf = options.getConfig() != null ? options.getConfig().copy() : new JsonObject();
this.verticleIdentifier = verticleIdentifier;
this.options = options;
this.workerPool = workerPool;
}
public void addVerticle(VerticleHolder holder) {
verticles.add(holder);
}
private synchronized void rollback(ContextInternal callingContext, Handler> completionHandler, ContextImpl context, VerticleHolder closeFuture, Throwable cause) {
if (status == ST_DEPLOYED) {
status = ST_UNDEPLOYING;
doUndeployChildren(callingContext).onComplete(childrenResult -> {
if (workerPool != null) {
workerPool.close();
}
Handler handler;
synchronized (DeploymentImpl.this) {
status = ST_UNDEPLOYED;
handler = undeployHandler;
undeployHandler = null;
}
if (handler != null) {
try {
handler.handle(null);
} catch (Exception e) {
context.reportException(e);
}
}
if (childrenResult.failed()) {
reportFailure(cause, callingContext, completionHandler);
} else {
closeFuture.close(closeHookAsyncResult -> reportFailure(cause, callingContext, completionHandler));
}
});
}
}
private synchronized Future doUndeployChildren(ContextInternal undeployingContext) {
if (!children.isEmpty()) {
List> undeployFutures = new ArrayList<>();
for (Deployment childDeployment: new HashSet<>(children)) {
Promise p = Promise.promise();
undeployFutures.add(p.future());
childDeployment.doUndeploy(undeployingContext).onComplete(ar -> {
children.remove(childDeployment);
p.handle(ar);
});
}
return Future.all(undeployFutures).mapEmpty();
} else {
return Future.succeededFuture();
}
}
public synchronized Future doUndeploy(ContextInternal undeployingContext) {
if (status == ST_UNDEPLOYED) {
return Future.failedFuture(new IllegalStateException("Already undeployed"));
}
if (!children.isEmpty()) {
status = ST_UNDEPLOYING;
return doUndeployChildren(undeployingContext).compose(v -> doUndeploy(undeployingContext));
} else {
status = ST_UNDEPLOYED;
List> undeployFutures = new ArrayList<>();
if (parent != null) {
parent.removeChild(this);
}
for (VerticleHolder verticleHolder: verticles) {
ContextImpl context = verticleHolder.context;
Promise> p = Promise.promise();
undeployFutures.add(p.future());
context.runOnContext(v -> {
Promise stopPromise = undeployingContext.promise();
Future stopFuture = stopPromise.future();
stopFuture.onComplete(ar -> {
deployments.remove(deploymentID);
verticleHolder.close(ar2 -> {
if (ar2.failed()) {
// Log error but we report success anyway
log.error("Failed to run close hook", ar2.cause());
}
if (ar.succeeded()) {
p.complete();
} else if (ar.failed()) {
p.fail(ar.cause());
}
});
});
try {
verticleHolder.verticle.stop(stopPromise);
} catch (Throwable t) {
if (!stopPromise.tryFail(t)) {
undeployingContext.reportException(t);
}
}
});
}
Promise resolvingPromise = undeployingContext.promise();
Future.all(undeployFutures).mapEmpty().onComplete(resolvingPromise);
Future fut = resolvingPromise.future();
if (workerPool != null) {
fut = fut.andThen(ar -> workerPool.close());
}
Handler handler = undeployHandler;
if (handler != null) {
undeployHandler = null;
return fut.compose(v -> {
handler.handle(null);
return Future.succeededFuture();
}, v -> {
handler.handle(null);
return Future.succeededFuture();
});
}
return fut;
}
}
@Override
public String verticleIdentifier() {
return verticleIdentifier;
}
@Override
public DeploymentOptions deploymentOptions() {
return options;
}
@Override
public JsonObject config() {
return conf;
}
@Override
public synchronized boolean addChild(Deployment deployment) {
if (status == ST_DEPLOYED) {
children.add(deployment);
return true;
} else {
return false;
}
}
@Override
public void removeChild(Deployment deployment) {
children.remove(deployment);
}
@Override
public Set getContexts() {
Set contexts = new HashSet<>();
for (VerticleHolder holder: verticles) {
contexts.add(holder.context);
}
return contexts;
}
@Override
public Set getVerticles() {
Set verts = new HashSet<>();
for (VerticleHolder holder: verticles) {
verts.add(holder.verticle);
}
return verts;
}
@Override
public void undeployHandler(Handler handler) {
synchronized (this) {
if (status != ST_UNDEPLOYED) {
undeployHandler = handler;
return;
}
}
handler.handle(null);
}
@Override
public boolean isChild() {
return child;
}
@Override
public String deploymentID() {
return deploymentID;
}
}
}