` container.
-The magic, as always, lies in the Javascript code:
-
-[source,javascript]
-.META-INF/resources/streaming.js
-----
-if (!!window.EventSource) {
- var eventSource = new EventSource("/stream/Quarkus");
- eventSource.onmessage = function (event) {
- var container = document.getElementById("container");
- var paragraph = document.createElement("p");
- paragraph.innerHTML = event.data;
- container.appendChild(paragraph);
- };
-} else {
- window.alert("EventSource not available on this browser.")
+@GET // <1>
+@Path("/lorem")
+public Uni
readShortFile() { // <2>
+ return vertx.fileSystem().readFile("lorem.txt") // <3>
+ .onItem().transform(content -> content.toString("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.
-IMPORTANT: Most browsers support SSE but some don't.
-More about this in Mozilla's https://developer.mozilla.org/en-US/docs/Web/API/EventSource#Browser_compatibility[SSE browser-compatibility list].
-
-Navigate to http://localhost:8080/streaming.html.
-A new greeting should show-up every 2 seconds.
-
-[source, text]
-----
-Hello Quarkus! (Wed Feb 12 17:13:55 CET 2020)
-
-Hello Quarkus! (Wed Feb 12 17:13:57 CET 2020)
-
-Hello Quarkus! (Wed Feb 12 17:13:59 CET 2020)
-
-Hello Quarkus! (Wed Feb 12 17:14:01 CET 2020)
-
-Hello Quarkus! (Wed Feb 12 17:14:03 CET 2020)
-
-...
-----
-
-=== Using Vert.x JSON
-
-Vert.x API heavily relies on JSON, namely the `io.vertx.core.json.JsonObject` and `io.vertx.core.json.JsonArray` types.
-They are both supported as Quarkus web resource request and response bodies.
-
-Consider these endpoints:
-
-[source,java]
-----
-@Path("/hello")
-@Produces(MediaType.APPLICATION_JSON)
-public class VertxJsonResource {
-
- @GET
- @Path("{name}/object")
- public JsonObject jsonObject(@PathParam String name) {
- return new JsonObject().put("Hello", name);
- }
-
- @GET
- @Path("{name}/array")
- public JsonArray jsonArray(@PathParam String name) {
- return new JsonArray().add("Hello").add(name);
- }
-}
-----
+In a terminal, navigate to the root of the project and run:
-In your browser, navigate to http://localhost:8080/hello/Quarkus/object. You should see:
-[source, text]
+[source, bash]
----
-{"Hello":"Quarkus"}
+> ./mvnw quarkus:dev
----
-Then, navigate to http://localhost:8080/hello/Quarkus/array:
+In another terminal, run:
-[source, text]
+[source, bash]
----
-["Hello","Quarkus"]
+> curl http://localhost:8080/vertx/lorem
----
-Needless to say, this works equally well when the JSON content is a request body or is wrapped in a `Uni`, `Multi`, `CompletionStage` or `Publisher`.
-
-== Using Vert.x Clients
+You should see the content of the file printed in the console.
-As you can inject a Vert.x instance, you can use Vert.x clients in a Quarkus application.
-This section gives an example with the `WebClient`.
+IMPORTANT: Quarkus provides other ways to serve static files. This is an example made for the guide.
-=== Picking the right dependency
+== Using Vert.x stream capability
-Depending on the API model you want to use you need to add the right dependency to your `pom.xml` file:
+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.
-[source, xml, subs=attributes+]
-----
-
-
- io.vertx
- vertx-web-client
-
+First, download https://www.gutenberg.org/files/2600/2600-0.txt[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.
-
-
- io.smallrye.reactive
- smallrye-mutiny-vertx-web-client
-
-----
+So, you should have the following files in your project:
-In this guide, we are going to use the Mutiny API, so:
-[source, xml, subs=attributes+]
-----
-
- io.smallrye.reactive
- smallrye-mutiny-vertx-web-client
-
+[source, text]
----
-
-Now, create a new resource in your project with the following content:
+.
+├── 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 method to the `VertxResource` class:
[source, java]
-.src/main/java/org/acme/vertx/ResourceUsingWebClient.java
----
-package org.acme.vertx;
-
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-
-import io.smallrye.mutiny.Uni;
-import org.jboss.resteasy.annotations.jaxrs.PathParam;
-
-import io.vertx.mutiny.core.Vertx;
-import io.vertx.mutiny.ext.web.client.WebClient;
-import io.vertx.core.json.JsonObject;
-import io.vertx.ext.web.client.WebClientOptions;
-
-@Path("/fruit-data")
-public class ResourceUsingWebClient {
-
- @Inject
- Vertx vertx;
-
- private WebClient client;
-
- @PostConstruct
- void initialize() {
- this.client = WebClient.create(vertx,
- new WebClientOptions().setDefaultHost("fruityvice.com")
- .setDefaultPort(443).setSsl(true).setTrustAll(true));
- }
-
- @GET
- @Produces(MediaType.APPLICATION_JSON)
- @Path("/{name}")
- public Uni getFruitData(@PathParam("name") String name) {
- return client.get("/api/fruit/" + name)
- .send()
- .onItem().transform(resp -> {
- if (resp.statusCode() == 200) {
- return resp.bodyAsJsonObject();
- } else {
- return new JsonObject()
- .put("code", resp.statusCode())
- .put("message", resp.bodyAsString());
- }
- });
- }
-
+@GET
+@Path("/book")
+public Multi readLargeFile() { // <1>
+ return vertx.fileSystem().open("book.txt", // <2>
+ new OpenOptions().setRead(true)
+ )
+ .onItem().transformToMulti(file -> file.toMulti()) // <3>
+ .onItem().transform(content -> content.toString("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`
+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
-This resource creates a `WebClient` and upon request use this client to invoke the _fruityvice_ API.
-Depending on the result the response is forwarded as it's received, or a new JSON object is created with the status and body.
-The `WebClient` is obviously asynchronous (and non-blocking), to the endpoint returns a `Uni`.
+Then, in a terminal, run:
-Run the application with:
-
-[source,bash]
+[source, bash]
----
-./mvnw compile quarkus:dev
+> curl http://localhost:8080/vertx/book
----
-And then, open a browser to: `http://localhost:8080/fruit-data/pear`. You should get some details about pears.
-
-The application can also run as a native executable.
-But, first, we need to instruct Quarkus to enable _ssl_.
-Open the `src/main/resources/application.properties` and add:
+It should retrieve the book content.
+In the output you should see the separator like:
-[source,properties]
-----
-quarkus.ssl.native=true
+[source, text]
----
+...
+The little princess had also left the tea table and followed Hélène.
-Then, create the native executable with:
-
-[source,bash]
-----
-./mvnw package -Pnative
+“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.”
+...
----
-== Deploying verticles
-
-link:https://vertx.io/docs/vertx-core/java/#\_verticles[Verticles] is "a simple, scalable, actor-like deployment and concurrency model" provided by _Vert.x_.
-This model does not claim to be a strict actor-model implementation, but it does share similarities especially with respect to concurrency, scaling and deployment.
-To use this model, you write and _deploy_ verticles, communicating with each other by sending messages on the event bus.
-
-You can deploy _verticles_ in Quarkus.
-It supports:
-
-* _bare_ verticle - Java classes extending `io.vertx.core.AbstractVerticle`
-* _Mutiny_ verticle - Java classes extending `io.smallrye.mutiny.vertx.core.AbstractVerticle`
-
-To deploy verticles, use the regular Vert.x API:
-
-[source, java]
-----
-@Inject Vertx vertx;
+== Using the event bus
-// ...
-vertx.deployVerticle(MyVerticle.class.getName(), ar -> { });
-vertx.deployVerticle(new MyVerticle(), ar -> { });
-----
+One of the core features of Vert.x is the https://vertx.io/docs/vertx-core/java/#event_bus[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.
-You can also pass deployment options to configure the verticle as well as set the number of instances.
+So, first, let's extend our `VertxResource` class with the following code:
-Verticles are not _beans_ by default.
-However, you can implement them as _ApplicationScoped_ beans and get injection support:
[source, java]
----
-package io.quarkus.vertx.verticles;
-
-import io.smallrye.mutiny.Uni;
-import io.smallrye.mutiny.vertx.core.AbstractVerticle;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
-
-import javax.enterprise.context.ApplicationScoped;
-
-@ApplicationScoped
-public class MyBeanVerticle extends AbstractVerticle {
-
- @ConfigProperty(name = "address") String address;
+@Inject
+EventBus bus; // <1>
- @Override
- public Uni asyncStart() {
- return vertx.eventBus().consumer(address)
- .handler(m -> m.replyAndForget("hello"))
- .completionHandler();
- }
+@GET
+@Path("/hello")
+public Uni hello(@QueryParam("name") String name) { // <2>
+ return bus.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
-You don't have to inject the `vertx` instance but instead leverage the instance stored in the protected field of `AbstractVerticle`.
-
-Then, deploy the verticle instance with:
+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:
[source, java]
----
-package io.quarkus.vertx.verticles;
+package org.acme;
-import io.quarkus.runtime.StartupEvent;
-import io.vertx.mutiny.core.Vertx;
+import io.quarkus.vertx.ConsumeEvent;
import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.event.Observes;
-@ApplicationScoped
-public class VerticleDeployer {
+@ApplicationScoped // <1>
+public class GreetingService {
- public void init(@Observes StartupEvent e, Vertx vertx, MyBeanVerticle verticle) {
- vertx.deployVerticle(verticle).await().indefinitely();
+ @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. TODO LINK REF
+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
-If you want to deploy every exposed `AbstractVerticle`, you can use:
+Let's try this.
+In a terminal, run:
-[source, java]
-----
-public void init(@Observes StartupEvent e, Vertx vertx, Instance verticles) {
- for (AbstractVerticle verticle : verticles) {
- vertx.deployVerticle(verticle).await().indefinitely();
- }
-}
-----
-
-== Listening to a Unix Domain Socket
-
-Listening on a unix domain socket allows us to dispense with the overhead of TCP
-if the connection to the quarkus service is established from the same host. This can happen
-if access to the service goes through a proxy which is often the case
-if you're setting up a service mesh with a proxy like Envoy.
-
-IMPORTANT: This will only work on platforms that support <>.
-
-To setup please enable the appropriate <> and set the following
-environment property:
-
-----
-quarkus.http.domain-socket=/var/run/io.quarkus.app.socket
-quarkus.http.domain-socket-enabled=true
-----
-
-By itself this will not disable the tcp socket which by default will open on
-`0.0.0.0:8080`. It can be explicitly disabled:
+[source, bash]
----
-quarkus.http.host-enabled=false
+> curl "http://localhost:8080/vertx/hello?name=bob"
----
-These properties can be set through Java's `-D` command line parameter or
-on `application.properties`.
-
-== Read only deployment environments
+You should get the expected `Hello bob` message back.
-In environments with read only file systems you may receive errors of the form:
-
-[source]
-----
-java.lang.IllegalStateException: Failed to create cache dir
-----
+== Using Vert.x Clients
-Assuming `/tmp/` is writable this can be fixed by setting the `vertx.cacheDirBase` property to point to a directory in `/tmp/` for instance in OpenShift by creating an environment variable `JAVA_OPTS` with the value `-Dvertx.cacheDirBase=/tmp/vertx`.
+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.
-[[reverse-proxy]]
-== Running behind a reverse proxy
+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.
-Quarkus could be accessed through proxies that additionally generate headers (e.g. `X-Forwarded-Host`) to keep
-information from the client-facing side of the proxy servers that is altered or lost when they are involved.
-In those scenarios, Quarkus can be configured to automatically update information like protocol, host, port and URI
-reflecting the values in these headers.
+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.
-IMPORTANT: Activating this feature makes the server exposed to several security issues (i.e. information spoofing).
-Consider activate it only when running behind a reverse proxy.
+First, 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:
-To setup this feature, please include the following lines in `src/main/resources/application.properties`:
-[source,properties]
-----
-quarkus.http.proxy-address-forwarding=true
+[source, java]
----
+private final Vertx vertx;
+private final WebClient client; // <1>
-To consider only de-facto standard header (`Forwarded` header), please include the following lines in `src/main/resources/application.properties`:
-[source,properties]
-----
-quarkus.http.proxy.allow-forwarded=true
+@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
-To consider only non-standard headers, please include the following lines instead in `src/main/resources/application.properties`:
+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:
-[source,properties]
-----
-quarkus.http.proxy.proxy-address-forwarding=true
-quarkus.http.proxy.enable-forwarded-host=true
-quarkus.http.proxy.enable-forwarded-prefix=true
+[source, java]
----
+private static final String URL = "https://en.wikipedia.org/w/api.php?action=parse&page=Quarkus&format=json&prop=langlinks";
-Both configurations related to standard and non-standard headers can be combined, although the standard headers configuration will have precedence.
-
-Supported forwarding address headers are:
-
-* `Forwarded`
-* `X-Forwarded-Proto`
-* `X-Forwarded-Host`
-* `X-Forwarded-Port`
-* `X-Forwarded-Ssl`
-* `X-Forwarded-Prefix`
-
-[[same-site-cookie]]
-== SameSite cookies
+@GET
+@Path("/web")
+public Uni 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 xref:vertx-reference.adoc#using-vert-x-json[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.
-One can easily add a https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite[SameSite] cookie property to any of the cookies set by a Quarkus endpoint by listing a cookie name and a `SameSite` attribute, for example:
+Then, invoke the endpoint using:
-[source]
+[source, bash]
----
-quarkus.http.same-site-cookie.jwt.value=Lax
-quarkus.http.same-site-cookie.session.value=Strict
+> 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"}]
----
-Given this configuration, the `jwt` cookie will have a `SameSite=Lax` attribute and the `session` cookie will have a `SameSite=Strict` attribute.
+The response indicates that, in addition to the English page, there are a German and a French page about Quarkus on wikipedia.
== Going further
-There are many other facets of Quarkus using Vert.x underneath:
+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 xref:vertx-reference.adoc[reference guide about Vert.x in Quarkus].
-* The event bus is the connecting tissue of Vert.x applications.
+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 link:reactive-event-bus[event bus documentation].
-* Data streaming and Apache Kafka are an important part of modern systems.
-Quarkus integrates data streaming using Reactive Messaging.
-More details on link:kafka[Interacting with Kafka].
-
-* Learn how to implement highly performant, low-overhead database applications on Quarkus with the link:reactive-sql-clients[Reactive SQL Clients].
+Learn how to implement highly performant, low-overhead database applications on Quarkus with the link:reactive-sql-clients[Reactive SQL Clients].