diff --git a/docs/src/main/asciidoc/http-reference.adoc b/docs/src/main/asciidoc/http-reference.adoc index 479bc0dd9f0a0..824795fd67dca 100644 --- a/docs/src/main/asciidoc/http-reference.adoc +++ b/docs/src/main/asciidoc/http-reference.adoc @@ -263,6 +263,63 @@ include::{generated-dir}/config/quarkus-vertx-http-config-group-access-log-confi |Response header | | `%{o,response_header_name}` |=== + +[[reverse-proxy]] +== Running behind a reverse proxy + +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. + +IMPORTANT: Activating this feature leaves the server exposed to several security issues (i.e. information spoofing). +Consider activate it only when running behind a reverse proxy. + +To setup this feature, please include the following lines in `src/main/resources/application.properties`: +[source,properties] +---- +quarkus.http.proxy-address-forwarding=true +---- + +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 +---- + +To consider only non-standard headers, please include the following lines instead in `src/main/resources/application.properties`: + +[source,properties] +---- +quarkus.http.proxy.proxy-address-forwarding=true +quarkus.http.proxy.enable-forwarded-host=true +quarkus.http.proxy.enable-forwarded-prefix=true +---- + +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 + +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: + +[source] +---- +quarkus.http.same-site-cookie.jwt.value=Lax +quarkus.http.same-site-cookie.session.value=Strict +---- + +Given this configuration, the `jwt` cookie will have a `SameSite=Lax` attribute and the `session` cookie will have a `SameSite=Strict` attribute. + == Servlet Config To use Servlet you need to explicitly include `quarkus-undertow`: diff --git a/docs/src/main/asciidoc/images/quarkus-vertx-guide-architecture.png b/docs/src/main/asciidoc/images/quarkus-vertx-guide-architecture.png new file mode 100644 index 0000000000000..b752e089161c1 Binary files /dev/null and b/docs/src/main/asciidoc/images/quarkus-vertx-guide-architecture.png differ diff --git a/docs/src/main/asciidoc/reactive-sql-clients.adoc b/docs/src/main/asciidoc/reactive-sql-clients.adoc index b47f753935573..44c6eae060c64 100644 --- a/docs/src/main/asciidoc/reactive-sql-clients.adoc +++ b/docs/src/main/asciidoc/reactive-sql-clients.adoc @@ -642,7 +642,7 @@ MySQLPool additional2Client; The PostgreSQL and MariaDB/MySQL clients can be configured to connect to the server through a UNIX domain socket. -First make sure that link:vertx#native-transport[native transport support] is enabled. +First make sure that link:vertx-reference.adoc#native-transport[native transport support] is enabled. Then configure the database connection url. This step depends on the database type. diff --git a/docs/src/main/asciidoc/rest-json.adoc b/docs/src/main/asciidoc/rest-json.adoc index 6b81fa4ea598f..21c723a9a5cd8 100644 --- a/docs/src/main/asciidoc/rest-json.adoc +++ b/docs/src/main/asciidoc/rest-json.adoc @@ -182,6 +182,7 @@ If you don't rely on the JSON default, it is heavily recommended to annotate you It will allow to narrow down the number of JAX-RS providers (which can be seen as converters) included in the native executable. ==== +[[json]] === Configuring JSON support ==== Jackson diff --git a/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc b/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc index b242e3be82ff3..a5acf51bc65f1 100644 --- a/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc @@ -864,7 +864,7 @@ quarkus.log.category."io.quarkus.oidc.runtime.OidcRecorder".min-level=TRACE OIDC authentication mechanism can be affected if your Quarkus application is running behind a reverse proxy/gateway/firewall when HTTP `Host` header may be reset to the internal IP address, HTTPS connection may be terminated, etc. For example, an authorization code flow `redirect_uri` parameter may be set to the internal host instead of the expected external one. -In such cases configuring Quarkus to recognize the original headers forwarded by the proxy will be required, see link:vertx#reverse-proxy[Running behind a reverse proxy] Vert.x documentation section for more information. +In such cases configuring Quarkus to recognize the original headers forwarded by the proxy will be required, see link:http-reference#reverse-proxy[Running behind a reverse proxy] Vert.x documentation section for more information. For example, if your Quarkus endpoint runs in a cluster behind Kubernetes Ingress then a redirect from the OpenId Connect Provider back to this endpoint may not work since the calcuated `redirect_uri` parameter may point to the internal endpoint address. This problem can be resolved with the following configuration: diff --git a/docs/src/main/asciidoc/security.adoc b/docs/src/main/asciidoc/security.adoc index 9ac7eae3d1d1c..36405f56ef2f0 100644 --- a/docs/src/main/asciidoc/security.adoc +++ b/docs/src/main/asciidoc/security.adoc @@ -251,7 +251,7 @@ If you plan to make your Quarkus application accessible to another application r == SameSite cookies -Please see link:vertx#same-site-cookie[SameSite cookies] for information about adding 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. +Please see link:http-reference.adoc#same-site-cookie[SameSite cookies] for information about adding 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. == Testing diff --git a/docs/src/main/asciidoc/vertx-reference.adoc b/docs/src/main/asciidoc/vertx-reference.adoc new file mode 100644 index 0000000000000..db2e2b6fb4977 --- /dev/null +++ b/docs/src/main/asciidoc/vertx-reference.adoc @@ -0,0 +1,939 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// += Vert.x Reference Guide + +include::./attributes.adoc[] + +https://vertx.io[Vert.x] is a toolkit for building reactive applications. +As described in the xref:quarkus-reactive-architecture.adoc[Quarkus Reactive Architecture], Quarkus uses Vert.x underneath. + +This guide is the companion to the xref:vertx.adoc[Using Eclipse Vert.x API from a Quarkus Application] guide. +It provides more advanced details about the usage and the configuration of the Vert.x instance used by Quarkus. + + +[#vertx-access] +== Accessing the Vert.x instance + +To access the managed Vert.x instance, add the `quarkus-vertx` extension to your project. +Note that this dependency may already be installed (as a transitive dependency). + +With this extension, you can retrieve the managed instance of Vert.x using either field or constructor injection: + +[source, java] +---- +@ApplicationScoped +public class MyBean { +// Field injection +@Inject Vertx vertx; + +// Constructor injection +MyBean(Vertx vertx) { + // ... +} + +} +---- + +You can inject either the: + +* `io.vertx.core.Vertx` instance exposing the _bare_ Vert.x API +* `io.vertx.mutiny.core.Vertx` instance exposing the _Mutiny_ API + +We recommend using the Mutiny variant as it integrates with the other reactive APIs provided by Quarkus. + +[TIP] +.Mutiny +==== +If you are not familiar with Mutiny, check xref:mutiny-primer.adoc[Mutiny - an intuitive reactive programming library]. +==== + +Documentation about the Vert.x Mutiny variant is available on https://smallrye.io/smallrye-mutiny-vertx-bindings. + +[[vertx-config]] +== Configuring the Vert.x instance + +You can configure the Vert.x instance from the `application.properties` file. +The following table lists the supported properties: + +include::{generated-dir}/config/quarkus-vertx-core.adoc[opts=optional, leveloffset=+1] + + +[[using-vertx-clients]] +== Using Vert.x clients + +In addition to Vert.x core, you can use most Vert.x ecosystem libraries. +Some Quarkus extension already wraps Vert.x libraries. + +=== Available APIs + +The following table lists the most used libraries from the Vert.x ecosystem. +To access these APIs, add the indicated extension or dependency to your project. +Refer to the associated documentation to learn how to use them. + +[cols="1,1,1",stripes=even,options=headers] +|=== +|API +|Extension or Dependency +|Documentation + +|AMQP Client +|`io.quarkus:quarkus-smallrye-reactive-messaging-amqp` (extension) +|https://quarkus.io/guides/amqp + +|Circuit Breaker +|`io.smallrye.reactive:smallrye-mutiny-vertx-circuit-breaker` (external dependency) +|https://vertx.io/docs/vertx-circuit-breaker/java/ + +|Consul Client +|`io.smallrye.reactive:smallrye-mutiny-vertx-consul-client` (external dependency) +|https://vertx.io/docs/vertx-consul-client/java/ + +|DB2 Client +|`io.quarkus:quarkus-reactive-db2-client` (extension) +|https://quarkus.io/guides/reactive-sql-clients + +|Kafka Client +|`io.quarkus:quarkus-smallrye-reactive-messaging-kafka` (extension) +|https://quarkus.io/guides/kafka + +|Mail Client +|`io.quarkus:quarkus-mailer` (extension) +|https://quarkus.io/guides/mailer + +|MQTT Client +|`io.quarkus:quarkus-smallrye-reactive-messaging-mqtt` (extension) +|https://quarkus.io/guides/mqtt + +|MS SQL Client +|`io.quarkus:quarkus-reactive-mssql-client` (extension) +|https://quarkus.io/guides/reactive-sql-clients + +|MySQL Client +|`io.quarkus:quarkus-reactive-mysql-client` (extension) +|https://quarkus.io/guides/reactive-sql-clients + +|PostgreSQL Client +|`io.quarkus:quarkus-reactive-pg-client` (extension) +|https://quarkus.io/guides/reactive-sql-clients + +|RabbitMQ Client +|`io.smallrye.reactive:smallrye-mutiny-vertx-rabbitmq-client` (external dependency) +|https://vertx.io/docs/vertx-rabbitmq-client/java + +|Redis Client +|`io.quarkus:quarkus-redis-client` (extension) +|https://quarkus.io/guides/redis + +|Web Client +|`io.smallrye.reactive:smallrye-mutiny-vertx-web-client` (external dependency) +|https://vertx.io/docs/vertx-web-client/java/ + +|=== + +To learn more about the usage of the Vert.x Mutiny API, refer to https://smallrye.io/smallrye-mutiny-vertx-bindings. + +=== Example of usage + +This section gives an example using the Vert.x `WebClient`. +As indicated in the table above, add the following dependency to your project: + +[source, xml, subs=attributes+] +---- + + io.smallrye.reactive + smallrye-mutiny-vertx-web-client + +---- + +Now, in your code, you can create an instance of `WebClient`: + +[source, 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 { + + private final WebClient client; + + @Inject + VertxResource(Vertx vertx) { + this.client = WebClient.create(vertx); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{name}") + public Uni getFruitData(@PathParam("name") String name) { + return client.getAbs("https://.../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()); + } + }); + } + +} + +---- + +This resource creates a `WebClient` and, upon request, uses this client to invoke a remote HTTP API. +Depending on the result, the response is forwarded as received, or it creates a JSON object wrapping the error. +The `WebClient` is asynchronous (and non-blocking), to the endpoint returns a `Uni`. + +The application can also run as a native executable. +But, first, we need to instruct Quarkus to enable _ssl_ (if the remote API uses HTTPS). +Open the `src/main/resources/application.properties` and add: + +[source,properties] +---- +quarkus.ssl.native=true +---- + +Then, create the native executable with: + +[source,bash] +---- +./mvnw package -Pnative +---- + +[#using-vert-x-json] +== Using Vert.x JSON + +Vert.x APIs often rely on JSON. +Vert.x provides two convenient classes to manipulate JSON document: `io.vertx.core.json.JsonObject` and `io.vertx.core.json.JsonArray`. + +`JsonObject` can be used to map an object into its JSON representation and build an object from a JSON document: + +[source, java] +---- +// Map an object into JSON +Person person = ...; +JsonObject json = JsonObject.mapFrom(person); + +// Build an object from JSON +json = new JsonObject(); +person = json.mapTo(Person.class); +---- + +Note that these features use the mapper managed by the `quarkus-jackson` extension. +Refer to xref:rest-json.adoc#json[Jackson configuration] to customize the mapping. + + +JSON Object and JSON Array are both supported as Quarkus HTTP endpoint requests and response bodies (using classic RESTEasy and RESTEasy Reactive). +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); + } +} +---- + +http://localhost:8080/hello/Quarkus/object returns: + +[source, text] +---- +{"Hello":"Quarkus"} +---- + +http://localhost:8080/hello/Quarkus/array returns: + +[source, text] +---- +["Hello","Quarkus"] +---- + +This works equally well when the JSON content is a request body or is wrapped in a `Uni`, `Multi`, `CompletionStage` or `Publisher`. + +== 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 shares similarities, especially concerning 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; + +// ... +vertx.deployVerticle(MyVerticle.class.getName(), ar -> { }); +vertx.deployVerticle(new MyVerticle(), ar -> { }); +---- + +NOTE: An example explaining how to deploy verticles during the initialization of the application will follow. + +You can also pass deployment options to configure the verticle as well as set the number of instances. + +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; + + @Override + public Uni asyncStart() { + return vertx.eventBus().consumer(address) + .handler(m -> m.replyAndForget("hello")) + .completionHandler(); + } +} +---- + +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 instances with: + +[source, java] +---- +package io.quarkus.vertx.verticles; + +import io.quarkus.runtime.StartupEvent; +import io.vertx.mutiny.core.Vertx; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; + +@ApplicationScoped +public class VerticleDeployer { + + public void init(@Observes StartupEvent e, Vertx vertx, MyBeanVerticle verticle) { + vertx.deployVerticle(verticle).await().indefinitely(); + } +} +---- + +If you want to deploy every exposed `AbstractVerticle`, you can use: + +[source,java] +---- +public void init(@Observes StartupEvent e, Vertx vertx, Instance verticles) { + for (AbstractVerticle verticle : verticles) { + vertx.deployVerticle(verticle).await().indefinitely(); + } +} +---- + +[#eventbus] +== Using the event bus + +Vert.x comes with a built-in https://vertx.io/docs/vertx-core/java/#event_bus[event bus] that you can use from your Quarkus application. +So, your application components (CDI beans, resources...) can interact using asynchronous events, thus promoting loose-coupling. + +With the event bus, you send _messages_ to _virtual addresses_. +The event bus offers three types of delivery mechanisms: + +- point-to-point - send the message, one consumer receives it. If several consumers listen to the address, a round-robin is applied; +- publish/subscribe - publish a message; all the consumers listening to the address are receiving the message; +- request/reply - send the message and expect a response. The receiver can respond to the message in an asynchronous fashion. + +All these delivery mechanisms are non-blocking and are providing one of the fundamental bricks to build reactive applications. + +=== Consuming events + +While you can use the Vert.x API to register consumers, Quarkus comes with declarative support. +To consume events, use the `io.quarkus.vertx.ConsumeEvent` annotation: + +[source, java] +---- +package org.acme.vertx; + +import io.quarkus.vertx.ConsumeEvent; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class GreetingService { + + @ConsumeEvent // <1> + public String consume(String name) { // <2> + return name.toUpperCase(); + } +} +---- +<1> If not set, the address is the fully qualified name of the bean; for instance, in this snippet, it's `org.acme.vertx.GreetingService`. +<2> The method parameter is the message body. If the method returns _something_, it's the message response. + +=== Configuring the address + +The `@ConsumeEvent` annotation can be configured to set the address: + +[source, java] +---- +@ConsumeEvent("greeting") // <1> +public String consume(String name) { + return name.toUpperCase(); +} +---- +<1> Receive the messages sent to the `greeting` address + +=== Asynchronous processing + +The previous examples use synchronous processing. +Asynchronous processing is also possible by returning either an `io.smallrye.mutiny.Uni` or a `java.util.concurrent.CompletionStage`: + +[source,java] +---- +package org.acme.vertx; + +import io.quarkus.vertx.ConsumeEvent; + +import javax.enterprise.context.ApplicationScoped; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import io.smallrye.mutiny.Uni; + +@ApplicationScoped +public class GreetingService { + + @ConsumeEvent + public CompletionStage consume(String name) { + // return a CompletionStage completed when the processing is finished. + // You can also fail the CompletionStage explicitly + } + + @ConsumeEvent + public Uni process(String name) { + // return an Uni completed when the processing is finished. + // You can also fail the Uni explicitly + } +} +---- + +[TIP] +.Mutiny +==== +The previous example uses Mutiny reactive types. +If you are not familiar with Mutiny, check xref:mutiny-primer.adoc[Mutiny - an intuitive reactive programming library]. +==== + +=== Blocking processing + +By default, the code consuming the event must be _non-blocking_, as it's called on an I/O thread. +If your processing is blocking, use the `@io.smallrye.common.annotation.Blocking` annotation: + +[source, java] +---- +@ConsumeEvent(value = "blocking-consumer") +@Blocking +void consumeBlocking(String message) { + // Something blocking +} +---- + +Alternatively, you can use the `blocking` attribute from the `@ConsumeEvent` annotation: + +[source, java] +---- +@ConsumeEvent(value = "blocking-consumer", blocking = true) +void consumeBlocking(String message) { + // Something blocking +} +---- + +When using `@Blocking`, it ignores the value of the `blocking` attribute of `@ConsumeEvent`. + +=== Replying to messages + +The _return_ value of a method annotated with `@ConsumeEvent` is used to respond to the incoming message. +For instance, in the following snippet, the returned `String` is the response. + +[source, java] +---- +@ConsumeEvent("greeting") +public String consume(String name) { + return name.toUpperCase(); +} +---- + +You can also return a `Uni` or a `CompletionStage` to handle asynchronous reply: + +[source, java] +---- +@ConsumeEvent("greeting") +public Uni consume2(String name) { + return Uni.createFrom().item(() -> name.toUpperCase()).emitOn(executor); +} +---- + +[NOTE] +==== +You can inject an `executor` if you use the Context Propagation extension: +[source, code] +---- +@Inject Executor executor; +---- +==== + +=== Implementing fire and forget interactions + +You don't have to reply to received messages. +Typically, for a _fire and forget_ interaction, the messages are consumed, and the sender does not need to know about it. +To implement this pattern, your consumer method returns `void`. + +[source,java] +---- +@ConsumeEvent("greeting") +public void consume(String event) { + // Do something with the event +} +---- + +=== Dealing with messages + +Unlike the previous example using the _payloads_ directly, you can also use `Message` directly: + +[source, java] +---- +@ConsumeEvent("greeting") +public void consume(Message msg) { + System.out.println(msg.address()); + System.out.println(msg.body()); +} +---- + +=== Handling Failures + +If a method annotated with `@ConsumeEvent` throws an exception, then: + +* if a reply handler is set, then the failure is propagated back to the sender via an `io.vertx.core.eventbus.ReplyException` with code `ConsumeEvent#FAILURE_CODE` and the exception message, +* if no reply handler is set, then the exception is rethrown (and wrapped in a `RuntimeException` if necessary) and can be handled by the default exception handler, i.e. `io.vertx.core.Vertx#exceptionHandler()`. + +=== Sending messages + +Sending and publishing messages use the Vert.x event bus: + +[source, java] +---- +package org.acme.vertx; + +import io.smallrye.mutiny.Uni; +import io.vertx.mutiny.core.eventbus.EventBus; +import io.vertx.mutiny.core.eventbus.Message; +import org.jboss.resteasy.annotations.jaxrs.PathParam; + +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; + +@Path("/async") +public class EventResource { + + @Inject + EventBus bus; // <1> + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("{name}") + public Uni greeting(@PathParam String name) { + return bus.request("greeting", name) // <2> + .onItem().transform(Message::body); + } +} +---- +<1> Inject the Event bus +<2> Send a message to the address `greeting`. Message payload is `name` + +The `EventBus` object provides methods to: + +1. `send` a message to a specific address - one single consumer receives the message. +2. `publish` a message to a specific address - all consumers receive the messages. +3. `request` a message and expect a reply + +[source, java] +---- +// Case 1 +bus.sendAndForget("greeting", name) +// Case 2 +bus.publish("greeting", name) +// Case 3 +Uni response = bus.request("address", "hello, how are you?") + .onItem().transform(Message::body); +---- + +=== Using codecs + +The https://vertx.io/docs/vertx-core/java/#event_bus[Vert.x Event Bus] uses codecs to _serialize_ and _deserialize_ objects. +Quarkus provides a default codec for local delivery. +So you can exchange objects as follows: + +[source, java] +---- +@GET +@Produces(MediaType.TEXT_PLAIN) +@Path("{name}") +public Uni greeting(@PathParam String name) { + return bus.request("greeting", new MyName(name)) + .onItem().transform(Message::body); +} + +@ConsumeEvent(value = "greeting") +Uni greeting(MyName name) { + return Uni.createFrom().item(() -> "Hello " + name.getName()); +} +---- + +If you want to use a specific codec, you need to set it on both ends explicitly: + +[source, java] +---- +@GET +@Produces(MediaType.TEXT_PLAIN) +@Path("{name}") +public Uni greeting(@PathParam String name) { + return bus.request("greeting", name, + new DeliveryOptions().setCodecName(MyNameCodec.class.getName())) // <1> + .onItem().transform(Message::body); +} + +@ConsumeEvent(value = "greeting", codec = MyNameCodec.class) // <2> +Uni greeting(MyName name) { + return Uni.createFrom().item(() -> "Hello "+name.getName()); +} +---- +1. Set the name of the codec to use to send the message +2. Set the codec to use to receive the message + +=== Combining HTTP and the event bus + +Let's revisit a greeting HTTP endpoint and use asynchronous message passing to delegate the call to a separated bean. +It uses the request/reply dispatching mechanism. +Instead of implementing the business logic inside the JAX-RS endpoint, we are sending a message. +Another bean consumes this message, and the response is sent using the _reply_ mechanism. + +In your HTTP endpoint class, inject the event bus and uses the `request` method to send a message to the event bus and expect a response: + +[source,java] +---- +package org.acme.vertx; + +import io.smallrye.mutiny.Uni; +import io.vertx.mutiny.core.eventbus.EventBus; +import io.vertx.mutiny.core.eventbus.Message; +import org.jboss.resteasy.annotations.jaxrs.PathParam; + +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; + +@Path("/bus") +public class EventResource { + + @Inject + EventBus bus; + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("{name}") + public Uni greeting(@PathParam String name) { + return bus.request("greeting", name) // <1> + .onItem().transform(Message::body); // <2> + } +} +---- +<1> send the `name` to the `greeting` address and request a response +<2> when we get the response, extract the body and send it to the user + +NOTE: the HTTP method returns a `Uni`. +If you are using RESTEasy Reactive, `Uni` support is built-in. +If you are using _classic_ RESTEasy, you need to add the `quarkus resteasy-mutiny` extension to your project. + +We need a consumer listening on the `greeting` address. +This consumer can be in the same class or another bean such as: + +[source, java] +---- +package org.acme.vertx; + +import io.quarkus.vertx.ConsumeEvent; + +import javax.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class GreetingService { + + @ConsumeEvent("greeting") + public String greeting(String name) { + return "Hello " + name; + } + +} +---- + +This bean receives the name and returns the greeting message. + +With this in place, every HTTP request on `/bus/quarkus` sends a message to the event bus, waits for a reply, and when this one arrives, writes the HTTP response: + +[source,text] +---- +Hello Quarkus +---- + +To better understand, let's detail how the HTTP request/response has been handled: + +1. The request is received by the `greeting` method +2. a message containing the _name_ is sent to the event bus +3. Another bean receives this message and computes the response +4. This response is sent back using the reply mechanism +5. Once the reply is received by the sender, the content is written to the HTTP response + + +=== Bi-directional communication with browsers using SockJS + +The SockJS bridge provided by Vert.x allows browser applications and Quarkus applications to communicate using the event bus. +It connects both sides. +So, both sides can send messages received on the other side. +It supports the three delivery mechanisms. + +SockJS negotiates the communication channel between the Quarkus application and the browser. +If WebSockets are supported, it uses them; otherwise, it degrades to SSE, long polling, etc. + +So use SockJS, you need to configure the bridge, especially the addresses that will be used to communicate: + +[source, java] +---- +package org.acme.vertx; + +import io.vertx.core.Vertx; +import io.vertx.ext.bridge.PermittedOptions; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.sockjs.SockJSBridgeOptions; +import io.vertx.ext.web.handler.sockjs.SockJSHandler; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.inject.Inject; +import java.util.concurrent.atomic.AtomicInteger; + +@ApplicationScoped +public class SockJsExample { + + @Inject + Vertx vertx; + + public void init(@Observes Router router) { + SockJSHandler sockJSHandler = SockJSHandler.create(vertx); + sockJSHandler.bridge(new SockJSBridgeOptions() + .addOutboundPermitted(new PermittedOptions().setAddress("ticks"))); + router.route("/eventbus/*").handler(sockJSHandler); + } + +} +---- + +This code configures the SockJS bridge to send all the messages targeting the `ticks` address to the connected browsers. +More detailled explanations about the configuration can be found on https://vertx.io/docs/vertx-web/java/#_sockjs_event_bus_bridge[the Vert.x SockJS Bridge documentation]. + +The browser must use the `vertx-eventbus` JavaScript library to consume the message: + +[source, html] +---- + + + + + SockJS example - Quarkus + + + + + + +

SockJS Examples

+ +

Last Tick:

+ + + + +---- + +[#native-transport] +== Native Transport + +IMPORTANT: Native transports are not supported in GraalVM produced binaries. + +Vert.x is capable of using https://netty.io/wiki/native-transports.html[Netty's native transports], which offers +performance improvements on specific platforms.To enable them, you must include the appropriate dependency for your +platform. It's usually a good idea to have both to keep your application platform agnostic. Netty is smart enough +to use the correct one, that includes none at all on unsupported platforms: + +[source,xml] +---- + + io.netty + netty-transport-native-epoll + linux-x86_64 + + + + io.netty + netty-transport-native-kqueue + osx-x86_64 + +---- + +You will also have to explicitly configure Vert.x to use the native transport. +In `application.properties` add: + +[source,properties] +---- +quarkus.vertx.prefer-native-transport=true +---- + +Or in `application.yml`: + +[source,yml] +---- +quarkus: + vertx: + prefer-native-transport: true +---- + +If all is well quarkus will log: + +---- +[io.qua.ver.cor.run.VertxCoreRecorder] (main) Vertx has Native Transport Enabled: true +---- + +=== Native Linux Transport + +On Linux you can enable the following socket options: + +* SO_REUSEPORT +---- +quarkus.http.so-reuse-port=true +---- +* TCP_QUICKACK +---- +quarkus.http.tcp-quick-ack=true +---- +* TCP_CORK +---- +quarkus.http.tcp-cork=true +---- +* TCP_FASTOPEN +---- +quarkus.http.tcp-fast-open=true +---- + +=== Native MacOS Transport + +On MacOS Sierra and above you can enable the following socket options: + +* SO_REUSEPORT +---- +quarkus.http.so-reuse-port=true +---- + + +== 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 <>. + +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: + +---- +quarkus.http.host-enabled=false +---- + +These properties can be set through Java's `-D` command line parameter or +on `application.properties`. + +== Read only deployment environments + +In environments with read only file systems you may receive errors of the form: + +[source] +---- +java.lang.IllegalStateException: Failed to create cache dir +---- + +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`. \ No newline at end of file diff --git a/docs/src/main/asciidoc/vertx.adoc b/docs/src/main/asciidoc/vertx.adoc index a02e53981f121..b7d96666495a1 100644 --- a/docs/src/main/asciidoc/vertx.adoc +++ b/docs/src/main/asciidoc/vertx.adoc @@ -3,743 +3,389 @@ This guide is maintained in the main Quarkus repository and pull requests should be submitted there: https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// -= Quarkus - Using Eclipse Vert.x += Using Eclipse Vert.x API from a Quarkus Application include::./attributes.adoc[] -Eclipse https://vertx.io[Vert.x] is a toolkit for building reactive applications. -It is designed to be lightweight and embeddable. -Vert.x defines a reactive execution model and provides a large ecosystem. +https://vertx.io[Vert.x] is a toolkit for building reactive applications. +As described in the xref:quarkus-reactive-architecture.adoc[Quarkus Reactive Architecture], Quarkus uses Vert.x underneath. -Quarkus is based on Vert.x, and almost all network-related features rely on Vert.x. -While lots of reactive features from Quarkus don't _show_ Vert.x, it's used underneath. -Quarkus also integrates smoothly with the Vert.x event bus (to enable asynchronous messaging passing between application components) and some reactive clients. -You can also use various Vert.x APIs in your Quarkus application, such as deploying _verticles_, instantiating clients... +image::quarkus-reactive-core.png[Quarkus Reactive Core,width=50%, align=center] -== Solution - -We recommend that you follow the instructions in the next sections and create the application step by step. -However, you can go right to the completed example. - -Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. - -The solution is located in the `vertx-quickstart` {quickstarts-tree-url}/vertx-quickstart[directory]. +Quarkus applications can access and uses the Vert.x APIs. -== Installing - -To access Vert.x, well, you need to enable the `vertx` extension to use this feature. -If you are creating a new project, set the `extensions` parameter are follows: - -[source,bash, subs=attributes+] ----- -mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \ - -DprojectGroupId=org.acme \ - -DprojectArtifactId=vertx-quickstart \ - -DclassName="org.acme.vertx.GreetingResource" \ - -Dpath="/hello" \ - -Dextensions="resteasy,vertx" -cd vertx-quickstart ----- - -If you have an already created project, the `vertx` extension can be added to an existing Quarkus project with -the `add-extension` command: - -[source,bash] ----- -./mvnw quarkus:add-extension -Dextensions="vertx" ----- +This guide presents how you can build a Quarkus application using: -Otherwise, you can manually add this to the dependencies section of your `pom.xml` file: +* the managed instance of Vert.x +* the Vert.x event bus +* the Vert.x Web Client -[source,xml] ----- - - io.quarkus - quarkus-vertx - ----- +It's an introductory guide. +The xref:vertx-reference.adoc[Vert.x reference guide] covers more advanced features such as verticles, and native transports. -== Native Transport +== Architecture -Vert.x is capable of using https://netty.io/wiki/native-transports.html[Netty's native transports] which offers -performance improvements on certain platforms. To enable them you must include the appropriate dependency for your -platform. It's usually a good idea to include both to keep your application platform agnostic. Netty is smart enough -to use the correct one, that includes none at all on unsupported platforms: +We are going to build a simple application exposing four HTTP endpoints: -[source,xml] ----- - - io.netty - netty-transport-native-epoll - linux-x86_64 - +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 - - io.netty - netty-transport-native-kqueue - osx-x86_64 - ----- +image::quarkus-vertx-guide-architecture.png[Architecture of the Vert.x guide,width=50%, align=center] -You will also have to explicitly configure Vert.x to use the native transport. In `application.properties` add: +== Solution -[source,properties] ----- -quarkus.vertx.prefer-native-transport=true ----- +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. -Or in `application.yml`: +Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. -[source,yml] ----- -quarkus: - vertx: - prefer-native-transport: true ----- +The solution is located in the `vertx-quickstart` {quickstarts-tree-url}/vertx-quickstart[directory]. -If all is well quarkus will log: +[TIP] +.Mutiny +==== +This guide uses the Mutiny API. +If you are not familiar with Mutiny, check xref:mutiny-primer.adoc[Mutiny - an intuitive, reactive programming library]. +==== ----- -[io.qua.ver.cor.run.VertxCoreRecorder] (main) Vertx has Native Transport Enabled: true ----- -=== Native Linux Transport +== Bootstrapping the application -On Linux you can enable the following socket options: +Click on https://code.quarkus.io/?a=quarkus-getting-started-vertx&nc=true&e=resteasy-reactive-jackson&e=vertx[this link] to configure your application. +It selected a few extensions: -* SO_REUSEPORT ----- -quarkus.http.so-reuse-port=true ----- -* TCP_QUICKACK ----- -quarkus.http.tcp-quick-ack=true ----- -* TCP_CORK ----- -quarkus.http.tcp-cork=true ----- -* TCP_FASTOPEN ----- -quarkus.http.tcp-fast-open=true ----- +* `resteasy-reactive-jackson`, which also brings `resteasy reactive`. We are going to use it to expose our HTTP endpoints. +* `vertx`, which provides access to the underlying managed Vert.x -=== Native MacOS Transport +Click on the `Generate your application` button, download the zip file and unzip it. +Then, open the project in your favorite IDE. -On MacOS Sierra and above you can enable the following socket options: +If you open the generated `pom.xml` file, you can see the selected extensions: -* SO_REUSEPORT +[source, xml] ---- -quarkus.http.so-reuse-port=true + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-vertx + ---- -== Accessing Vert.x +While you are in the `pom.xml`, add the following dependency: -Once the extension has been added, you can access the _managed_ Vert.x instance using `@Inject`: - -[source, java] +[source, xml] ---- -@Inject Vertx vertx; + + io.smallrye.reactive + smallrye-mutiny-vertx-web-client + ---- -If you are familiar with Vert.x, you know that Vert.x provides different API models. -The _bare_ Vert.x uses callbacks, while the Mutiny variant uses `Uni` and `Multi`. - -Quarkus provides 2 Vert.x APIs: - -[options="header"] -|=== - -| Name | Code | Description - -| _bare_ | `@Inject io.vertx.core.Vertx vertx` | _bare_ Vert.x instance, the API uses callbacks. - -| https://smallrye.io/smallrye-mutiny/[Mutiny] | `@Inject io.vertx.mutiny.core.Vertx vertx` | The Mutiny API for Vert.x. - -|=== +This dependency provides the Vert.x Web Client, which we will be using to implement the `/web` endpoint. -TIP: You may inject any of the 2 flavors of `Vertx` as well as the `EventBus` in your Quarkus application beans: `bare` and `Mutiny`. -They are just shims and rely on a single _managed_ Vert.x instance. -You will pick one or the other depending on your use cases. +== Accessing the managed Vert.x instance -- `bare`: for advanced usage or if you have existing Vert.x code you want to reuse in your Quarkus application -- `mutiny`: Mutiny is an event-driven reactive programming API. It uses 2 types: `Uni` and `Multi`. This is the recommended API. +Create the `src/main/java/org/acme/VertxResource.java` file. +It will contain our HTTP endpoints. -The following snippets illustrate the difference between these 4 APIs: +In this file, copy the following code: [source, java] ---- -// Bare Vert.x: -vertx.fileSystem().readFile("lorem-ipsum.txt", ar -> { - if (ar.succeeded()) { - System.out.println("Content:" + ar.result().toString("UTF-8")); - } else { - System.out.println("Cannot read the file: " + ar.cause().getMessage()); - } -}); - -// Mutiny Vert.x: -vertx.fileSystem().readFile("lorem-ipsum.txt") - .onItem().transform(buffer -> buffer.toString("UTF-8")) - .subscribe() - .with( - content -> System.out.println("Content: " + content), - err -> System.out.println("Cannot read the file: " + err.getMessage()) - ); ----- - -[TIP] -.Mutiny -==== -If you are not familiar with Mutiny, check xref:mutiny-primer.adoc[Mutiny - an intuitive reactive programming library]. -==== - -== Using Vert.x in Reactive JAX-RS resources - -Quarkus web resources support asynchronous processing and streaming results over https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events[server-sent events]. - -=== Asynchronous processing - -To asynchronously handle the HTTP request, the endpoint method must return a `java.util.concurrent.CompletionStage` or an `io.smallrye.mutiny.Uni` (requires the `quarkus-resteasy-mutiny` extension): - -[source,java] ----- -@Path("/lorem") -public class GreetingResource { - - @GET - @Produces(MediaType.TEXT_PLAIN) - public Uni doSomethingAsync() { - // Mimic an asynchronous computation. - return Uni.createFrom() - .item(() -> "Hello!") - .onItem().delayIt().by(Duration.ofMillis(10)); - } -} ----- +package org.acme; - -[source,bash] ----- -./mvnw compile quarkus:dev ----- - -Then, open your browser to http://localhost:8080/lorem and you should get the message. - -So far so good. -Now let's use the Vert.x API instead of this artificial delay: - -[source,java] ----- -package org.acme.vertx; - -import io.smallrye.mutiny.Uni; import io.vertx.mutiny.core.Vertx; 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; -@Path("/lorem") -public class GreetingResource { +@Path("/vertx") // <1> +public class VertxResource { - @Inject - Vertx vertx; + private final Vertx vertx; - @GET - @Produces(MediaType.TEXT_PLAIN) - public Uni doSomethingAsync() { - return vertx.fileSystem().readFile("/META-INF/resources/lorem.txt") - .onItem().transform(b -> b.toString("UTF-8")); + @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. -In this code, we inject the `vertx` instance (`io.vertx.mutiny.core.Vertx`) and read a file from the file system. - -Create the `src/main/resources/META_INF/resources/lorem.txt` file with the following content: - -[source,text] ----- -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ----- - -Then, refresh the page, you should see the _lorem ipsum_ text. +With this, we can start implementing the endpoints. -=== Streaming using Server-Sent Events +== Using Vert.x Core API -Quarkus web resources that need to send content as https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events[server-sent events] must have a method: +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. -* declaring the `text/event-stream` response content type -* returning a https://www.reactive-streams.org/[Reactive Streams] `Publisher` or Mutiny `Multi` (requires the `quarkus-resteasy-mutiny` extension) -In practice, a streaming greeting service would look like: +In the `src/main/resource` directory, create a `lorem.txt` file with the following content: -[source,java] +[source, text] ---- -@Path("/hello") -public class StreamingResource { - - @GET - @Produces(MediaType.SERVER_SENT_EVENTS) - @Path("/{name}") - public Multi greeting(@PathParam String name) { - // TODO: create a Reactive Streams publisher or a Mutiny Multi - return publisher; - } -} +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. ---- -Now we just need to return our `Publisher` or `Multi`: +Then, in the `VertxResource` file add the following method: [source, java] ---- -package org.acme.vertx; - -import io.smallrye.mutiny.Multi; -import io.vertx.mutiny.core.Vertx; -import org.jboss.resteasy.annotations.jaxrs.PathParam; - -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 java.util.Date; - -@Path("/stream") -public class StreamingResource { - - @Inject - Vertx vertx; - - @GET - @Produces(MediaType.SERVER_SENT_EVENTS) - @Path("/{name}") - public Multi greeting(@PathParam String name) { - return vertx.periodicStream(2000).toMulti() - .map(l -> String.format("Hello %s! (%s)%n", name, new Date())); - } -} ----- - -The server side is ready. -In order to see the result in the browser, we need a web page. - -[source,html] -.META-INF/resources/streaming.html ----- - - - - - SSE with Vert.x - Quarkus - - - -
- - ----- - -Our web page just has an empty `
` 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].