My first Vert.x 3 Application
Let’s say, you heard someone saying that Vert.x is awesome. Ok great, but you may want to try it by yourself. Well, the next natural question is “where do I start ?”. This post is a good starting point. It shows how is built a very simple vert.x application (nothing fancy), how it is tested and how it is packaged and executed. So, everything you need to know before building your own groundbreaking application.
The code developed in this post is available on github. This post is part of the Introduction to Vert.x series. The code of this post in in the post-1 branch.
Let’s start !
First, let’s create a project. In this post, we use Apache Maven, but you can use Gradle or the build process tool you prefer. You could use the Maven jar archetype to create the structure, but basically, you just need a directory with:
- a
src/main/java
directory - a
src/test/java
directory - a
pom.xml
file
So, you would get something like:
.
├── pom.xml
├── src
│ ├── main
│ │ └── java
│ └── test
│ └── java
Let’s create the pom.xml
file with the following content:
<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>io.vertx.blog</groupId>
<artifactId>my-first-app</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
This pom.xml
file is pretty straightforward:
- it declares a dependency on
vertx-core
- it configures the maven-compiler-plugin to use Java 8.
This second point is important, Vert.x applications require Java 8.
Let’s code !
Ok, now we have made the pom.xml
file. Let’s do some real coding… Create the src/main/java/io/vertx/blog/first/MyFirstVerticle.java
file with the following content:
package io.vertx.blog.first;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
public class MyFirstVerticle extends AbstractVerticle {
@Override
public void start(Future<Void> fut) {
vertx
.createHttpServer()
.requestHandler(r -> {
r.response().end("<h1>Hello from my first " +
"Vert.x 3 application</h1>");
})
.listen(8080, result -> {
if (result.succeeded()) {
fut.complete();
} else {
fut.fail(result.cause());
}
});
}
}
This is actually our not fancy application. The class extends AbstractVerticle
. In the Vert.x world, a verticle is a component. By extending AbstractVerticle
, our class gets access to the vertx
field.
The start
method is called when the verticle is deployed. We could also implement a stop
method, but in this case Vert.x takes care of the garbage for us. The start
method receives a Future
object that will let us inform Vert.x when our start sequence is completed or report an error. One of the particularity of Vert.x is its asynchronous / non-blocking aspect. When our verticle is going to be deployed it won’t wait until the start method has been completed. So, the Future
parameter is important to notify of the completion.
The start
method creates a HTTP server and attaches a request handler to it. The request handler is a lambda, passed in the requestHandler
method, called every time the server receives a request. Here, we just reply Hello ...
(nothing fancy I told you). Finally, the server is bound to the 8080 port. As this may fails (because the port may already be used), we pass another lambda expression checking whether or not the connection has succeeded. As mentioned above it calls either fut.complete
in case of success or fut.fail
to report an error.
Let’s try to compile the application using:
mvn clean compile
Fortunately, it should succeed.
That’s all for the application.
Let’s test
Well, that’s good to have developed an application, but we can never be too careful, so let’s test it. The test uses JUnit and vertx-unit - a framework delivered with vert.x to make the testing of vert.x application more natural.
Open the pom.xml
file to add the two following dependencies:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-unit</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
Now create the src/test/java/io/vertx/blog/first/MyFirstVerticleTest.java
with the following content:
package io.vertx.blog.first;
import io.vertx.core.Vertx;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(VertxUnitRunner.class)
public class MyFirstVerticleTest {
private Vertx vertx;
@Before
public void setUp(TestContext context) {
vertx = Vertx.vertx();
vertx.deployVerticle(MyFirstVerticle.class.getName(),
context.asyncAssertSuccess());
}
@After
public void tearDown(TestContext context) {
vertx.close(context.asyncAssertSuccess());
}
@Test
public void testMyApplication(TestContext context) {
final Async async = context.async();
vertx.createHttpClient().getNow(8080, "localhost", "/",
response -> {
response.handler(body -> {
context.assertTrue(body.toString().contains("Hello"));
async.complete();
});
});
}
}
This is a JUnit test for our verticle. The test uses vertx-unit, so we use a custom runner. vert.x-unit makes easy to test asynchronous interactions, which are the basis of vert.x applications.
In the setUp
method, we creates an instance of Vertx
and deploy our verticle. You may have noticed that unlike the traditional JUnit @Before
method, it receives a TestContext
. This object lets us control the asynchronous aspect of our test. For instance, when we deploy our verticle, it starts asynchronously, as most Vert.x interactions. We cannot check anything until it gets started correctly. So, as second argument of the deployVerticle
method, we pass a result handler: context.asyncAssertSuccess()
. It fails the test if the verticle does not start correctly. In addition it waits until the verticle has completed its start sequence. Remember, in our verticle, we call fut.complete()
. So it waits until this method is called, and in the case of a failures, fails the test.
Well, the tearDown
method is straightforward, and just terminates the vertx
instance we created.
Let’s now have a look to the test of our application: the testMyApplication
method. The test emits a request to our application and checks the result. Emitting the request and receiving the response is asynchronous. So we need a way to control this. As the setUp
and tearDown
methods, the test method receives a TestContext
. From this object we creates an async handle (async
) that lets us notify the test framework when the test has completed (using async.complete()
).
So, once the async handle is created, we create a HTTP client and emits a HTTP request handled by our application with the getNow()
method (getNow
is just a shortcut for get(...).end()
). The response is handled by a lambda. In this lambda we retrieves the response body by passing another lambda to the handler
method. The body
argument is the response body (as a buffer
object). We check that the body contains the "Hello"
String and declare the test complete.
Let’s take a minute to mention the assertions. Unlike in traditional JUnit tests, it uses context.assert...
. Indeed, if the assertion fails, it will interrupt the test immediately. So it’s pretty important to always uses these assertion methods because of the asynchronous aspect of the Vert.x application and so tests.
Our test can be run from an IDE, or using Maven:
mvn clean test
Packaging
So, let’s sum up. We have an application and a test. Well, let’s now package the application. In this post we package the application in a fat jar. A fat jar is a standalone executable Jar file containing all the dependencies required to run the application. This is a very convenient way to package Vert.x applications as it’s only one file. It also make them easy to execute.
To create a fat jar, edit the pom.xml
file and add the following snippet just before </plugins>
:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>io.vertx.core.Starter</Main-Class>
<Main-Verticle>io.vertx.blog.first.MyFirstVerticle</Main-Verticle>
</manifestEntries>
</transformer>
</transformers>
<artifactSet/>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>
</configuration>
</execution>
</executions>
</plugin>
It uses the maven-shade-plugin to create the fat jar
. In the manifestEntries
it indicates the name of our verticle. You may wonder from where comes the Starter
class. It’s actually a class from vert.x, that is going to create the vertx
instance and deploy our verticle.
So, with this plugin configured, let’s launch:
mvn clean package
This is going to create target/my-first-app-1.0-SNAPSHOT-fat.jar
embedding our application along with all the dependencies (including vert.x itself).
Executing our application
Well, it’s nice to have a fat jar, but we want to see our application running! As said above, thanks to the fat jar packaging, running Vert.x application is easy as:
java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar
Then, open a browser to http://localhost:8080.
To stop the application, hit CTRL+C
.
Conclusion
This Vert.x 3 crash class has presented how you can develop a simple application using Vert.x 3, how to test it, package it and run it. So, you now know everything you need to build amazing system on top of Vert.x 3. Next time we will see how to configure our application.
Happy coding & Stay tuned !