/*
* Copyright (c) 2011-2014 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.test.core;
import io.vertx.core.AbstractVerticle;
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.Verticle;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.impl.ContextImpl;
import io.vertx.core.impl.Deployment;
import io.vertx.core.impl.VertxInternal;
import io.vertx.core.impl.WorkerContext;
import io.vertx.core.impl.verticle.CompilingClassLoader;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.test.core.sourceverticle.SourceVerticle;
import org.junit.Test;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
*
* @author <a href="http://tfox.org">Tim Fox</a>
*/
public class DeploymentTest extends VertxTestBase {
public void setUp() throws Exception {
super.setUp();
TestVerticle.instanceCount.set(0);
}
@Test
public void testOptions() {
DeploymentOptions options = new DeploymentOptions();
assertNull(options.getConfig());
JsonObject config = new JsonObject().put("foo", "bar").put("obj", new JsonObject().put("quux", 123));
assertEquals(options, options.setConfig(config));
assertEquals(config, options.getConfig());
assertFalse(options.isWorker());
assertEquals(options, options.setWorker(true));
assertTrue(options.isWorker());
assertFalse(options.isMultiThreaded());
assertEquals(options, options.setMultiThreaded(true));
assertTrue(options.isMultiThreaded());
assertNull(options.getIsolationGroup());
String rand = TestUtils.randomUnicodeString(1000);
assertEquals(options, options.setIsolationGroup(rand));
assertEquals(rand, options.getIsolationGroup());
assertFalse(options.isHa());
assertEquals(options, options.setHa(true));
assertTrue(options.isHa());
assertNull(options.getExtraClasspath());
List<String> cp = Arrays.asList("foo", "bar");
assertEquals(options, options.setExtraClasspath(cp));
assertNull(options.getIsolatedClasses());
List<String> isol = Arrays.asList("com.foo.MyClass", "org.foo.*");
assertEquals(options, options.setIsolatedClasses(isol));
assertSame(isol, options.getIsolatedClasses());
String workerPoolName = TestUtils.randomAlphaString(10);
assertEquals(options, options.setWorkerPoolName(workerPoolName));
assertEquals(workerPoolName, options.getWorkerPoolName());
int workerPoolSize = TestUtils.randomPositiveInt();
assertEquals(options, options.setWorkerPoolSize(workerPoolSize));
assertEquals(workerPoolSize, options.getWorkerPoolSize());
long maxWorkerExecuteTime = TestUtils.randomPositiveLong();
assertEquals(options, options.setMaxWorkerExecuteTime(maxWorkerExecuteTime));
assertEquals(maxWorkerExecuteTime, options.getMaxWorkerExecuteTime());
}
@Test
public void testCopyOptions() {
DeploymentOptions options = new DeploymentOptions();
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.*");
String poolName = TestUtils.randomAlphaString(10);
int poolSize = TestUtils.randomPositiveInt();
long maxWorkerExecuteTime = TestUtils.randomPositiveLong();
options.setConfig(config);
options.setWorker(worker);
options.setMultiThreaded(multiThreaded);
options.setIsolationGroup(isolationGroup);
options.setHa(ha);
options.setExtraClasspath(cp);
options.setIsolatedClasses(isol);
options.setWorkerPoolName(poolName);
options.setWorkerPoolSize(poolSize);
options.setMaxWorkerExecuteTime(maxWorkerExecuteTime);
DeploymentOptions copy = new DeploymentOptions(options);
assertEquals(worker, copy.isWorker());
assertEquals(multiThreaded, copy.isMultiThreaded());
assertEquals(isolationGroup, copy.getIsolationGroup());
assertNotSame(config, copy.getConfig());
assertEquals("bar", copy.getConfig().getString("foo"));
assertEquals(ha, copy.isHa());
assertEquals(cp, copy.getExtraClasspath());
assertNotSame(cp, copy.getExtraClasspath());
assertEquals(isol, copy.getIsolatedClasses());
assertNotSame(isol, copy.getIsolatedClasses());
assertEquals(poolName, copy.getWorkerPoolName());
assertEquals(poolSize, copy.getWorkerPoolSize());
assertEquals(maxWorkerExecuteTime, copy.getMaxWorkerExecuteTime());
}
@Test
public void testDefaultJsonOptions() {
DeploymentOptions def = new DeploymentOptions();
DeploymentOptions json = new DeploymentOptions(new JsonObject());
assertEquals(def.getConfig(), json.getConfig());
assertEquals(def.isWorker(), json.isWorker());
assertEquals(def.isMultiThreaded(), json.isMultiThreaded());
assertEquals(def.getIsolationGroup(), json.getIsolationGroup());
assertEquals(def.isHa(), json.isHa());
assertEquals(def.getExtraClasspath(), json.getExtraClasspath());
assertEquals(def.getIsolatedClasses(), json.getIsolatedClasses());
assertEquals(def.getWorkerPoolName(), json.getWorkerPoolName());
assertEquals(def.getWorkerPoolSize(), json.getWorkerPoolSize());
assertEquals(def.getMaxWorkerExecuteTime(), json.getMaxWorkerExecuteTime());
}
@Test
public void testJsonOptions() {
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.*");
String poolName = TestUtils.randomAlphaString(10);
int poolSize = TestUtils.randomPositiveInt();
long maxWorkerExecuteTime = TestUtils.randomPositiveLong();
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);
DeploymentOptions options = new DeploymentOptions(json);
assertEquals(worker, options.isWorker());
assertEquals(multiThreaded, options.isMultiThreaded());
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());
}
@Test
public void testToJson() {
DeploymentOptions options = new DeploymentOptions();
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.*");
String poolName = TestUtils.randomAlphaString(10);
int poolSize = TestUtils.randomPositiveInt();
long maxWorkerExecuteTime = TestUtils.randomPositiveLong();
options.setConfig(config);
options.setWorker(worker);
options.setMultiThreaded(multiThreaded);
options.setIsolationGroup(isolationGroup);
options.setHa(ha);
options.setExtraClasspath(cp);
options.setIsolatedClasses(isol);
options.setWorkerPoolName(poolName);
options.setWorkerPoolSize(poolSize);
options.setMaxWorkerExecuteTime(maxWorkerExecuteTime);
JsonObject json = options.toJson();
DeploymentOptions copy = new DeploymentOptions(json);
assertEquals(worker, copy.isWorker());
assertEquals(multiThreaded, copy.isMultiThreaded());
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());
}
@Test
public void testDeployFromTestThread() throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, ar -> {
assertDeployment(1, verticle, null, ar);
assertFalse(verticle.startContext.isMultiThreadedWorkerContext());
assertFalse(verticle.startContext.isWorkerContext());
assertTrue(verticle.startContext.isEventLoopContext());
testComplete();
});
await();
}
@Test
public void testDeployFromTestThreadNoHandler() throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle);
assertWaitUntil(() -> vertx.deploymentIDs().size() == 1);
}
@Test
public void testDeployWithConfig() throws Exception {
MyVerticle verticle = new MyVerticle();
JsonObject config = generateJSONObject();
vertx.deployVerticle(verticle, new DeploymentOptions().setConfig(config), ar -> {
assertDeployment(1, verticle, config, ar);
testComplete();
});
await();
}
@Test
public void testDeployFromContext() throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
Context ctx = Vertx.currentContext();
MyVerticle verticle2 = new MyVerticle();
vertx.deployVerticle(verticle2, ar2 -> {
assertDeployment(2, verticle2, null, ar2);
Context ctx2 = Vertx.currentContext();
assertEquals(ctx, ctx2);
testComplete();
});
});
await();
}
@Test
public void testDeployWorkerFromTestThread() throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, new DeploymentOptions().setWorker(true), ar -> {
assertDeployment(1, verticle, null, ar);
assertTrue(verticle.startContext instanceof WorkerContext);
vertx.undeploy(ar.result(), ar2 -> {
assertTrue(ar2.succeeded());
assertEquals(verticle.startContext, verticle.stopContext);
testComplete();
});
});
await();
}
@Test
public void testDeployWorkerWithConfig() throws Exception {
MyVerticle verticle = new MyVerticle();
JsonObject conf = generateJSONObject();
vertx.deployVerticle(verticle, new DeploymentOptions().setConfig(conf).setWorker(true), ar -> {
assertDeployment(1, verticle, conf, ar);
assertFalse(verticle.startContext.isMultiThreadedWorkerContext());
assertTrue(verticle.startContext.isWorkerContext());
assertFalse(verticle.startContext.isEventLoopContext());
vertx.undeploy(ar.result(), ar2 -> {
assertTrue(ar2.succeeded());
assertEquals(verticle.startContext, verticle.stopContext);
testComplete();
});
});
await();
}
@Test
public void testDeployMultithreadedWorkerWithConfig() throws Exception {
MyVerticle verticle = new MyVerticle();
JsonObject conf = generateJSONObject();
vertx.deployVerticle(verticle, new DeploymentOptions().setConfig(conf).setWorker(true).setMultiThreaded(true), ar -> {
assertDeployment(1, verticle, conf, ar);
assertTrue(verticle.startContext.isMultiThreadedWorkerContext());
assertTrue(verticle.startContext.isWorkerContext());
assertFalse(verticle.startContext.isEventLoopContext());
vertx.undeploy(ar.result(), ar2 -> {
assertTrue(ar2.succeeded());
assertEquals(verticle.startContext, verticle.stopContext);
testComplete();
});
});
await();
}
@Test
public void testWorkerRightThread() throws Exception {
assertFalse(Context.isOnVertxThread());
Verticle verticle = new AbstractVerticle() {
@Override
public void start() throws Exception {
assertTrue(Context.isOnVertxThread());
assertTrue(Context.isOnWorkerThread());
assertFalse(Context.isOnEventLoopThread());
}
@Override
public void stop() throws Exception {
assertTrue(Context.isOnVertxThread());
assertTrue(Context.isOnWorkerThread());
assertFalse(Context.isOnEventLoopThread());
}
};
vertx.deployVerticle(verticle, new DeploymentOptions().setWorker(true), onSuccess(res -> {
assertTrue(Context.isOnVertxThread());
assertFalse(Context.isOnWorkerThread());
assertTrue(Context.isOnEventLoopThread());
vertx.undeploy(res, onSuccess(res2 -> {
assertTrue(Context.isOnVertxThread());
assertFalse(Context.isOnWorkerThread());
assertTrue(Context.isOnEventLoopThread());
testComplete();
}));
}));
await();
}
@Test
public void testMTWorkerRightThread() throws Exception {
assertFalse(Context.isOnVertxThread());
Verticle verticle = new AbstractVerticle() {
@Override
public void start() throws Exception {
assertTrue(Context.isOnVertxThread());
assertTrue(Context.isOnWorkerThread());
assertFalse(Context.isOnEventLoopThread());
}
@Override
public void stop() throws Exception {
assertTrue(Context.isOnVertxThread());
assertTrue(Context.isOnWorkerThread());
assertFalse(Context.isOnEventLoopThread());
}
};
vertx.deployVerticle(verticle, new DeploymentOptions().setWorker(true).setMultiThreaded(true), onSuccess(res -> {
assertTrue(Context.isOnVertxThread());
assertFalse(Context.isOnWorkerThread());
assertTrue(Context.isOnEventLoopThread());
vertx.undeploy(res, onSuccess(res2 -> {
assertTrue(Context.isOnVertxThread());
assertFalse(Context.isOnWorkerThread());
assertTrue(Context.isOnEventLoopThread());
testComplete();
}));
}));
await();
}
@Test
public void testStandardRightThread() throws Exception {
assertFalse(Context.isOnVertxThread());
Verticle verticle = new AbstractVerticle() {
@Override
public void start() throws Exception {
assertTrue(Context.isOnVertxThread());
assertFalse(Context.isOnWorkerThread());
assertTrue(Context.isOnEventLoopThread());
}
@Override
public void stop() throws Exception {
assertTrue(Context.isOnVertxThread());
assertFalse(Context.isOnWorkerThread());
assertTrue(Context.isOnEventLoopThread());
}
};
vertx.deployVerticle(verticle, onSuccess(res -> {
assertTrue(Context.isOnVertxThread());
assertFalse(Context.isOnWorkerThread());
assertTrue(Context.isOnEventLoopThread());
vertx.undeploy(res, onSuccess(res2 -> {
assertTrue(Context.isOnVertxThread());
assertFalse(Context.isOnWorkerThread());
assertTrue(Context.isOnEventLoopThread());
testComplete();
}));
}));
await();
}
@Test
public void testDeployMultithreadedNotWorker() throws Exception {
MyVerticle verticle = new MyVerticle();
try {
vertx.deployVerticle(verticle, new DeploymentOptions().setWorker(false).setMultiThreaded(true), ar -> {
});
fail("Should throw exception");
} catch (IllegalArgumentException e) {
// OK
}
}
@Test
public void testDeployFromContextExceptionInStart() throws Exception {
testDeployFromThrowableInStart(MyVerticle.THROW_EXCEPTION, Exception.class);
}
@Test
public void testDeployFromContextErrorInStart() throws Exception {
testDeployFromThrowableInStart(MyVerticle.THROW_ERROR, Error.class);
}
private void testDeployFromThrowableInStart(int startAction, Class<? extends Throwable> expectedThrowable) throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
Context ctx = Vertx.currentContext();
MyVerticle verticle2 = new MyVerticle(startAction, MyVerticle.NOOP);
vertx.deployVerticle(verticle2, ar2 -> {
assertFalse(ar2.succeeded());
assertEquals(expectedThrowable, ar2.cause().getClass());
assertEquals("FooBar!", ar2.cause().getMessage());
assertEquals(1, vertx.deploymentIDs().size());
Context ctx2 = Vertx.currentContext();
assertEquals(ctx, ctx2);
testComplete();
});
});
await();
}
@Test
public void testDeployFromContextExceptonInStop() throws Exception {
testDeployFromContextThrowableInStop(MyVerticle.THROW_EXCEPTION, Exception.class);
}
@Test
public void testDeployFromContextErrorInStop() throws Exception {
testDeployFromContextThrowableInStop(MyVerticle.THROW_ERROR, Error.class);
}
private void testDeployFromContextThrowableInStop(int stopAction, Class<? extends Throwable> expectedThrowable) throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
Context ctx = Vertx.currentContext();
MyVerticle verticle2 = new MyVerticle(MyVerticle.NOOP, stopAction);
vertx.deployVerticle(verticle2, ar2 -> {
assertTrue(ar2.succeeded());
vertx.undeploy(ar2.result(), ar3 -> {
assertFalse(ar3.succeeded());
assertEquals(expectedThrowable, ar3.cause().getClass());
assertEquals("BooFar!", ar3.cause().getMessage());
assertEquals(1, vertx.deploymentIDs().size());
assertEquals(ctx, Vertx.currentContext());
testComplete();
});
});
});
await();
}
@Test
public void testUndeploy() throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
vertx.undeploy(ar.result(), ar2 -> {
assertTrue(ar2.succeeded());
assertNull(ar2.result());
assertFalse(vertx.deploymentIDs().contains(ar.result()));
assertEquals(verticle.startContext, verticle.stopContext);
Context currentContext = Vertx.currentContext();
assertNotSame(currentContext, verticle.startContext);
testComplete();
});
});
await();
}
@Test
public void testUndeployNoHandler() throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
vertx.undeploy(ar.result());
});
assertWaitUntil(() -> vertx.deploymentIDs().isEmpty());
}
@Test
public void testUndeployTwice() throws Exception {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
vertx.undeploy(ar.result(), ar2 -> {
assertTrue(ar2.succeeded());
vertx.undeploy(ar.result(), ar3 -> {
assertFalse(ar3.succeeded());
assertTrue(ar3.cause() instanceof IllegalStateException);
testComplete();
});
});
});
await();
}
@Test
public void testUndeployInvalidID() throws Exception {
vertx.undeploy("uqhwdiuhqwd", ar -> {
assertFalse(ar.succeeded());
assertTrue(ar.cause() instanceof IllegalStateException);
testComplete();
});
await();
}
@Test
public void testDeployExceptionInStart() throws Exception {
testDeployThrowableInStart(MyVerticle.THROW_EXCEPTION, Exception.class);
}
@Test
public void testDeployErrorInStart() throws Exception {
testDeployThrowableInStart(MyVerticle.THROW_ERROR, Error.class);
}
private void testDeployThrowableInStart(int startAction, Class<? extends Throwable> expectedThrowable) throws Exception {
MyVerticle verticle = new MyVerticle(startAction, MyVerticle.NOOP);
vertx.deployVerticle(verticle, ar -> {
assertFalse(ar.succeeded());
assertEquals(expectedThrowable, ar.cause().getClass());
assertEquals("FooBar!", ar.cause().getMessage());
assertTrue(vertx.deploymentIDs().isEmpty());
testComplete();
});
await();
}
@Test
public void testUndeployExceptionInStop() throws Exception {
testUndeployThrowableInStop(MyVerticle.THROW_EXCEPTION, Exception.class);
}
@Test
public void testUndeployErrorInStop() throws Exception {
testUndeployThrowableInStop(MyVerticle.THROW_ERROR, Error.class);
}
private void testUndeployThrowableInStop(int stopAction, Class<? extends Throwable> expectedThrowable) throws Exception {
MyVerticle verticle = new MyVerticle(MyVerticle.NOOP, stopAction);
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
vertx.undeploy(ar.result(), ar2 -> {
assertFalse(ar2.succeeded());
assertEquals(expectedThrowable, ar2.cause().getClass());
assertEquals("BooFar!", ar2.cause().getMessage());
assertTrue(vertx.deploymentIDs().isEmpty());
testComplete();
});
});
await();
}
@Test
public void testDeployUndeployMultiple() throws Exception {
int num = 10;
CountDownLatch deployLatch = new CountDownLatch(num);
for (int i = 0; i < num; i++) {
MyVerticle verticle = new MyVerticle();
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
assertTrue(vertx.deploymentIDs().contains(ar.result()));
deployLatch.countDown();
});
}
assertTrue(deployLatch.await(10, TimeUnit.SECONDS));
assertEquals(num, vertx.deploymentIDs().size());
CountDownLatch undeployLatch = new CountDownLatch(num);
for (String deploymentID: vertx.deploymentIDs()) {
vertx.undeploy(deploymentID, ar -> {
assertTrue(ar.succeeded());
assertFalse(vertx.deploymentIDs().contains(deploymentID));
undeployLatch.countDown();
});
}
assertTrue(undeployLatch.await(10, TimeUnit.SECONDS));
assertTrue(vertx.deploymentIDs().isEmpty());
}
@Test(expected = IllegalArgumentException.class)
public void testDeployInstanceSetInstances() throws Exception {
vertx.deployVerticle(new MyVerticle(), new DeploymentOptions().setInstances(2));
}
@Test(expected = IllegalArgumentException.class)
public void testDeployInstanceSetExtraClasspath() throws Exception {
vertx.deployVerticle(new MyVerticle(), new DeploymentOptions().setExtraClasspath(Arrays.asList("foo")));
}
@Test(expected = IllegalArgumentException.class)
public void testDeployInstanceSetIsolationGroup() throws Exception {
vertx.deployVerticle(new MyVerticle(), new DeploymentOptions().setIsolationGroup("foo"));
}
@Test(expected = IllegalArgumentException.class)
public void testDeployInstanceSetIsolatedClasses() throws Exception {
vertx.deployVerticle(new MyVerticle(), new DeploymentOptions().setIsolatedClasses(Arrays.asList("foo")));
}
@Test
public void testDeployUsingClassName() throws Exception {
vertx.deployVerticle("java:" + TestVerticle.class.getCanonicalName(), ar -> {
assertTrue(ar.succeeded());
testComplete();
});
await();
}
@Test
public void testDeployUsingClassAndConfig() throws Exception {
JsonObject config = generateJSONObject();
vertx.deployVerticle("java:" + TestVerticle.class.getCanonicalName(), new DeploymentOptions().setConfig(config), ar -> {
assertTrue(ar.succeeded());
testComplete();
});
await();
}
@Test
public void testDeployUsingClassFails() throws Exception {
vertx.deployVerticle("java:uhqwuhiqwduhwd", ar -> {
assertFalse(ar.succeeded());
assertTrue(ar.cause() instanceof ClassNotFoundException);
testComplete();
});
await();
}
@Test
public void testDeployUndeployMultipleInstancesUsingClassName() throws Exception {
int numInstances = 10;
DeploymentOptions options = new DeploymentOptions().setInstances(numInstances);
AtomicInteger deployCount = new AtomicInteger();
AtomicInteger undeployCount = new AtomicInteger();
AtomicInteger deployHandlerCount = new AtomicInteger();
AtomicInteger undeployHandlerCount = new AtomicInteger();
vertx.eventBus().<String>consumer("tvstarted").handler(msg -> {
deployCount.incrementAndGet();
});
vertx.eventBus().<String>consumer("tvstopped").handler(msg -> {
undeployCount.incrementAndGet();
msg.reply("whatever");
});
CountDownLatch deployLatch = new CountDownLatch(1);
vertx.deployVerticle(TestVerticle2.class.getCanonicalName(), options, onSuccess(depID -> {
assertEquals(1, deployHandlerCount.incrementAndGet());
deployLatch.countDown();
}));
awaitLatch(deployLatch);
assertWaitUntil(() -> deployCount.get() == numInstances);
assertEquals(1, vertx.deploymentIDs().size());
Deployment deployment = ((VertxInternal) vertx).getDeployment(vertx.deploymentIDs().iterator().next());
Set<Verticle> verticles = deployment.getVerticles();
assertEquals(numInstances, verticles.size());
CountDownLatch undeployLatch = new CountDownLatch(1);
assertEquals(numInstances, deployCount.get());
vertx.undeploy(deployment.deploymentID(), onSuccess(v -> {
assertEquals(1, undeployHandlerCount.incrementAndGet());
undeployLatch.countDown();
}));
awaitLatch(undeployLatch);
assertWaitUntil(() -> deployCount.get() == numInstances);
assertTrue(vertx.deploymentIDs().isEmpty());
}
@Test
public void testDeployClassNotFound1() throws Exception {
testDeployClassNotFound("iqwjdiqwjdoiqwjdqwij");
}
@Test
public void testDeployClassNotFound2() throws Exception {
testDeployClassNotFound("foo.bar.wibble.CiejdioqjdoiqwjdoiqjwdClass");
}
private void testDeployClassNotFound(String className) throws Exception {
vertx.deployVerticle(className, ar -> {
assertTrue(ar.failed());
// No prefix or suffix so should be interpreted as a java class
assertTrue(ar.cause() instanceof ClassNotFoundException);
testComplete();
});
await();
}
@Test
public void testDeployAsSource() throws Exception {
String sourceFile = SourceVerticle.class.getName().replace('.', '/');
sourceFile += ".java";
vertx.deployVerticle("java:" + sourceFile, onSuccess(res -> {
testComplete();
}));
await();
}
@Test
public void testSimpleChildDeployment() throws Exception {
Verticle verticle = new MyAsyncVerticle(f -> {
Context parentContext = Vertx.currentContext();
Verticle child1 = new MyAsyncVerticle(f2 -> {
Context childContext = Vertx.currentContext();
assertNotSame(parentContext, childContext);
f2.complete(null);
testComplete();
}, f2 -> f2.complete(null));
vertx.deployVerticle(child1, ar -> {
assertTrue(ar.succeeded());
});
f.complete(null);
}, f -> f.complete(null));
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
});
await();
}
@Test
public void testSimpleChildUndeploymentOrder() throws Exception {
AtomicBoolean childStopCalled = new AtomicBoolean();
AtomicBoolean parentStopCalled = new AtomicBoolean();
AtomicReference<String> parentDepID = new AtomicReference<>();
AtomicReference<String> childDepID = new AtomicReference<>();
CountDownLatch deployLatch = new CountDownLatch(1);
Verticle verticle = new MyAsyncVerticle(f -> {
Verticle child1 = new MyAsyncVerticle(f2 -> f2.complete(null), f2 -> {
// Child stop is called
assertFalse(parentStopCalled.get());
assertFalse(childStopCalled.get());
childStopCalled.set(true);
f2.complete(null);
});
vertx.deployVerticle(child1, ar -> {
assertTrue(ar.succeeded());
childDepID.set(ar.result());
f.complete(null);
});
}, f2 -> {
// Parent stop is called
assertFalse(parentStopCalled.get());
assertTrue(childStopCalled.get());
assertTrue(vertx.deploymentIDs().contains(parentDepID.get()));
assertFalse(vertx.deploymentIDs().contains(childDepID.get()));
parentStopCalled.set(true);
testComplete();
f2.complete(null);
});
vertx.deployVerticle(verticle, ar -> {
parentDepID.set(ar.result());
assertTrue(ar.succeeded());
deployLatch.countDown();
});
assertTrue(deployLatch.await(10, TimeUnit.SECONDS));
assertTrue(vertx.deploymentIDs().contains(parentDepID.get()));
assertTrue(vertx.deploymentIDs().contains(childDepID.get()));
// Now they're deployed, undeploy them
vertx.undeploy(parentDepID.get(), ar -> {
assertTrue(ar.succeeded());
});
await();
}
@Test
public void testAsyncDeployCalledSynchronously() throws Exception {
MyAsyncVerticle verticle = new MyAsyncVerticle(f -> f.complete(null), f -> f.complete(null));
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
testComplete();
});
await();
}
@Test
public void testAsyncDeployFailureCalledSynchronously() throws Exception {
MyAsyncVerticle verticle = new MyAsyncVerticle(f -> f.fail(new Exception("foobar")), null);
vertx.deployVerticle(verticle, ar -> {
assertFalse(ar.succeeded());
assertEquals("foobar", ar.cause().getMessage());
testComplete();
});
await();
}
@Test
public void testAsyncDeploy() throws Exception {
long start = System.currentTimeMillis();
long delay = 1000;
MyAsyncVerticle verticle = new MyAsyncVerticle(f -> {
vertx.setTimer(delay, id -> {
f.complete(null);
});
}, f -> f.complete(null));
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
long now = System.currentTimeMillis();
assertTrue(now - start >= delay);
assertTrue(vertx.deploymentIDs().contains(ar.result()));
testComplete();
});
Thread.sleep(delay / 2);
assertTrue(vertx.deploymentIDs().isEmpty());
await();
}
@Test
public void testAsyncDeployFailure() throws Exception {
long start = System.currentTimeMillis();
long delay = 1000;
MyAsyncVerticle verticle = new MyAsyncVerticle(f -> vertx.setTimer(delay, id -> f.fail(new Exception("foobar"))), null);
vertx.deployVerticle(verticle, ar -> {
assertFalse(ar.succeeded());
assertEquals("foobar", ar.cause().getMessage());
long now = System.currentTimeMillis();
assertTrue(now - start >= delay);
assertTrue(vertx.deploymentIDs().isEmpty());
testComplete();
});
await();
}
@Test
public void testAsyncUndeployCalledSynchronously() throws Exception {
MyAsyncVerticle verticle = new MyAsyncVerticle(f -> f.complete(null), f -> f.complete(null));
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
vertx.undeploy(ar.result(), ar2 -> {
assertTrue(ar2.succeeded());
assertFalse(vertx.deploymentIDs().contains(ar.result()));
testComplete();
});
});
await();
}
@Test
public void testAsyncUndeployFailureCalledSynchronously() throws Exception {
MyAsyncVerticle verticle = new MyAsyncVerticle(f -> f.complete(null), f -> f.fail(new Exception("foobar")));
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
vertx.undeploy(ar.result(), ar2 -> {
assertFalse(ar2.succeeded());
assertEquals("foobar", ar2.cause().getMessage());
assertFalse(vertx.deploymentIDs().contains(ar.result()));
testComplete();
});
});
await();
}
@Test
public void testAsyncUndeploy() throws Exception {
long delay = 1000;
MyAsyncVerticle verticle = new MyAsyncVerticle(f-> f.complete(null), f -> vertx.setTimer(delay, id -> f.complete(null)));
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
long start = System.currentTimeMillis();
vertx.undeploy(ar.result(), ar2 -> {
assertTrue(ar2.succeeded());
long now = System.currentTimeMillis();
assertTrue(now - start >= delay);
assertFalse(vertx.deploymentIDs().contains(ar.result()));
testComplete();
});
vertx.setTimer(delay / 2, id -> assertFalse(vertx.deploymentIDs().isEmpty()));
});
await();
}
@Test
public void testAsyncUndeployFailure() throws Exception {
long delay = 1000;
MyAsyncVerticle verticle = new MyAsyncVerticle(f-> f.complete(null), f -> vertx.setTimer(delay, id -> f.fail(new Exception("foobar"))));
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
long start = System.currentTimeMillis();
vertx.undeploy(ar.result(), ar2 -> {
assertFalse(ar2.succeeded());
long now = System.currentTimeMillis();
assertTrue(now - start >= delay);
assertFalse(vertx.deploymentIDs().contains(ar.result()));
testComplete();
});
});
await();
}
@Test
public void testChildUndeployedDirectly() throws Exception {
Verticle parent = new AbstractVerticle() {
@Override
public void start(Future<Void> startFuture) throws Exception {
Verticle child = new AbstractVerticle() {
@Override
public void start(Future<Void> startFuture) throws Exception {
startFuture.complete();
// Undeploy it directly
vertx.runOnContext(v -> vertx.undeploy(context.deploymentID()));
}
};
vertx.deployVerticle(child, onSuccess(depID -> {
startFuture.complete();
}));
}
@Override
public void stop(Future<Void> stopFuture) throws Exception {
super.stop(stopFuture);
}
};
vertx.deployVerticle(parent, onSuccess(depID -> {
vertx.setTimer(10, tid -> vertx.undeploy(depID, onSuccess(v -> {
testComplete();
})));
}));
await();
}
@Test
public void testCloseHooksCalled() throws Exception {
AtomicInteger closedCount = new AtomicInteger();
Closeable myCloseable1 = completionHandler -> {
closedCount.incrementAndGet();
completionHandler.handle(Future.succeededFuture());
};
Closeable myCloseable2 = completionHandler -> {
closedCount.incrementAndGet();
completionHandler.handle(Future.succeededFuture());
};
MyAsyncVerticle verticle = new MyAsyncVerticle(f-> {
ContextImpl ctx = (ContextImpl)Vertx.currentContext();
ctx.addCloseHook(myCloseable1);
ctx.addCloseHook(myCloseable2);
f.complete(null);
}, f -> f.complete(null));
vertx.deployVerticle(verticle, ar -> {
assertTrue(ar.succeeded());
assertEquals(0, closedCount.get());
// Now undeploy
vertx.undeploy(ar.result(), ar2 -> {
assertTrue(ar2.succeeded());
assertEquals(2, closedCount.get());
testComplete();
});
});
await();
}
@Test
public void testDeployWhenClosedShouldFail() throws Exception {
CountDownLatch closed = new CountDownLatch(1);
vertx.close(ar -> {
assertTrue(ar.succeeded());
closed.countDown();
});
awaitLatch(closed);
vertx.deployVerticle(new AbstractVerticle() {
}, ar -> {
assertFalse(ar.succeeded());
assertEquals("Vert.x closed", ar.cause().getMessage());
testComplete();
});
await();
}
@Test
public void testIsolationGroup1() throws Exception {
List<String> isolatedClasses = Arrays.asList(TestVerticle.class.getCanonicalName());
vertx.deployVerticle("java:" + TestVerticle.class.getCanonicalName(),
new DeploymentOptions().setIsolationGroup("somegroup").setIsolatedClasses(isolatedClasses), ar -> {
assertTrue(ar.succeeded());
assertEquals(0, TestVerticle.instanceCount.get());
testComplete();
});
await();
}
@Test
public void testNullIsolationGroup() throws Exception {
vertx.deployVerticle("java:" + TestVerticle.class.getCanonicalName(), new DeploymentOptions().setIsolationGroup(null), ar -> {
assertTrue(ar.succeeded());
assertEquals(1, TestVerticle.instanceCount.get());
testComplete();
});
await();
}
@Test
public void testIsolationGroupSameGroup() throws Exception {
List<String> isolatedClasses = Arrays.asList(TestVerticle.class.getCanonicalName());
testIsolationGroup("somegroup", "somegroup", 1, 2, isolatedClasses, "java:" + TestVerticle.class.getCanonicalName());
}
@Test
public void testIsolationGroupSameGroupWildcard() throws Exception {
List<String> isolatedClasses = Arrays.asList("io.vertx.test.core.*");
testIsolationGroup("somegroup", "somegroup", 1, 2, isolatedClasses, "java:" + TestVerticle.class.getCanonicalName());
}
@Test
public void testIsolationGroupDifferentGroup() throws Exception {
List<String> isolatedClasses = Arrays.asList(TestVerticle.class.getCanonicalName());
testIsolationGroup("somegroup", "someothergroup", 1, 1, isolatedClasses, "java:" + TestVerticle.class.getCanonicalName());
}
private String createClassOutsideClasspath(String className) throws Exception {
File dir = Files.createTempDirectory("vertx").toFile();
dir.deleteOnExit();
File source = new File(dir, className + ".java");
Files.write(source.toPath(), ("public class " + className + " extends io.vertx.core.AbstractVerticle {} ").getBytes());
URLClassLoader loader = new URLClassLoader(new URL[]{dir.toURI().toURL()}, Thread.currentThread().getContextClassLoader());
CompilingClassLoader compilingClassLoader = new CompilingClassLoader(loader, className + ".java");
compilingClassLoader.loadClass(className);
byte[] bytes = compilingClassLoader.getClassBytes(className);
assertNotNull(bytes);
File classFile = new File(dir, className + ".class");
Files.write(classFile.toPath(), bytes);
return dir.getAbsolutePath();
}
@Test
public void testExtraClasspathLoaderNotInParentLoader() throws Exception {
String dir = createClassOutsideClasspath("MyVerticle");
List<String> extraClasspath = Arrays.asList(dir);
vertx.deployVerticle("java:" + ExtraCPVerticleNotInParentLoader.class.getCanonicalName(), new DeploymentOptions().setIsolationGroup("somegroup").
setExtraClasspath(extraClasspath), ar -> {
assertTrue(ar.succeeded());
testComplete();
});
await();
}
@Test
public void testExtraClasspathLoaderAlreadyInParentLoader() throws Exception {
String dir = createClassOutsideClasspath("MyVerticle");
URLClassLoader loader = new URLClassLoader(new URL[]{new File(dir).toURI().toURL()}, Thread.currentThread().getContextClassLoader());
List<String> extraClasspath = Arrays.asList(dir);
ClassLoader currentCL = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(loader);
try {
vertx.deployVerticle("java:" + ExtraCPVerticleAlreadyInParentLoader.class.getCanonicalName(), new DeploymentOptions().setIsolationGroup("somegroup").
setExtraClasspath(extraClasspath), ar -> {
assertTrue(ar.succeeded());
testComplete();
});
} finally {
Thread.currentThread().setContextClassLoader(currentCL);
}
await();
}
public static class ParentVerticle extends AbstractVerticle {
@Override
public void start(Future<Void> startFuture) throws Exception {
vertx.deployVerticle("java:" + ChildVerticle.class.getName(), ar -> {
if (ar.succeeded()) {
startFuture.complete(null);
} else {
ar.cause().printStackTrace();
}
});
}
}
public static class ChildVerticle extends AbstractVerticle {
}
@Test
public void testUndeployAll() throws Exception {
int numVerticles = 10;
List<MyVerticle> verticles = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(numVerticles);
for (int i = 0; i < numVerticles; i++) {
MyVerticle verticle = new MyVerticle();
verticles.add(verticle);
vertx.deployVerticle("java:" + ParentVerticle.class.getName(), onSuccess(res -> {
latch.countDown();
}));
}
awaitLatch(latch);
assertEquals(2 * numVerticles, vertx.deploymentIDs().size());
vertx.close(ar -> {
assertTrue(ar.succeeded());
assertEquals(0, vertx.deploymentIDs().size());
testComplete();
});
await();
vertx = null;
}
@Test
public void testUndeployAllFailureInUndeploy() throws Exception {
int numVerticles = 10;
List<MyVerticle> verticles = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(numVerticles);
for (int i = 0; i < numVerticles; i++) {
MyVerticle verticle = new MyVerticle(MyVerticle.NOOP, MyVerticle.THROW_EXCEPTION);
verticles.add(verticle);
vertx.deployVerticle(verticle, ar2 -> {
assertTrue(ar2.succeeded());
latch.countDown();
});
}
awaitLatch(latch);
vertx.close(ar -> {
assertTrue(ar.succeeded());
for (MyVerticle verticle: verticles) {
assertFalse(verticle.stopCalled);
}
testComplete();
});
await();
vertx = null;
}
@Test
public void testUndeployAllNoDeployments() throws Exception {
vertx.close(ar -> {
assertTrue(ar.succeeded());
testComplete();
});
await();
vertx = null;
}
@Test
public void testGetInstanceCount() throws Exception {
class MultiInstanceVerticle extends AbstractVerticle {
@Override
public void start() {
assertEquals(vertx.getOrCreateContext().getInstanceCount(), 1);
}
}
vertx.deployVerticle(new MultiInstanceVerticle(), ar -> {
assertTrue(ar.succeeded());
testComplete();
});
await();
Deployment deployment = ((VertxInternal) vertx).getDeployment(vertx.deploymentIDs().iterator().next());
vertx.undeploy(deployment.deploymentID());
}
@Test
public void testGetInstanceCountMultipleVerticles() throws Exception {
AtomicInteger messageCount = new AtomicInteger(0);
AtomicInteger totalReportedInstances = new AtomicInteger(0);
vertx.eventBus().consumer("instanceCount", event -> {
messageCount.incrementAndGet();
totalReportedInstances.addAndGet((int)event.body());
if(messageCount.intValue() == 3) {
assertEquals(9, totalReportedInstances.get());
testComplete();
}
});
vertx.deployVerticle(TestVerticle3.class.getCanonicalName(), new DeploymentOptions().setInstances(3), ar -> {
assertTrue(ar.succeeded());
});
await();
Deployment deployment = ((VertxInternal) vertx).getDeployment(vertx.deploymentIDs().iterator().next());
vertx.undeploy(deployment.deploymentID());
}
@Test
public void testFailedVerticleStopNotCalled() {
Verticle verticleChild = new AbstractVerticle() {
@Override
public void start(Future<Void> startFuture) throws Exception {
startFuture.fail("wibble");
}
@Override
public void stop() {
fail("stop should not be called");
}
};
Verticle verticleParent = new AbstractVerticle() {
@Override
public void start(Future<Void> startFuture) throws Exception {
vertx.deployVerticle(verticleChild, onFailure(v -> {
startFuture.complete();
}));
}
};
vertx.deployVerticle(verticleParent, onSuccess(depID -> {
vertx.undeploy(depID, onSuccess(v -> {
testComplete();
}));
}));
await();
}
@Test
public void testUndeployWhenUndeployIsInProgress() throws Exception {
int numIts = 10;
CountDownLatch latch = new CountDownLatch(numIts);
for (int i = 0; i < numIts; i++) {
Verticle parent = new AbstractVerticle() {
@Override
public void start() throws Exception {
vertx.deployVerticle(new AbstractVerticle() {
}, id -> vertx.undeploy(id.result()));
}
};
vertx.deployVerticle(parent, id -> {
vertx.undeploy(id.result(), res -> {
latch.countDown();
});
});
}
awaitLatch(latch);
}
// TODO
// Multi-threaded workers
private void testIsolationGroup(String group1, String group2, int count1, int count2, List<String> isolatedClasses,
String verticleID) throws Exception {
Map<String, Integer> countMap = new ConcurrentHashMap<>();
vertx.eventBus().<JsonObject>consumer("testcounts").handler((Message<JsonObject> msg) -> {
countMap.put(msg.body().getString("deploymentID"), msg.body().getInteger("count"));
});
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<String> deploymentID1 = new AtomicReference<>();
AtomicReference<String> deploymentID2 = new AtomicReference<>();
vertx.deployVerticle(verticleID, new DeploymentOptions().
setIsolationGroup(group1).setIsolatedClasses(isolatedClasses), ar -> {
assertTrue(ar.succeeded());
deploymentID1.set(ar.result());
assertEquals(0, TestVerticle.instanceCount.get());
vertx.deployVerticle(verticleID,
new DeploymentOptions().setIsolationGroup(group2).setIsolatedClasses(isolatedClasses), ar2 -> {
assertTrue(ar2.succeeded());
deploymentID2.set(ar2.result());
assertEquals(0, TestVerticle.instanceCount.get());
latch.countDown();
});
});
awaitLatch(latch);
// Wait until two entries in the map
assertWaitUntil(() -> countMap.size() == 2);
assertEquals(count1, countMap.get(deploymentID1.get()).intValue());
assertEquals(count2, countMap.get(deploymentID2.get()).intValue());
}
private void assertDeployment(int instances, MyVerticle verticle, JsonObject config, AsyncResult<String> ar) {
assertTrue(ar.succeeded());
assertEquals(vertx, verticle.getVertx());
String deploymentID = ar.result();
assertNotNull(ar.result());
assertEquals(deploymentID, verticle.deploymentID);
if (config == null) {
assertEquals(0, verticle.config.size());
} else {
assertEquals(config, verticle.config);
}
assertTrue(verticle.startCalled);
assertFalse(verticle.stopCalled);
assertTrue(vertx.deploymentIDs().contains(deploymentID));
assertEquals(instances, vertx.deploymentIDs().size());
Context currentContext = Vertx.currentContext();
assertNotSame(currentContext, verticle.startContext);
}
private JsonObject generateJSONObject() {
return new JsonObject().put("foo", "bar").put("blah", 123)
.put("obj", new JsonObject().put("quux", "flip"));
}
public class MyVerticle extends AbstractVerticle {
static final int NOOP = 0, THROW_EXCEPTION = 1, THROW_ERROR = 2;
boolean startCalled;
boolean stopCalled;
Context startContext;
Context stopContext;
int startAction;
int stopAction;
String deploymentID;
JsonObject config;
MyVerticle() {
this(NOOP, NOOP);
}
MyVerticle(int startAction, int stopAction) {
this.startAction = startAction;
this.stopAction = stopAction;
}
@Override
public void start() throws Exception {
switch (startAction) {
case THROW_EXCEPTION:
throw new Exception("FooBar!");
case THROW_ERROR:
throw new Error("FooBar!");
default:
startCalled = true;
startContext = Vertx.currentContext();
}
deploymentID = Vertx.currentContext().deploymentID();
config = context.config();
}
@Override
public void stop() throws Exception {
switch (stopAction) {
case THROW_EXCEPTION:
throw new Exception("BooFar!");
case THROW_ERROR:
throw new Error("BooFar!");
default:
stopCalled = true;
stopContext = Vertx.currentContext();
}
}
}
public class MyAsyncVerticle extends AbstractVerticle {
private final Consumer<Future<Void>> startConsumer;
private final Consumer<Future<Void>> stopConsumer;
public MyAsyncVerticle(Consumer<Future<Void>> startConsumer, Consumer<Future<Void>> stopConsumer) {
this.startConsumer = startConsumer;
this.stopConsumer = stopConsumer;
}
@Override
public void start(Future<Void> startFuture) throws Exception {
if (startConsumer != null) {
startConsumer.accept(startFuture);
}
}
@Override
public void stop(Future<Void> stopFuture) throws Exception {
if (stopConsumer != null) {
stopConsumer.accept(stopFuture);
}
}
}
}