Using Eclipse Vert.x API from a Quarkus Application

Vert.x is a toolkit for building reactive applications. As described in the Quarkus Reactive Architecture, Quarkus uses Vert.x underneath.

Quarkus Reactive Core

Quarkus applications can access and use the Vert.x APIs.

This guide presents how you can build a Quarkus application using:

  • the managed instance of Vert.x

  • the Vert.x event bus

  • the Vert.x Web Client

It’s an introductory guide. The Vert.x reference guide covers more advanced features such as verticles, and native transports.

Architecture

We are going to build a simple application exposing four HTTP endpoints:

  1. /vertx/lorem returns the content from a small file

  2. /vertx/book returns the content from a large file (a book)

  3. /vertx/hello uses the Vert.x event bus to produce the response

  4. /vertx/web uses the Vert.x Web Client to retrieve data from Wikipedia

Architecture of the Vert.x guide

Solution

We recommend that you follow the instructions in the following sections and create the application step by step. However, you can go right to the completed example.

Clone the Git repository: git clone https://github.com/quarkusio/quarkus-quickstarts.git, or download an archive.

The solution is located in the vertx-quickstart directory.

Mutiny

This guide uses the Mutiny API. If you are not familiar with Mutiny, check Mutiny - an intuitive, reactive programming library.

Bootstrapping the application

Click on this link to configure your application. It selected a few extensions:

  • rest-jackson, which also brings rest. We are going to use it to expose our HTTP endpoints.

  • vertx, which provides access to the underlying managed Vert.x

Click on the Generate your application button, download the zip file and unzip it. Then, open the project in your favorite IDE.

If you open the generated build file, you can see the selected extensions:

pom.xml
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-vertx</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-rest-jackson")
implementation("io.quarkus:quarkus-vertx")

While you are in your build file, add the following dependency:

pom.xml
<dependency>
  <groupId>io.smallrye.reactive</groupId>
  <artifactId>smallrye-mutiny-vertx-web-client</artifactId>
</dependency>
build.gradle
implementation("io.smallrye.reactive:smallrye-mutiny-vertx-web-client")

This dependency provides the Vert.x Web Client, which we will be using to implement the /web endpoint.

Accessing the managed Vert.x instance

Create the src/main/java/org/acme/VertxResource.java file. It will contain our HTTP endpoints.

In this file, copy the following code:

package org.acme;

import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;

import java.nio.charset.StandardCharsets;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/vertx")                        (1)
public class VertxResource {

    private final Vertx vertx;

    @Inject                             (2)
    public VertxResource(Vertx vertx) { (3)
        this.vertx = vertx;             (4)
    }
}
1 Declare the root HTTP path.
2 We use constructor injection to receive the managed Vert.x instance. Field injection works too.
3 Receives the Vert.x instance as a constructor parameter
4 Store the managed Vert.x instance into a field.

With this, we can start implementing the endpoints.

Using Vert.x Core API

The injected Vert.x instance provides a set of APIs you can use. The one we are going to use in this section is the Vert.x File System. It provides a non-blocking API to access files.

In the src/main/resources directory, create a lorem.txt file with the following content:

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.

Then, in the VertxResource file add the following method:

@GET                                                                                   (1)
@Path("/lorem")
public Uni<String> readShortFile() {                                                   (2)
    return vertx.fileSystem().readFile("lorem.txt")                                    (3)
            .onItem().transform(content -> content.toString(StandardCharsets.UTF_8));  (4)
}
1 This endpoint handles HTTP GET request on path /lorem (so the full path will be vertx/lorem)
2 As the Vert.x API is asynchronous, our method returns a Uni. The content is written into the HTTP response when the asynchronous operation represented by the Uni completes.
3 We use the Vert.x file system API to read the created file
4 Once the file is read, the content is stored in an in-memory buffer. We transform this buffer into a String.

In a terminal, navigate to the root of the project and run:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

In another terminal, run:

> curl http://localhost:8080/vertx/lorem

You should see the content of the file printed in the console.

Quarkus provides other ways to serve static files. This is an example made for the guide.

Using Vert.x stream capability

Reading a file and storing the content in memory works for small files, but not big ones. In this section, we will see how you can use Vert.x streaming capability.

First, download War and Peace and store it in src/main/resources/book.txt. It’s a 3.2 Mb file, which, while not being huge, illustrates the purpose of streams. This time, we will not accumulate the file’s content in memory and write it in one batch, but read it chunk by chunk and write these chunks into the HTTP response one by one.

So, you should have the following files in your project:

.
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
├── src
│  └── main
│     ├── docker
│     │  ├── ...
│     ├── java
│     │  └── org
│     │     └── acme
│     │        └── VertxResource.java
│     └── resources
│        ├── application.properties
│        ├── book.txt
│        └── lorem.txt

Add the following imports to the src/main/java/org/acme/VertxResource.java file:

import io.smallrye.mutiny.Multi;
import io.vertx.core.file.OpenOptions;

Add the following method to the VertxResource class:

@GET
@Path("/book")
public Multi<String> readLargeFile() {                                               (1)
    return vertx.fileSystem().open("book.txt",                                       (2)
                    new OpenOptions().setRead(true)
            )
            .onItem().transformToMulti(file -> file.toMulti())                       (3)
            .onItem().transform(content -> content.toString(StandardCharsets.UTF_8) (4)
                    + "\n------------\n");                                           (5)
}
1 This time, we return a Multi as we want to stream the chunks
2 We open the file using the open method. It returns a Uni<AsyncFile>
3 When the file is opened, we retrieve a Multi which will contain the chunks.
4 For each chunk, we produce a String
5 To visually see the chunks in the response, we append a separator

Then, in a terminal, run:

> curl http://localhost:8080/vertx/book

It should retrieve the book content. In the output you should see the separator like:

...
The little princess had also left the tea table and followed Hélène.

“Wait a moment, I’ll get my work.... Now then, what
------------
 are you
thinking of?” she went on, turning to Prince Hippolyte. “Fetch me my
workbag.”
...

Using the event bus

One of the core features of Vert.x is the event bus. It provides a message-based backbone to your application. So, you can have components interacting using asynchronous message passing, and so decouple your components. You can send a message to a single consumer, or dispatch to multiple consumers, or implement a request-reply interaction, where you send a message (request) and expect a response. This is what we are going to use in this section. Our VertxResource will send a message containing a name to the greetings address. Another component will receive the message and produce the "hello $name" response. The VertxResource will receive the response and return it as the HTTP response.

So, first, add the following imports to the src/main/java/org/acme/VertxResource.java file:

import io.vertx.mutiny.core.eventbus.EventBus;
import jakarta.ws.rs.QueryParam;

Next, let’s extend our VertxResource class with the following code:

@Inject
EventBus bus;                                                   (1)

@GET
@Path("/hello")
public Uni<String> hello(@QueryParam("name") String name) {     (2)
    return bus.<String>request("greetings", name)               (3)
            .onItem().transform(response -> response.body());   (4)
}
1 Inject the event bus. Alternatively you can use vertx.eventBus().
2 We receive a name as a query parameter
3 We use the request method to initiate the request-reply interaction. We send the name to the "greetings" address.
4 When the response is received, we extract the body and return it as the HTTP response

Now, we need the other side: the component receiving the name and replying. Create the src/main/java/org/acme/GreetingService.java file with the following content:

package org.acme;

import io.quarkus.vertx.ConsumeEvent;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped                          (1)
public class GreetingService {

    @ConsumeEvent("greetings")              (2)
    public String hello(String name) {      (3)
        return "Hello " + name;             (4)
    }
}
1 Declaring a CDI Bean in the Application scope. Quarkus will create a single instance of this class.
2 Use the @ConsumeEvent annotation to declare a consumer. It is possible to use the Vert.x API directly too.
3 Receive the message payload as a method parameter. The returned object will be the reply.
4 Return the response. This response is sent back to the VertxResource class

Let’s try this. In a terminal, run:

> curl "http://localhost:8080/vertx/hello?name=bob"

You should get the expected Hello bob message back.

Using Vert.x Clients

So far, we have used the Vert.x Core API. Vert.x offers much more. It provides a vast ecosystem. In this section, we will see how you can use the Vert.x Web Client, a reactive HTTP client.

Note that some Quarkus extensions are wrapping Vert.x clients and manage them for you. That’s the case for the reactive data sources, Redis, mail…​ That’s not the case with the Web Client.

Remember, at the beginning of the guide, we added the smallrye-mutiny-vertx-web-client dependency to our pom.xml file. It’s now time to use it.

First, add the following imports to the src/main/java/org/acme/VertxResource.java file:

import io.vertx.core.json.JsonArray;
import io.vertx.mutiny.ext.web.client.HttpResponse;
import io.vertx.mutiny.ext.web.client.WebClient;

Next, we need to create an instance of WebClient. Extend the VertxResource class with the client field and the creation of the web client in the constructor:

private final Vertx vertx;
private final WebClient client;            (1)

@Inject
public VertxResource(Vertx vertx) {
    this.vertx = vertx;
    this.client = WebClient.create(vertx); (2)
}
1 Store the WebClient, so we will be able to use it in our HTTP endpoint
2 Create the WebClient. Be sure to use the io.vertx.mutiny.ext.web.client.WebClient class

Let’s now implement a new HTTP endpoint that queries the Wikipedia API to retrieve the pages about Quarkus in the different languages. Add the following method to the VertxResource class:

private static final String URL = "https://en.wikipedia.org/w/api.php?action=parse&page=Quarkus&format=json&prop=langlinks";

@GET
@Path("/web")
public Uni<JsonArray> retrieveDataFromWikipedia() {                     (1)
    return client.getAbs(URL).send()                                    (2)
            .onItem().transform(HttpResponse::bodyAsJsonObject)         (3)
            .onItem().transform(json -> json.getJsonObject("parse")     (4)
                                        .getJsonArray("langlinks"));
}
1 This endpoint returns a JSON Array. Vert.x provides a convenient way to manipulate JSON Object and Array. More details about these in the reference guide.
2 Send a GET request to the Wikipedia API
3 Once the response is received, extract it as a JSON Object
4 Extract the langlinks array from the response.

Then, invoke the endpoint using:

> curl http://localhost:8080/vertx/web
[{"lang":"de","url":"https://de.wikipedia.org/wiki/Quarkus","langname":"German","autonym":"Deutsch","*":"Quarkus"},{"lang":"fr","url":"https://fr.wikipedia.org/wiki/Quarkus","langname":"French","autonym":"français","*":"Quarkus"}]

The response indicates that, in addition to the English page, there are a German and a French page about Quarkus on wikipedia.

Executing Asynchronous Code From a Blocking Thread

Sometimes it’s necessary to execute an asynchronous code from a blocking thread. Specifically, to execute the code on a Vert.x thread with an isolated/duplicated Vert.x context. A typical example is an asynchronous code that needs to leverage the Hibernate Reactive API during application startup. Quarkus provides the VertxContextSupport#subscribeAndAwait() method which subscribes to the supplied io.smallrye.mutiny.Uni on a Vert.x duplicated context, then blocks the current thread and waits for the result.

void onStart(@Observes StartupEvent event, Mutiny.SessionFactory sf) {
   VertxContextSupport.subscribeAndAwait(() -> {
      return sf.withTransaction(session -> session.persist(new Person()));
   });
}
If necessary, the CDI request context is activated during execution of the asynchronous code.
VertxContextSupport#subscribeAndAwait() must not be called on an event loop!

It is also possible to subscribe to a supplied io.smallrye.mutiny.Multi on a Vert.x duplicated context. In this case, the current thread is not blocked and the supplied subscription logic is used to consume the events.

void onStart(@Observes StartupEvent event, ExternalService service) {
   VertxContextSupport.subscribeWith(() -> service.getFoos(), foo -> {
     // do something useful with foo
   });
}

Going further

This guide introduced how you can use Vert.x APIs from a Quarkus application. It’s just a brief overview. If you want to know more, check the reference guide about Vert.x in Quarkus.

As we have seen, the event bus is the connecting tissue of Vert.x applications. Quarkus integrates it so different beans can interact with asynchronous messages. This part is covered in the event bus documentation.

Learn how to implement highly performant, low-overhead database applications on Quarkus with the Reactive SQL Clients.