mirror of
https://github.com/jlengrand/vert.x.git
synced 2026-03-10 08:51:19 +00:00
Isolated deployment becomes a feature exclusive to Java 8 thanks to Multi-Release Jar support. The DeploymentOptions fields are deprecated and documented to be removed when used with Java 11 or above. The documentation is removed. - closes #3274
This commit is contained in:
36
pom.xml
36
pom.xml
@@ -281,10 +281,46 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-jar</id>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Multi-Release>true</Multi-Release>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jdk11</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<compileSourceRoots>
|
||||
<compileSourceRoot>src/main/java11</compileSourceRoot>
|
||||
</compileSourceRoots>
|
||||
<outputDirectory>${project.build.outputDirectory}/META-INF/versions/11/</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.bsc.maven</groupId>
|
||||
<artifactId>maven-processor-plugin</artifactId>
|
||||
|
||||
@@ -357,23 +357,12 @@ Set the send timeout.
|
||||
|[[config]]`@config`|`Json object`|+++
|
||||
Set the JSON configuration that will be passed to the verticle(s) when it's deployed
|
||||
+++
|
||||
|[[extraClasspath]]`@extraClasspath`|`Array of String`|+++
|
||||
Set any extra classpath to be used when deploying the verticle.
|
||||
<p>
|
||||
Ignored if no isolation group is set.
|
||||
+++
|
||||
|[[ha]]`@ha`|`Boolean`|+++
|
||||
Set whether the verticle(s) will be deployed as HA.
|
||||
+++
|
||||
|[[instances]]`@instances`|`Number (int)`|+++
|
||||
Set the number of instances that should be deployed.
|
||||
+++
|
||||
|[[isolatedClasses]]`@isolatedClasses`|`Array of String`|+++
|
||||
Set the isolated class names.
|
||||
+++
|
||||
|[[isolationGroup]]`@isolationGroup`|`String`|+++
|
||||
Set the isolation group that will be used when deploying the verticle(s)
|
||||
+++
|
||||
|[[maxWorkerExecuteTime]]`@maxWorkerExecuteTime`|`Number (long)`|+++
|
||||
Sets the value of max worker execute time, in link.
|
||||
<p>
|
||||
|
||||
@@ -554,39 +554,6 @@ and multiple cores on your machine, so you want to deploy multiple instances to
|
||||
|
||||
include::override/verticle-configuration.adoc[]
|
||||
|
||||
=== Verticle Isolation Groups
|
||||
|
||||
By default, Vert.x has a _flat classpath_. I.e, when Vert.x deploys verticles it does so with the current classloader -
|
||||
it doesn't create a new one. In the majority of cases this is the simplest, clearest, and sanest thing to do.
|
||||
|
||||
However, in some cases you may want to deploy a verticle so the classes of that verticle are isolated from others in
|
||||
your application.
|
||||
|
||||
This might be the case, for example, if you want to deploy two different versions of a verticle with the same class name
|
||||
in the same Vert.x instance, or if you have two different verticles which use different versions of the same jar library.
|
||||
|
||||
When using an isolation group you provide a list of the class names that you want isolated using
|
||||
{@link io.vertx.core.DeploymentOptions#setIsolatedClasses(java.util.List)}- an entry can be a fully qualified
|
||||
classname such as `com.mycompany.myproject.engine.MyClass` or it can be a wildcard which will match any classes in a package and any
|
||||
sub-packages, e.g. `com.mycompany.myproject.*` would match any classes in the package `com.mycompany.myproject` or
|
||||
any sub-packages.
|
||||
|
||||
Please note that _only_ the classes that match will be isolated - any other classes will be loaded by the current
|
||||
class loader.
|
||||
|
||||
Extra classpath entries can also be provided with {@link io.vertx.core.DeploymentOptions#setExtraClasspath} so if
|
||||
you want to load classes or resources that aren't already present on the main classpath you can add this.
|
||||
|
||||
WARNING: Use this feature with caution. Class-loaders can be a can of worms, and can make debugging difficult, amongst
|
||||
other things.
|
||||
|
||||
Here's an example of using an isolation group to isolate a verticle deployment.
|
||||
|
||||
[source,$lang]
|
||||
----
|
||||
{@link examples.CoreExamples#example14}
|
||||
----
|
||||
|
||||
=== High Availability
|
||||
|
||||
Verticles can be deployed with High Availability (HA) enabled. In that context, when a verticle is deployed on
|
||||
|
||||
@@ -20,16 +20,6 @@ public class DeploymentOptionsConverter {
|
||||
obj.setConfig(((JsonObject)member.getValue()).copy());
|
||||
}
|
||||
break;
|
||||
case "extraClasspath":
|
||||
if (member.getValue() instanceof JsonArray) {
|
||||
java.util.ArrayList<java.lang.String> list = new java.util.ArrayList<>();
|
||||
((Iterable<Object>)member.getValue()).forEach( item -> {
|
||||
if (item instanceof String)
|
||||
list.add((String)item);
|
||||
});
|
||||
obj.setExtraClasspath(list);
|
||||
}
|
||||
break;
|
||||
case "ha":
|
||||
if (member.getValue() instanceof Boolean) {
|
||||
obj.setHa((Boolean)member.getValue());
|
||||
@@ -40,21 +30,6 @@ public class DeploymentOptionsConverter {
|
||||
obj.setInstances(((Number)member.getValue()).intValue());
|
||||
}
|
||||
break;
|
||||
case "isolatedClasses":
|
||||
if (member.getValue() instanceof JsonArray) {
|
||||
java.util.ArrayList<java.lang.String> list = new java.util.ArrayList<>();
|
||||
((Iterable<Object>)member.getValue()).forEach( item -> {
|
||||
if (item instanceof String)
|
||||
list.add((String)item);
|
||||
});
|
||||
obj.setIsolatedClasses(list);
|
||||
}
|
||||
break;
|
||||
case "isolationGroup":
|
||||
if (member.getValue() instanceof String) {
|
||||
obj.setIsolationGroup((String)member.getValue());
|
||||
}
|
||||
break;
|
||||
case "maxWorkerExecuteTime":
|
||||
if (member.getValue() instanceof Number) {
|
||||
obj.setMaxWorkerExecuteTime(((Number)member.getValue()).longValue());
|
||||
@@ -92,21 +67,8 @@ public class DeploymentOptionsConverter {
|
||||
if (obj.getConfig() != null) {
|
||||
json.put("config", obj.getConfig());
|
||||
}
|
||||
if (obj.getExtraClasspath() != null) {
|
||||
JsonArray array = new JsonArray();
|
||||
obj.getExtraClasspath().forEach(item -> array.add(item));
|
||||
json.put("extraClasspath", array);
|
||||
}
|
||||
json.put("ha", obj.isHa());
|
||||
json.put("instances", obj.getInstances());
|
||||
if (obj.getIsolatedClasses() != null) {
|
||||
JsonArray array = new JsonArray();
|
||||
obj.getIsolatedClasses().forEach(item -> array.add(item));
|
||||
json.put("isolatedClasses", array);
|
||||
}
|
||||
if (obj.getIsolationGroup() != null) {
|
||||
json.put("isolationGroup", obj.getIsolationGroup());
|
||||
}
|
||||
json.put("maxWorkerExecuteTime", obj.getMaxWorkerExecuteTime());
|
||||
if (obj.getMaxWorkerExecuteTimeUnit() != null) {
|
||||
json.put("maxWorkerExecuteTimeUnit", obj.getMaxWorkerExecuteTimeUnit().name());
|
||||
|
||||
@@ -262,13 +262,6 @@ public class CoreExamples {
|
||||
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
|
||||
}
|
||||
|
||||
public void example14(Vertx vertx) {
|
||||
DeploymentOptions options = new DeploymentOptions().setIsolationGroup("mygroup");
|
||||
options.setIsolatedClasses(Arrays.asList("com.mycompany.myverticle.*",
|
||||
"com.mycompany.somepkg.SomeClass", "org.somelibrary.*"));
|
||||
vertx.deployVerticle("com.mycompany.myverticle.VerticleClass", options);
|
||||
}
|
||||
|
||||
public void example15(Vertx vertx) {
|
||||
long timerID = vertx.setTimer(1000, id -> {
|
||||
System.out.println("And one second later this is printed");
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
package io.vertx.core;
|
||||
|
||||
import io.vertx.codegen.annotations.DataObject;
|
||||
import io.vertx.codegen.annotations.GenIgnore;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
@@ -30,7 +31,6 @@ import java.util.concurrent.TimeUnit;
|
||||
public class DeploymentOptions {
|
||||
|
||||
public static final boolean DEFAULT_WORKER = false;
|
||||
public static final String DEFAULT_ISOLATION_GROUP = null;
|
||||
public static final boolean DEFAULT_HA = false;
|
||||
public static final int DEFAULT_INSTANCES = 1;
|
||||
|
||||
@@ -52,7 +52,7 @@ public class DeploymentOptions {
|
||||
public DeploymentOptions() {
|
||||
this.worker = DEFAULT_WORKER;
|
||||
this.config = null;
|
||||
this.isolationGroup = DEFAULT_ISOLATION_GROUP;
|
||||
this.isolationGroup = null;
|
||||
this.ha = DEFAULT_HA;
|
||||
this.instances = DEFAULT_INSTANCES;
|
||||
this.workerPoolName = null;
|
||||
@@ -98,7 +98,7 @@ public class DeploymentOptions {
|
||||
public void fromJson(JsonObject json) {
|
||||
this.config = json.getJsonObject("config");
|
||||
this.worker = json.getBoolean("worker", DEFAULT_WORKER);
|
||||
this.isolationGroup = json.getString("isolationGroup", DEFAULT_ISOLATION_GROUP);
|
||||
this.isolationGroup = json.getString("isolationGroup", null);
|
||||
this.ha = json.getBoolean("ha", DEFAULT_HA);
|
||||
JsonArray arr = json.getJsonArray("extraClasspath", null);
|
||||
if (arr != null) {
|
||||
@@ -153,19 +153,27 @@ public class DeploymentOptions {
|
||||
|
||||
/**
|
||||
* Get the isolation group that will be used when deploying the verticle(s)
|
||||
* <br/>
|
||||
* <strong>IMPORTANT</strong> this feature is removed when running with Java 11 or above.
|
||||
*
|
||||
* @return the isolation group
|
||||
*/
|
||||
@GenIgnore
|
||||
@Deprecated
|
||||
public String getIsolationGroup() {
|
||||
return isolationGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the isolation group that will be used when deploying the verticle(s)
|
||||
* <br/>
|
||||
* <strong>IMPORTANT</strong> this feature is removed when running with Java 11 or above.
|
||||
*
|
||||
* @param isolationGroup - the isolation group
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
@GenIgnore
|
||||
@Deprecated
|
||||
public DeploymentOptions setIsolationGroup(String isolationGroup) {
|
||||
this.isolationGroup = isolationGroup;
|
||||
return this;
|
||||
@@ -195,9 +203,13 @@ public class DeploymentOptions {
|
||||
* Get any extra classpath to be used when deploying the verticle.
|
||||
* <p>
|
||||
* Ignored if no isolation group is set.
|
||||
* <br/>
|
||||
* <strong>IMPORTANT</strong> this feature is removed when running with Java 11 or above.
|
||||
*
|
||||
* @return any extra classpath
|
||||
*/
|
||||
@GenIgnore
|
||||
@Deprecated
|
||||
public List<String> getExtraClasspath() {
|
||||
return extraClasspath;
|
||||
}
|
||||
@@ -206,9 +218,13 @@ public class DeploymentOptions {
|
||||
* Set any extra classpath to be used when deploying the verticle.
|
||||
* <p>
|
||||
* Ignored if no isolation group is set.
|
||||
* <br/>
|
||||
* <strong>IMPORTANT</strong> this feature is removed when running with Java 11 or above.
|
||||
*
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
@GenIgnore
|
||||
@Deprecated
|
||||
public DeploymentOptions setExtraClasspath(List<String> extraClasspath) {
|
||||
this.extraClasspath = extraClasspath;
|
||||
return this;
|
||||
@@ -237,19 +253,27 @@ public class DeploymentOptions {
|
||||
/**
|
||||
* Get the list of isolated class names, the names can be a Java class fully qualified name such as
|
||||
* 'com.mycompany.myproject.engine.MyClass' or a wildcard matching such as `com.mycompany.myproject.*`.
|
||||
* <br/>
|
||||
* <strong>IMPORTANT</strong> this feature is removed when running with Java 11 or above.
|
||||
*
|
||||
* @return the list of isolated classes
|
||||
*/
|
||||
@GenIgnore
|
||||
@Deprecated
|
||||
public List<String> getIsolatedClasses() {
|
||||
return isolatedClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the isolated class names.
|
||||
* <br/>
|
||||
* <strong>IMPORTANT</strong> this feature is removed when running with Java 11 or above.
|
||||
*
|
||||
* @param isolatedClasses the list of isolated class names
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
@GenIgnore
|
||||
@Deprecated
|
||||
public DeploymentOptions setIsolatedClasses(List<String> isolatedClasses) {
|
||||
this.isolatedClasses = isolatedClasses;
|
||||
return this;
|
||||
@@ -348,6 +372,21 @@ public class DeploymentOptions {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw {@code IllegalArgumentException} when loader isolation configuration has been defined.
|
||||
*/
|
||||
public void checkIsolationNotDefined() {
|
||||
if (getExtraClasspath() != null) {
|
||||
throw new IllegalArgumentException("Can't specify extraClasspath for already created verticle");
|
||||
}
|
||||
if (getIsolationGroup() != null) {
|
||||
throw new IllegalArgumentException("Can't specify isolationGroup for already created verticle");
|
||||
}
|
||||
if (getIsolatedClasses() != null) {
|
||||
throw new IllegalArgumentException("Can't specify isolatedClasses for already created verticle");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this to JSON
|
||||
*
|
||||
|
||||
26
src/main/java/io/vertx/core/impl/ClassLoaderHolder.java
Normal file
26
src/main/java/io/vertx/core/impl/ClassLoaderHolder.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
|
||||
*/
|
||||
class ClassLoaderHolder {
|
||||
|
||||
final String group;
|
||||
final ClassLoader loader;
|
||||
int refCount;
|
||||
|
||||
ClassLoaderHolder(String group, ClassLoader loader) {
|
||||
this.group = group;
|
||||
this.loader = loader;
|
||||
}
|
||||
}
|
||||
@@ -61,15 +61,7 @@ public class DeploymentManager {
|
||||
if (options.getInstances() < 1) {
|
||||
throw new IllegalArgumentException("Can't specify < 1 instances to deploy");
|
||||
}
|
||||
if (options.getExtraClasspath() != null) {
|
||||
throw new IllegalArgumentException("Can't specify extraClasspath for already created verticle");
|
||||
}
|
||||
if (options.getIsolationGroup() != null) {
|
||||
throw new IllegalArgumentException("Can't specify isolationGroup for already created verticle");
|
||||
}
|
||||
if (options.getIsolatedClasses() != null) {
|
||||
throw new IllegalArgumentException("Can't specify isolatedClasses for already created verticle");
|
||||
}
|
||||
options.checkIsolationNotDefined();
|
||||
ContextInternal currentContext = vertx.getOrCreateContext();
|
||||
ClassLoader cl = getCurrentClassLoader();
|
||||
return doDeploy(options, v -> "java:" + v.getClass().getName(), currentContext, currentContext, cl, verticleSupplier)
|
||||
|
||||
@@ -26,12 +26,10 @@ public class IsolatingClassLoader extends URLClassLoader {
|
||||
|
||||
private volatile boolean closed;
|
||||
private List<String> isolatedClasses;
|
||||
int refCount;
|
||||
|
||||
public IsolatingClassLoader(URL[] urls, ClassLoader parent, List<String> isolatedClasses) {
|
||||
super(urls, parent);
|
||||
this.isolatedClasses = isolatedClasses;
|
||||
this.refCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
99
src/main/java/io/vertx/core/impl/LoaderManager.java
Normal file
99
src/main/java/io/vertx/core/impl/LoaderManager.java
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.DeploymentOptions;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Supports isolated classloader for Java 8.
|
||||
* <br/>
|
||||
* For Java 11 and above, the JVM uses a no-op implementation thanks to Multi-Release Jar.
|
||||
*/
|
||||
class LoaderManager {
|
||||
|
||||
private final Map<String, ClassLoaderHolder> classLoaders = new HashMap<>();
|
||||
|
||||
/**
|
||||
* <strong>IMPORTANT</strong> - Isolation groups are not supported on Java 9+ because the application classloader is not
|
||||
* an URLClassLoader anymore. Thus we can't extract the list of jars to configure the IsolatedClassLoader.
|
||||
*/
|
||||
ClassLoaderHolder getClassLoader(DeploymentOptions options) {
|
||||
String isolationGroup = options.getIsolationGroup();
|
||||
ClassLoaderHolder holder;
|
||||
if (isolationGroup == null) {
|
||||
return null;
|
||||
} else {
|
||||
// IMPORTANT - Isolation groups are not supported on Java 9+, because the system classloader is not an URLClassLoader
|
||||
// anymore. Thus we can't extract the paths from the classpath and isolate the loading.
|
||||
synchronized (this) {
|
||||
holder = classLoaders.get(isolationGroup);
|
||||
if (holder == null) {
|
||||
ClassLoader current = VerticleManager.getCurrentClassLoader();
|
||||
if (!(current instanceof URLClassLoader)) {
|
||||
throw new IllegalStateException("Current classloader must be URLClassLoader");
|
||||
}
|
||||
holder = new ClassLoaderHolder(isolationGroup, LoaderManager.buildLoader((URLClassLoader) current, options));
|
||||
classLoaders.put(isolationGroup, holder);
|
||||
}
|
||||
holder.refCount++;
|
||||
}
|
||||
}
|
||||
return holder;
|
||||
}
|
||||
|
||||
void release(ClassLoaderHolder holder) {
|
||||
synchronized (this) {
|
||||
if (--holder.refCount == 0) {
|
||||
classLoaders.remove(holder.group);
|
||||
if (holder.loader instanceof Closeable) {
|
||||
try {
|
||||
((Closeable)holder.loader).close();
|
||||
} catch (IOException e) {
|
||||
// log.debug("Issue when closing isolation group loader", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassLoader buildLoader(URLClassLoader parent, DeploymentOptions options) {
|
||||
List<URL> urls = new ArrayList<>();
|
||||
// Add any extra URLs to the beginning of the classpath
|
||||
List<String> extraClasspath = options.getExtraClasspath();
|
||||
if (extraClasspath != null) {
|
||||
for (String pathElement: extraClasspath) {
|
||||
File file = new File(pathElement);
|
||||
try {
|
||||
URL url = file.toURI().toURL();
|
||||
urls.add(url);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// And add the URLs of the Vert.x classloader
|
||||
urls.addAll(Arrays.asList(parent.getURLs()));
|
||||
return new IsolatingClassLoader(urls.toArray(new URL[urls.size()]), parent,
|
||||
options.getIsolatedClasses());
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,8 @@ import io.vertx.core.ServiceHelper;
|
||||
import io.vertx.core.Verticle;
|
||||
import io.vertx.core.spi.VerticleFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -41,7 +34,7 @@ public class VerticleManager {
|
||||
|
||||
private final VertxInternal vertx;
|
||||
private final DeploymentManager deploymentManager;
|
||||
private final Map<String, IsolatingClassLoader> classloaders = new HashMap<>();
|
||||
private final LoaderManager loaderManager = new LoaderManager();
|
||||
private final Map<String, List<VerticleFactory>> verticleFactories = new ConcurrentHashMap<>();
|
||||
private final List<VerticleFactory> defaultFactories = new ArrayList<>();
|
||||
|
||||
@@ -152,8 +145,23 @@ public class VerticleManager {
|
||||
public Future<Deployment> deployVerticle(String identifier,
|
||||
DeploymentOptions options) {
|
||||
ContextInternal callingContext = vertx.getOrCreateContext();
|
||||
ClassLoader cl = getClassLoader(options);
|
||||
return doDeployVerticle(identifier, options, callingContext, callingContext, cl);
|
||||
ClassLoaderHolder holder = loaderManager.getClassLoader(options);
|
||||
ClassLoader loader = holder != null ? holder.loader : getCurrentClassLoader();
|
||||
Future<Deployment> deployment = doDeployVerticle(identifier, options, callingContext, callingContext, loader);
|
||||
if (holder != null) {
|
||||
deployment.onComplete(ar -> {
|
||||
if (ar.succeeded()) {
|
||||
Deployment result = ar.result();
|
||||
result.undeployHandler(v -> {
|
||||
loaderManager.release(holder);
|
||||
});
|
||||
} else {
|
||||
// ??? not tested
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
});
|
||||
}
|
||||
return deployment;
|
||||
}
|
||||
|
||||
private Future<Deployment> doDeployVerticle(String identifier,
|
||||
@@ -204,88 +212,14 @@ public class VerticleManager {
|
||||
} catch (Exception e) {
|
||||
return Future.failedFuture(e);
|
||||
}
|
||||
Future<Deployment> fut = p.future().compose(callable -> deploymentManager.doDeploy(options, v -> identifier, parentContext, callingContext, cl, callable));
|
||||
String group = options.getIsolationGroup();
|
||||
if (group != null) {
|
||||
fut.onComplete(ar -> {
|
||||
if (ar.succeeded()) {
|
||||
Deployment result = ar.result();
|
||||
result.undeployHandler(v -> {
|
||||
synchronized (VerticleManager.this) {
|
||||
IsolatingClassLoader icl = classloaders.get(group);
|
||||
if (--icl.refCount == 0) {
|
||||
classloaders.remove(group);
|
||||
try {
|
||||
icl.close();
|
||||
} catch (IOException e) {
|
||||
// log.debug("Issue when closing isolation group loader", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// ??? not tested
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
});
|
||||
}
|
||||
return fut;
|
||||
return p.future()
|
||||
.compose(callable -> deploymentManager.doDeploy(options, v -> identifier, parentContext, callingContext, cl, callable));
|
||||
}
|
||||
|
||||
/**
|
||||
* <strong>IMPORTANT</strong> - Isolation groups are not supported on Java 9+ because the application classloader is not
|
||||
* an URLClassLoader anymore. Thus we can't extract the list of jars to configure the IsolatedClassLoader.
|
||||
*
|
||||
*/
|
||||
private ClassLoader getClassLoader(DeploymentOptions options) {
|
||||
String isolationGroup = options.getIsolationGroup();
|
||||
ClassLoader cl;
|
||||
if (isolationGroup == null) {
|
||||
cl = getCurrentClassLoader();
|
||||
} else {
|
||||
// IMPORTANT - Isolation groups are not supported on Java 9+, because the system classloader is not an URLClassLoader
|
||||
// anymore. Thus we can't extract the paths from the classpath and isolate the loading.
|
||||
synchronized (this) {
|
||||
IsolatingClassLoader icl = classloaders.get(isolationGroup);
|
||||
if (icl == null) {
|
||||
ClassLoader current = getCurrentClassLoader();
|
||||
if (!(current instanceof URLClassLoader)) {
|
||||
throw new IllegalStateException("Current classloader must be URLClassLoader");
|
||||
}
|
||||
List<URL> urls = new ArrayList<>();
|
||||
// Add any extra URLs to the beginning of the classpath
|
||||
List<String> extraClasspath = options.getExtraClasspath();
|
||||
if (extraClasspath != null) {
|
||||
for (String pathElement: extraClasspath) {
|
||||
File file = new File(pathElement);
|
||||
try {
|
||||
URL url = file.toURI().toURL();
|
||||
urls.add(url);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// And add the URLs of the Vert.x classloader
|
||||
URLClassLoader urlc = (URLClassLoader)current;
|
||||
urls.addAll(Arrays.asList(urlc.getURLs()));
|
||||
|
||||
// Create an isolating cl with the urls
|
||||
icl = new IsolatingClassLoader(urls.toArray(new URL[urls.size()]), getCurrentClassLoader(),
|
||||
options.getIsolatedClasses());
|
||||
classloaders.put(isolationGroup, icl);
|
||||
}
|
||||
icl.refCount++;
|
||||
cl = icl;
|
||||
}
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
private ClassLoader getCurrentClassLoader() {
|
||||
static ClassLoader getCurrentClassLoader() {
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
if (cl == null) {
|
||||
cl = getClass().getClassLoader();
|
||||
cl = VerticleManager.class.getClassLoader();
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
286
src/main/java11/io/vertx/core/DeploymentOptions.java
Normal file
286
src/main/java11/io/vertx/core/DeploymentOptions.java
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import io.vertx.codegen.annotations.DataObject;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Options for configuring a verticle deployment.
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* @author <a href="http://tfox.org">Tim Fox</a>
|
||||
*/
|
||||
@DataObject(generateConverter = true, publicConverter = false)
|
||||
public class DeploymentOptions {
|
||||
|
||||
public static final boolean DEFAULT_WORKER = false;
|
||||
public static final boolean DEFAULT_HA = false;
|
||||
public static final int DEFAULT_INSTANCES = 1;
|
||||
|
||||
private JsonObject config;
|
||||
private boolean worker;
|
||||
private String workerPoolName;
|
||||
private int workerPoolSize;
|
||||
private long maxWorkerExecuteTime;
|
||||
private boolean ha;
|
||||
private int instances;
|
||||
private TimeUnit maxWorkerExecuteTimeUnit;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public DeploymentOptions() {
|
||||
this.worker = DEFAULT_WORKER;
|
||||
this.config = null;
|
||||
this.ha = DEFAULT_HA;
|
||||
this.instances = DEFAULT_INSTANCES;
|
||||
this.workerPoolName = null;
|
||||
this.workerPoolSize = VertxOptions.DEFAULT_WORKER_POOL_SIZE;
|
||||
this.maxWorkerExecuteTime = VertxOptions.DEFAULT_MAX_WORKER_EXECUTE_TIME;
|
||||
this.maxWorkerExecuteTimeUnit = VertxOptions.DEFAULT_MAX_WORKER_EXECUTE_TIME_UNIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param other the instance to copy
|
||||
*/
|
||||
public DeploymentOptions(DeploymentOptions other) {
|
||||
this.config = other.getConfig() == null ? null : other.getConfig().copy();
|
||||
this.worker = other.isWorker();
|
||||
this.ha = other.isHa();
|
||||
this.instances = other.instances;
|
||||
this.workerPoolName = other.workerPoolName;
|
||||
setWorkerPoolSize(other.workerPoolSize);
|
||||
setMaxWorkerExecuteTime(other.maxWorkerExecuteTime);
|
||||
this.maxWorkerExecuteTimeUnit = other.maxWorkerExecuteTimeUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for creating a instance from JSON
|
||||
*
|
||||
* @param json the JSON
|
||||
*/
|
||||
public DeploymentOptions(JsonObject json) {
|
||||
this();
|
||||
DeploymentOptionsConverter.fromJson(json, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the fields of this instance from the specified JSON
|
||||
*
|
||||
* @param json the JSON
|
||||
*/
|
||||
public void fromJson(JsonObject json) {
|
||||
this.config = json.getJsonObject("config");
|
||||
this.worker = json.getBoolean("worker", DEFAULT_WORKER);
|
||||
this.ha = json.getBoolean("ha", DEFAULT_HA);
|
||||
this.instances = json.getInteger("instances", DEFAULT_INSTANCES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON configuration that will be passed to the verticle(s) when deployed.
|
||||
*
|
||||
* @return the JSON config
|
||||
*/
|
||||
public JsonObject getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JSON configuration that will be passed to the verticle(s) when it's deployed
|
||||
*
|
||||
* @param config the JSON config
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
public DeploymentOptions setConfig(JsonObject config) {
|
||||
this.config = config;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the verticle(s) be deployed as a worker verticle?
|
||||
*
|
||||
* @return true if will be deployed as worker, false otherwise
|
||||
*/
|
||||
public boolean isWorker() {
|
||||
return worker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the verticle(s) should be deployed as a worker verticle
|
||||
*
|
||||
* @param worker true for worker, false otherwise
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
public DeploymentOptions setWorker(boolean worker) {
|
||||
this.worker = worker;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will the verticle(s) be deployed as HA (highly available) ?
|
||||
*
|
||||
* @return true if HA, false otherwise
|
||||
*/
|
||||
public boolean isHa() {
|
||||
return ha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the verticle(s) will be deployed as HA.
|
||||
*
|
||||
* @param ha true if to be deployed as HA, false otherwise
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
public DeploymentOptions setHa(boolean ha) {
|
||||
this.ha = ha;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of instances that should be deployed.
|
||||
*
|
||||
* @return the number of instances
|
||||
*/
|
||||
public int getInstances() {
|
||||
return instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of instances that should be deployed.
|
||||
*
|
||||
* @param instances the number of instances
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
public DeploymentOptions setInstances(int instances) {
|
||||
this.instances = instances;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the worker pool name
|
||||
*/
|
||||
public String getWorkerPoolName() {
|
||||
return workerPoolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the worker pool name to use for this verticle. When no name is set, the Vert.x
|
||||
* worker pool will be used, when a name is set, the verticle will use a named worker pool.
|
||||
*
|
||||
* @param workerPoolName the worker pool name
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
public DeploymentOptions setWorkerPoolName(String workerPoolName) {
|
||||
this.workerPoolName = workerPoolName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of worker threads to be used by the worker pool when the verticle is deployed
|
||||
* with a {@link #setWorkerPoolName}. When the verticle does not use a named worker pool, this option
|
||||
* has no effect.
|
||||
* <p>
|
||||
* Worker threads are used for running blocking code and worker verticles.
|
||||
*
|
||||
* @return the maximum number of worker threads
|
||||
*/
|
||||
public int getWorkerPoolSize() {
|
||||
return workerPoolSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of worker threads to be used by the Vert.x instance.
|
||||
*
|
||||
* @param workerPoolSize the number of threads
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
public DeploymentOptions setWorkerPoolSize(int workerPoolSize) {
|
||||
if (workerPoolSize < 1) {
|
||||
throw new IllegalArgumentException("workerPoolSize must be > 0");
|
||||
}
|
||||
this.workerPoolSize = workerPoolSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of max worker execute time, in {@link DeploymentOptions#setMaxWorkerExecuteTimeUnit maxWorkerExecuteTimeUnit}.
|
||||
* <p>
|
||||
* Vert.x will automatically log a warning if it detects that worker threads haven't returned within this time.
|
||||
* <p>
|
||||
* This can be used to detect where the user is blocking a worker thread for too long. Although worker threads
|
||||
* can be blocked longer than event loop threads, they shouldn't be blocked for long periods of time.
|
||||
*
|
||||
* @return The value of max worker execute time, the default value of {@link DeploymentOptions#setMaxWorkerExecuteTimeUnit} {@code maxWorkerExecuteTimeUnit} is {@link TimeUnit#NANOSECONDS}
|
||||
*/
|
||||
public long getMaxWorkerExecuteTime() {
|
||||
return maxWorkerExecuteTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of max worker execute time, in {@link DeploymentOptions#setMaxWorkerExecuteTimeUnit maxWorkerExecuteTimeUnit}.
|
||||
* <p>
|
||||
* The default value of {@link DeploymentOptions#setMaxWorkerExecuteTimeUnit maxWorkerExecuteTimeUnit} is {@link TimeUnit#NANOSECONDS}
|
||||
*
|
||||
* @param maxWorkerExecuteTime the value of max worker execute time, in in {@link DeploymentOptions#setMaxWorkerExecuteTimeUnit maxWorkerExecuteTimeUnit}.
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
public DeploymentOptions setMaxWorkerExecuteTime(long maxWorkerExecuteTime) {
|
||||
if (maxWorkerExecuteTime < 1) {
|
||||
throw new IllegalArgumentException("maxWorkerExecuteTime must be > 0");
|
||||
}
|
||||
this.maxWorkerExecuteTime = maxWorkerExecuteTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the time unit of {@code maxWorkerExecuteTime}
|
||||
*/
|
||||
public TimeUnit getMaxWorkerExecuteTimeUnit() {
|
||||
return maxWorkerExecuteTimeUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time unit of {@code maxWorkerExecuteTime}
|
||||
* @param maxWorkerExecuteTimeUnit the time unit of {@code maxWorkerExecuteTime}
|
||||
* @return a reference to this, so the API can be used fluently
|
||||
*/
|
||||
public DeploymentOptions setMaxWorkerExecuteTimeUnit(TimeUnit maxWorkerExecuteTimeUnit) {
|
||||
this.maxWorkerExecuteTimeUnit = maxWorkerExecuteTimeUnit;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*/
|
||||
public void checkIsolationNotDefined() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this to JSON
|
||||
*
|
||||
* @return the JSON
|
||||
*/
|
||||
public JsonObject toJson() {
|
||||
JsonObject json = new JsonObject();
|
||||
DeploymentOptionsConverter.toJson(this, json);
|
||||
return json;
|
||||
}
|
||||
}
|
||||
29
src/main/java11/io/vertx/core/impl/LoaderManager.java
Normal file
29
src/main/java11/io/vertx/core/impl/LoaderManager.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.DeploymentOptions;
|
||||
|
||||
/**
|
||||
* No-op implementation for Java 11 and above.
|
||||
*/
|
||||
class LoaderManager {
|
||||
|
||||
/**
|
||||
* @return {@code null}
|
||||
*/
|
||||
ClassLoaderHolder getClassLoader(DeploymentOptions options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
void release(ClassLoaderHolder holder) {
|
||||
}
|
||||
}
|
||||
@@ -146,8 +146,6 @@ public class DeploymentTest extends VertxTestBase {
|
||||
JsonObject config = new JsonObject().put("foo", "bar");
|
||||
Random rand = new Random();
|
||||
boolean worker = rand.nextBoolean();
|
||||
boolean multiThreaded = rand.nextBoolean();
|
||||
String isolationGroup = TestUtils.randomAlphaString(100);
|
||||
boolean ha = rand.nextBoolean();
|
||||
List<String> cp = Arrays.asList("foo", "bar");
|
||||
List<String> isol = Arrays.asList("com.foo.MyClass", "org.foo.*");
|
||||
@@ -158,22 +156,15 @@ public class DeploymentTest extends VertxTestBase {
|
||||
JsonObject json = new JsonObject();
|
||||
json.put("config", config);
|
||||
json.put("worker", worker);
|
||||
json.put("multiThreaded", multiThreaded);
|
||||
json.put("isolationGroup", isolationGroup);
|
||||
json.put("ha", ha);
|
||||
json.put("extraClasspath", new JsonArray(cp));
|
||||
json.put("isolatedClasses", new JsonArray(isol));
|
||||
json.put("workerPoolName", poolName);
|
||||
json.put("workerPoolSize", poolSize);
|
||||
json.put("maxWorkerExecuteTime", maxWorkerExecuteTime);
|
||||
json.put("maxWorkerExecuteTimeUnit", maxWorkerExecuteTimeUnit);
|
||||
DeploymentOptions options = new DeploymentOptions(json);
|
||||
assertEquals(worker, options.isWorker());
|
||||
assertEquals(isolationGroup, options.getIsolationGroup());
|
||||
assertEquals("bar", options.getConfig().getString("foo"));
|
||||
assertEquals(ha, options.isHa());
|
||||
assertEquals(cp, options.getExtraClasspath());
|
||||
assertEquals(isol, options.getIsolatedClasses());
|
||||
assertEquals(poolName, options.getWorkerPoolName());
|
||||
assertEquals(poolSize, options.getWorkerPoolSize());
|
||||
assertEquals(maxWorkerExecuteTime, options.getMaxWorkerExecuteTime());
|
||||
@@ -186,8 +177,6 @@ public class DeploymentTest extends VertxTestBase {
|
||||
JsonObject config = new JsonObject().put("foo", "bar");
|
||||
Random rand = new Random();
|
||||
boolean worker = rand.nextBoolean();
|
||||
boolean multiThreaded = rand.nextBoolean();
|
||||
String isolationGroup = TestUtils.randomAlphaString(100);
|
||||
boolean ha = rand.nextBoolean();
|
||||
List<String> cp = Arrays.asList("foo", "bar");
|
||||
List<String> isol = Arrays.asList("com.foo.MyClass", "org.foo.*");
|
||||
@@ -197,10 +186,7 @@ public class DeploymentTest extends VertxTestBase {
|
||||
TimeUnit maxWorkerExecuteTimeUnit = TimeUnit.MILLISECONDS;
|
||||
options.setConfig(config);
|
||||
options.setWorker(worker);
|
||||
options.setIsolationGroup(isolationGroup);
|
||||
options.setHa(ha);
|
||||
options.setExtraClasspath(cp);
|
||||
options.setIsolatedClasses(isol);
|
||||
options.setWorkerPoolName(poolName);
|
||||
options.setWorkerPoolSize(poolSize);
|
||||
options.setMaxWorkerExecuteTime(maxWorkerExecuteTime);
|
||||
@@ -208,11 +194,8 @@ public class DeploymentTest extends VertxTestBase {
|
||||
JsonObject json = options.toJson();
|
||||
DeploymentOptions copy = new DeploymentOptions(json);
|
||||
assertEquals(worker, copy.isWorker());
|
||||
assertEquals(isolationGroup, copy.getIsolationGroup());
|
||||
assertEquals("bar", copy.getConfig().getString("foo"));
|
||||
assertEquals(ha, copy.isHa());
|
||||
assertEquals(cp, copy.getExtraClasspath());
|
||||
assertEquals(isol, copy.getIsolatedClasses());
|
||||
assertEquals(poolName, copy.getWorkerPoolName());
|
||||
assertEquals(poolSize, copy.getWorkerPoolSize());
|
||||
assertEquals(maxWorkerExecuteTime, copy.getMaxWorkerExecuteTime());
|
||||
|
||||
Reference in New Issue
Block a user