These are my first impressions on what have changed in Vert.x 3 from a regular java developer perspective.
Vert.x 3 has ripped off the old modules concepts in favor of a more straightforward approach. Now you don’t need to create a .zip file with an structure to deploy your application, instead, you run your application inside vert.x however you want. These is a great power and at first I found myself a little lost.
You can still download a vert.x distribution but, with the current state of the platform, I see it like an easy way to start making experiments (even in the many languages vert.x support) and it’s not necessary anymore for productive environments.
In Vert.x 3 you create your own main
function, invoke a Vert.x factory, and voilà, you have the whole platform there to serve you. Indeed, you can see the io.vertx.core.Starter
class, which is invoked by the executable bin/vertx
in the distribution, to see there is nothing but CLI args parsing and building configurations for the factory I mentioned before.
From the v2 era I liked to have a main point written in javascript to deploying all my java verticles. I though it as my init script so it made sense to have it in an scripting language, I didn’t want to lose that. So now it’s my responsibility to create this structure. A few lines of code is worth a thousand words:
(Deprecated: see Update section below)
public class Launcher {
public static void main(String[] args) {
Logger vertxLogger = LoggerFactory.getLogger(Launcher.class.getName());
Vertx vertx = Vertx.vertx();
vertx.deployVerticle("main.js", event -> {
if (event.succeeded()) {
vertxLogger.info("Your Vert.x application is started!");
} else {
vertxLogger.error("Unable to start your application", event.cause());
}
});
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
final CountDownLatch latch = new CountDownLatch(1);
vertx.close(ar -> {
if (ar.succeeded()) {
vertxLogger.info("Your Vert.x application is stopped!");
} else {
vertxLogger.error("Failure in stopping Vert.x", ar.cause());
}
latch.countDown();
});
try {
if (!latch.await(2, TimeUnit.MINUTES)) {
vertxLogger.error("Timed out waiting to undeploy all");
}
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
});
}
}
In line 5 I deploy my main verticle.
Lines 13-32 are stolen from io.vertx.core.Starter
to properly shutdown the application.
For this to run you just need a few dependencies: io.vertx:vertx-core:jar:3.0.0-milestone4:compile
and io.vertx:vertx-lang-js:jar:3.0.0-milestone4:runtime
Packing your application
Now it’s just a matter of packing the application to make it runnable. There is a movement loving the concept of fatJar, I personally dislike it for various reasons, one of them is that I see it completely unnecessary, we are advanced Java developers, man up, setup you classpath and invoke the Main Class as the good lord says (well, that was too much).
In Maven this is extra easy, you just need to properly setup the maven-jar-plugin
to write the MANIFEST and the maven-assembly-plugin
to load an assembly descriptor. Then you can have a simple run.sh
script for running it without even having to remember the name of your main jar. I use something like this:
#!/usr/bin/env bash
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
java -jar $DIR/libs/${project.build.finalName}.${project.packaging} $@
Here is a minimal pom.xml
briefing all the above ideas:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.locademiaz</groupId>
<artifactId>initial-vertx-3-project</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-lang-js</artifactId>
<version>${vertx.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/js</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifest>
<mainClass>com.locademiaz.Launcher</mainClass>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<descriptor>src/main/assembly/dist.xml</descriptor>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<vertx.version>3.0.0-milestone4</vertx.version>
</properties>
</project>
And also the dist.xml
:
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>dist</id>
<formats>
<format>tar.gz</format>
</formats>
<baseDirectory>${project.artifactId}</baseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>libs/</outputDirectory>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
<files>
<file>
<source>${project.build.directory}/${project.build.finalName}.${project.packaging}</source>
<outputDirectory>libs/</outputDirectory>
<fileMode>0664</fileMode>
</file>
<file>
<source>src/main/sh/run.sh</source>
<fileMode>0744</fileMode>
<filtered>true</filtered>
</file>
</files>
</assembly>
With this initial configuration, you run mvn package
and you’ll got a tarball into the target/ folder, untar it and you’ll have a nice running script for your whole application.
Bonus track
I prefer to use Logback as my logging system, and in the old days we had to modify the vert.x distribution and the executable to get this support.
Nowadays you just add System.setProperty(LoggerFactory.LOGGER_DELEGATE_FACTORY_CLASS_NAME, SLF4JLogDelegateFactory.class.getName());
as the first line of your main, add the ch.qos.logback:logback-classic:jar:1.1.3:runtime
dependency and you are there.
For the extra lazy, here is a simple logback.xml
:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender" level="INFO">
<encoder>
<pattern>[%-5level] %logger{15} - %msg%n</pattern>
</encoder>
</appender>
<logger name="io.vertx" level="INFO">
<appender-ref ref="STDOUT" />
</logger>
<logger name="com.hazelcast" level="ERROR">
<appender-ref ref="STDOUT" />
</logger>
<logger name="io.netty.util.internal.PlatformDependent" level="ERROR">
<appender-ref ref="STDOUT" />
</logger>
<root level="INFO">
</root>
</configuration>
I also like to have an user accessible logging configuration, for this, you must add the following to the dist.xml
file
<file>
<source>src/main/resources/logback.xml</source>
<fileMode>0644</fileMode>
</file>
and modify the java invocation line in run.sh
to include -Dlogback.configurationFile=file:$DIR/logback.xml
I hope you find this useful and enjoy the great things Vert.x 3 has to offer.
UPDATE:
I’ve been thinking and I rather see unnecessary to have my own main class which at the end will imitate the provided io.vertx.core.Starter. So I’ve simplified my initial deployment removing all references to my custom mainClass and with this run.sh
#!/usr/bin/env bash
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
java -Dvertx.logger-delegate-factory-class-name=io.vertx.core.logging.impl.SLF4JLogDelegateFactory \
-Dlogback.configurationFile=file:$DIR/logback.xml \
-classpath "$DIR/libs/*" io.vertx.core.Starter run main.js \
-conf $DIR/conf.json
Also, if you want to run your application using maven (useful for debugging) you just need to configure the org.codehaus.mojo:exec-maven-plugin
like this:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<mainClass>io.vertx.core.Starter</mainClass>
<systemProperties>
<systemProperty>
<key>vertx.logger-delegate-factory-class-name</key>
<value>io.vertx.core.logging.impl.SLF4JLogDelegateFactory</value>
</systemProperty>
</systemProperties>
<arguments>
<argument>run</argument>
<argument>main.js</argument>
<argument>-conf</argument>
<argument>${project.build.outputDirectory}/conf.json</argument>
</arguments>
</configuration>
</plugin>