Let’s say, you heard someone saying that Eclipse Vert.x is awesome. Ok great, but you may want to try it yourself. The next logical question is “where do I start?”. This article is a good starting point. It shows: how to build a very simple Vert.x application (nothing fancy), how it is tested, and how it is packaged and executed. Basically everything you need to know before building your own groundbreaking application.
The code developed in this article is available on GitHub. This is part of the "Introduction to Vert.x Series". The code for this post is located in the https://github.com/redhat-developer/introduction-to-eclipse-vertx repository in the post-1
directory.
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:
4.0.0
io.vertx.intro
my-first-app
1.0-SNAPSHOT
3.5.0
1.0.13
io.vertx
vertx-core
${vertx.version}
maven-compiler-plugin
3.7.0
1.8
1.8
io.fabric8
vertx-maven-plugin
${vmp.version}
vmp
initialize
package
true
This pom.xml
file is pretty straightforward:
- It declares a dependency on the `vertx-core`, the Vert.x version is declared as a property.
- It configures the `maven-compiler-plugin` to use Java 8.
- It declares the `vertx-maven-plugin`; we will come back to this in a bit.
Vert.x requires at least Java 8, so don't try to run a Vert.x application on a Java 6 or 7 JVM; it won't work.
The vertx-maven-plugin
is an optional plugin that packages your app and provides additional functionality (documentation is here). Otherwise you can use the maven-shade-plugin
or any other packaging plugin. The vertx-maven-plugin
is convenient as it packages the application without any configuration. It also provides a redeploy feature, restarting the application when you update the code or the resources.
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/intro/first/MyFirstVerticle.java
file with the following content:
package io.vertx.intro.first;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
public class MyFirstVerticle extends AbstractVerticle {
@Override
public void start(Future fut) {
vertx
.createHttpServer()
.requestHandler(r ->
r.response()
.end("
<h1>Hello from my first Vert.x 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, and the vertx
instance on which the verticle is deployed.
The start
method is called when the verticle is deployed. We could also implement a stop
method (called when the verticle is undeployed), but in this case Vert.x takes care of the garbage for us. The start
method receives a Future
object that lets us notify Vert.x when our start sequence has been completed or reports an error if anything bad happens. One of the particularities of Vert.x is its asynchronous and 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 for notification of the completion. Notice that you can also implement a version of the start
method without the Future
parameter. In this case, Vert.x considers the verticle deployed when the start
method returns.
The start
method creates an HTTP server and attaches a request handler to it. The request handler is a function (here a lambda), which is passed into the requestHandler
method, and is called every time the server receives a request. In this code, we just reply Hello (Nothing fancy I told you). Finally, the server is bound to the 8080
port. As this may fail (because the port may already be used), we pass another lambda expression called with the result (and so are able to check 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.
Before trying the application, edit the pom.xml
file and add the vertx.verticle
entry in the properties:
3.5.0
1.0.13
<!-- line to add: -->
io.vertx.intro.first.MyFirstVerticle
This property instructs Vert.x to deploy this class when it starts.
Let’s try to compile the application using:
mvn compile vertx:run
The application is started; open your browser to http://localhost:8080 and you should see the Hello message. If you change the message in the code and save the file, the application is restarted with the updated message.
Hit CTRL+C
to stop the application.
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:
junit
junit
4.12
test
io.vertx
vertx-unit
${vertx.version}
test
Now create the src/test/java/io/vertx/intro/first/MyFirstVerticleTest.java
with the following content:
package io.vertx.intro.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 case for our verticle. The test uses vertx-unit
, so we configure a custom runner (with the @RunWith
annotation). vertx-unit
makes easy to test asynchronous interactions, which are the basis of Vert.x applications.
In the setUp
method (called before each test), we create an instance of vertx
and deploy our verticle. You may have noticed that unlike the traditional JUnit @Before
method, it receives a TestContext
object. This object lets us control the asynchronous aspect of our test. For instance, when we deploy our verticle, it starts asynchronously, as do most Vert.x interactions. We cannot check anything until it gets started correctly. So, as a 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 failures, fails the test.
Well, the tearDown
(called after each test) method is straightforward, and just closes the vertx
instance we created.
Let’s now have a look at 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. Like the setUp
and tearDown
methods, the test method receives a TestContext
. From this object we create 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 an HTTP client and emit an HTTP request handled by our application with the getNow()
method (getNow
is just a shortcut for get(...).end()
). The response is handled by a handler. In this function, we retrieve the response body by passing another function to the handler method. The body
argument is the response body (as a buffer
object). We check that the body contains"Hello"
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 important to use these assertions because of the asynchronous aspect of the Vert.x application and tests. However, Vert.x Unit provides hooks to let you use Hamcrest or AssertJ, as demonstrated in this example and this other example.
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. 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 makes them easy to execute.
To create a fat jar, just run:
mvn clean package
The vertx-maven-plugin
is taking care of the packaging and creates the target/my-first-app-1.0-SNAPSHOT.jar
file embedding our application along with all the dependencies (including Vert.x itself). Check the size: around 6MB; and this includes everything to run the application.
Executing our application
Well, it’s nice to have a jar, but we want to see our application running! As stated above, thanks to the fat jar packaging, running the Vert.x application is easy:
java -jar target/my-first-app-1.0-SNAPSHOT.jar
Then, open a browser to http://localhost:8080.
To stop the application, hit CTRL+C
.
Conclusion
This Vert.x crash course has presented: how you can develop a simple application using Vert.x; how to test it, package it, and run it. So you now have a great start on the road to building an amazing system on top of Vert.x. Next time we will see how to configure our application. Don't forget that the code from this blog post series is available in this repository.
Happy coding & Stay tuned!
Last updated: April 22, 2022