/*
* 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.DeploymentOptions;
import io.vertx.core.Launcher;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.impl.launcher.commands.HelloCommand;
import io.vertx.core.impl.launcher.commands.RunCommand;
import io.vertx.core.impl.launcher.commands.VersionCommand;
import io.vertx.core.json.JsonObject;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
public class LauncherTest extends VertxTestBase {
private String expectedVersion;
private ByteArrayOutputStream out;
private PrintStream stream;
private Vertx vertx;
@Override
public void setUp() throws Exception {
super.setUp();
TestVerticle.instanceCount.set(0);
TestVerticle.processArgs = null;
TestVerticle.conf = null;
// Read the expected version from the vertx=version.txt
final URL resource = this.getClass().getClassLoader().getResource("vertx-version.txt");
if (resource == null) {
throw new IllegalStateException("Cannot find the vertx-version.txt");
} else {
BufferedReader in = new BufferedReader(
new InputStreamReader(resource.openStream()));
expectedVersion = in.readLine();
in.close();
}
Launcher.resetProcessArguments();
out = new ByteArrayOutputStream();
stream = new PrintStream(out);
}
@Override
public void tearDown() throws Exception {
clearProperties();
super.tearDown();
out.close();
stream.close();
if (vertx != null) {
vertx.close();
}
}
@Test
public void testVersion() throws Exception {
String[] args = {"-version"};
MyLauncher launcher = new MyLauncher();
launcher.dispatch(args);
final VersionCommand version = (VersionCommand) launcher.getExistingCommandInstance("version");
assertNotNull(version);
assertEquals(version.getVersion(), expectedVersion);
}
@Test
public void testRunVerticleWithoutArgs() throws Exception {
MyLauncher launcher = new MyLauncher();
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName()};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
assertEquals(Arrays.asList(args), TestVerticle.processArgs);
launcher.assertHooksInvoked();
}
@Test
public void testRunWithoutArgs() throws Exception {
MyLauncher launcher = new MyLauncher() {
@Override
public PrintStream getPrintStream() {
return stream;
}
};
String[] args = {"run"};
launcher.dispatch(args);
assertTrue(out.toString().contains("The argument 'main-verticle' is required"));
}
@Test
public void testNoArgsAndNoMainVerticle() throws Exception {
MyLauncher launcher = new MyLauncher() {
@Override
public PrintStream getPrintStream() {
return stream;
}
};
String[] args = {};
launcher.dispatch(args);
assertTrue(out.toString().contains("Usage:"));
assertTrue(out.toString().contains("bare"));
assertTrue(out.toString().contains("run"));
assertTrue(out.toString().contains("hello"));
}
@Test
public void testRunVerticle() throws Exception {
testRunVerticleMultiple(1);
}
@Test
public void testRunVerticleMultipleInstances() throws Exception {
testRunVerticleMultiple(10);
}
public void testRunVerticleMultiple(int instances) throws Exception {
MyLauncher launcher = new MyLauncher();
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName(), "-instances", String.valueOf(instances)};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == instances);
assertEquals(Arrays.asList(args), TestVerticle.processArgs);
launcher.assertHooksInvoked();
}
@Test
public void testRunVerticleClustered() throws Exception {
MyLauncher launcher = new MyLauncher();
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName(), "-cluster"};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
assertEquals(Arrays.asList(args), TestVerticle.processArgs);
launcher.assertHooksInvoked();
}
@Test
public void testRunVerticleHA() throws Exception {
MyLauncher launcher = new MyLauncher();
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName(), "-ha"};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
assertEquals(Arrays.asList(args), TestVerticle.processArgs);
launcher.assertHooksInvoked();
}
@Test
public void testRunVerticleWithMainVerticleInManifestNoArgs() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Launcher.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Launcher launcher = new Launcher();
String[] args = new String[0];
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
assertEquals(Arrays.asList(args), TestVerticle.processArgs);
cleanup(launcher);
}
private void cleanup(Launcher launcher) {
RunCommand run = (RunCommand) launcher.getExistingCommandInstance("run");
if (run != null) {
Vertx v = run.vertx();
if (v != null) {
v.close();
}
}
}
@Test
public void testRunVerticleWithMainVerticleInManifestWithArgs() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Launcher.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Launcher launcher = new Launcher();
String[] args = {"-cluster", "-worker", "-instances=10"};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 10);
assertEquals(Arrays.asList(args), TestVerticle.processArgs);
cleanup(launcher);
}
@Test
public void testRunVerticleWithMainVerticleInManifestWithCustomCommand() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher-hello.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Launcher-hello.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Launcher launcher = new Launcher();
HelloCommand.called = false;
String[] args = {"--name=vert.x"};
launcher.dispatch(args);
assertWaitUntil(() -> HelloCommand.called);
}
@Test
public void testRunVerticleWithoutMainVerticleInManifestButWithCustomCommand() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher-Default-Command.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Default-Command.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Launcher launcher = new Launcher();
HelloCommand.called = false;
String[] args = {"--name=vert.x"};
launcher.dispatch(args);
assertWaitUntil(() -> HelloCommand.called);
}
@Test
public void testRunWithOverriddenDefaultCommand() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher-hello.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Launcher-hello.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
HelloCommand.called = false;
String[] args = {"run", TestVerticle.class.getName(), "--name=vert.x"};
Launcher launcher = new Launcher();
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
cleanup(launcher);
}
@Test
public void testRunWithOverriddenDefaultCommandRequiringArgs() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher-run.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Launcher-run.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
String[] args = {TestVerticle.class.getName()};
Launcher launcher = new Launcher();
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
cleanup(launcher);
}
@Test
public void testRunVerticleWithExtendedMainVerticleNoArgs() throws Exception {
MySecondLauncher launcher = new MySecondLauncher();
String[] args = new String[0];
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
assertEquals(Arrays.asList(args), TestVerticle.processArgs);
}
@Test
public void testRunVerticleWithExtendedMainVerticleWithArgs() throws Exception {
MySecondLauncher launcher = new MySecondLauncher();
String[] args = {"-cluster", "-worker"};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
assertEquals(Arrays.asList(args), TestVerticle.processArgs);
}
@Test
public void testFatJarWithHelp() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Launcher.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Launcher launcher = new Launcher() {
@Override
public PrintStream getPrintStream() {
return stream;
}
};
String[] args = {"--help"};
launcher.dispatch(args);
assertTrue(out.toString().contains("Usage"));
assertTrue(out.toString().contains("run"));
assertTrue(out.toString().contains("version"));
assertTrue(out.toString().contains("bare"));
}
@Test
public void testFatJarWithCommandHelp() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Launcher.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Launcher launcher = new Launcher() {
@Override
public PrintStream getPrintStream() {
return stream;
}
};
String[] args = {"hello", "--help"};
launcher.dispatch(args);
assertTrue(out.toString().contains("Usage"));
assertTrue(out.toString().contains("hello"));
assertTrue(out.toString().contains("A simple command to wish you a good day.")); // Description text.
}
@Test
public void testFatJarWithMissingCommandHelp() throws Exception {
// Copy the right manifest
File manifest = new File("target/test-classes/META-INF/MANIFEST-Launcher.MF");
if (!manifest.isFile()) {
throw new IllegalStateException("Cannot find the MANIFEST-Launcher.MF file");
}
File target = new File("target/test-classes/META-INF/MANIFEST.MF");
Files.copy(manifest.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
Launcher launcher = new Launcher() {
@Override
public PrintStream getPrintStream() {
return stream;
}
};
String[] args = {"not-a-command", "--help"};
launcher.dispatch(args);
assertTrue(out.toString().contains("The command 'not-a-command' is not a valid command."));
}
@Test
public void testRunVerticleWithConfString() throws Exception {
MyLauncher launcher = new MyLauncher();
JsonObject conf = new JsonObject().put("foo", "bar").put("wibble", 123);
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName(), "-conf", conf.encode()};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
assertEquals(conf, TestVerticle.conf);
}
@Rule
public TemporaryFolder testFolder = new TemporaryFolder();
@Test
public void testRunVerticleWithConfFile() throws Exception {
Path tempDir = testFolder.newFolder().toPath();
Path tempFile = Files.createTempFile(tempDir, "conf", "json");
MyLauncher launcher = new MyLauncher();
JsonObject conf = new JsonObject().put("foo", "bar").put("wibble", 123);
Files.write(tempFile, conf.encode().getBytes());
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName(), "-conf", tempFile.toString()};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
assertEquals(conf, TestVerticle.conf);
}
@Test
public void testConfigureFromSystemProperties() throws Exception {
testConfigureFromSystemProperties(false);
}
@Test
public void testConfigureFromSystemPropertiesClustered() throws Exception {
testConfigureFromSystemProperties(true);
}
private void testConfigureFromSystemProperties(boolean clustered) throws Exception {
// One for each type that we support
System.setProperty(RunCommand.VERTX_OPTIONS_PROP_PREFIX + "eventLoopPoolSize", "123");
System.setProperty(RunCommand.VERTX_OPTIONS_PROP_PREFIX + "maxEventLoopExecuteTime", "123767667");
System.setProperty(RunCommand.METRICS_OPTIONS_PROP_PREFIX + "enabled", "true");
System.setProperty(RunCommand.VERTX_OPTIONS_PROP_PREFIX + "haGroup", "somegroup");
MyLauncher launcher = new MyLauncher();
String[] args;
if (clustered) {
args = new String[] {"run", "java:" + TestVerticle.class.getCanonicalName(), "-cluster"};
} else {
args = new String[] {"run", "java:" + TestVerticle.class.getCanonicalName()};
}
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
VertxOptions opts = launcher.getVertxOptions();
assertEquals(123, opts.getEventLoopPoolSize(), 0);
assertEquals(123767667L, opts.getMaxEventLoopExecuteTime());
assertEquals(true, opts.getMetricsOptions().isEnabled());
assertEquals("somegroup", opts.getHAGroup());
}
private void clearProperties() {
Set<String> toClear = new HashSet<>();
Enumeration e = System.getProperties().propertyNames();
// Uhh, properties suck
while (e.hasMoreElements()) {
String propName = (String) e.nextElement();
if (propName.startsWith("vertx.options")) {
toClear.add(propName);
}
}
toClear.forEach(System::clearProperty);
}
@Test
public void testCustomMetricsOptions() throws Exception {
System.setProperty(RunCommand.METRICS_OPTIONS_PROP_PREFIX + "enabled", "true");
System.setProperty(RunCommand.METRICS_OPTIONS_PROP_PREFIX + "customProperty", "customPropertyValue");
MyLauncher launcher = new MyLauncher();
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName()};
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(MetricsOptionsTest.createMetricsFromMetaInfLoader("io.vertx.test.core.CustomMetricsFactory"));
try {
launcher.dispatch(args);
} finally {
Thread.currentThread().setContextClassLoader(oldCL);
}
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
VertxOptions opts = launcher.getVertxOptions();
CustomMetricsOptions custom = (CustomMetricsOptions) opts.getMetricsOptions();
assertEquals("customPropertyValue", custom.getCustomProperty());
}
@Test
public void testConfigureFromSystemPropertiesInvalidPropertyName() throws Exception {
System.setProperty(RunCommand.VERTX_OPTIONS_PROP_PREFIX + "nosuchproperty", "123");
// Should be ignored
MyLauncher launcher = new MyLauncher();
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName()};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
VertxOptions opts = launcher.getVertxOptions();
VertxOptions def = new VertxOptions();
if (opts.getMetricsOptions().isEnabled()) {
def.getMetricsOptions().setEnabled(true);
}
assertEquals(def, opts);
}
@Test
public void testConfigureFromSystemPropertiesInvalidPropertyType() throws Exception {
// One for each type that we support
System.setProperty(RunCommand.VERTX_OPTIONS_PROP_PREFIX + "eventLoopPoolSize", "sausages");
// Should be ignored
MyLauncher launcher = new MyLauncher();
String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName()};
launcher.dispatch(args);
assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
VertxOptions opts = launcher.getVertxOptions();
VertxOptions def = new VertxOptions();
if (opts.getMetricsOptions().isEnabled()) {
def.getMetricsOptions().setEnabled(true);
}
assertEquals(def, opts);
}
@Test
public void testWhenPassingTheMainObject() throws Exception {
MyLauncher launcher = new MyLauncher();
int instances = 10;
launcher.dispatch(launcher, new String[] {"run", "java:" + TestVerticle.class.getCanonicalName(),
"-instances", "10"});
assertWaitUntil(() -> TestVerticle.instanceCount.get() == instances);
}
@Test
public void testBare() throws Exception {
MyLauncher launcher = new MyLauncher();
launcher.dispatch(new String[] {"bare"});
assertWaitUntil(() -> launcher.afterStartingVertxInvoked);
}
@Test
public void testBareAlias() throws Exception {
MyLauncher launcher = new MyLauncher();
launcher.dispatch(new String[] {"-ha"});
assertWaitUntil(() -> launcher.afterStartingVertxInvoked);
}
class MyLauncher extends Launcher {
boolean afterConfigParsed = false;
boolean beforeStartingVertxInvoked = false;
boolean afterStartingVertxInvoked = false;
boolean beforeDeployingVerticle = false;
VertxOptions options;
DeploymentOptions deploymentOptions;
JsonObject config;
PrintStream stream = new PrintStream(out);
/**
* @return the printer used to write the messages. Defaults to {@link System#out}.
*/
@Override
public PrintStream getPrintStream() {
return stream;
}
public Vertx getVertx() {
return vertx;
}
public VertxOptions getVertxOptions() {
return options;
}
@Override
public void afterConfigParsed(JsonObject config) {
afterConfigParsed = true;
this.config = config;
}
@Override
public void beforeStartingVertx(VertxOptions options) {
beforeStartingVertxInvoked = true;
this.options = options;
}
@Override
public void afterStartingVertx(Vertx vertx) {
afterStartingVertxInvoked = true;
LauncherTest.this.vertx = vertx;
}
@Override
public void beforeDeployingVerticle(DeploymentOptions deploymentOptions) {
beforeDeployingVerticle = true;
this.deploymentOptions = deploymentOptions;
}
public void assertHooksInvoked() {
assertTrue(afterConfigParsed);
assertTrue(beforeStartingVertxInvoked);
assertTrue(afterStartingVertxInvoked);
assertTrue(beforeDeployingVerticle);
assertNotNull(vertx);
}
}
class MySecondLauncher extends MyLauncher {
@Override
public String getMainVerticle() {
return "java:io.vertx.test.core.TestVerticle";
}
}
}