From 13cde8c5cd85a0f80eabd590cd9640a4cbda05c8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 28 Jan 2025 01:54:49 +0000 Subject: [PATCH] Sync documentation of main branch --- .../main/config/quarkus-all-config.adoc | 2 +- .../config/quarkus-core_quarkus.test.adoc | 2 +- _versions/main/guides/kafka.adoc | 124 +++++++++++++++--- _versions/main/guides/messaging.adoc | 74 +++++++++++ .../main/guides/tls-registry-reference.adoc | 109 ++++++++------- 5 files changed, 232 insertions(+), 79 deletions(-) diff --git a/_generated-doc/main/config/quarkus-all-config.adoc b/_generated-doc/main/config/quarkus-all-config.adoc index e8aece64d29..3e37311a1cf 100644 --- a/_generated-doc/main/config/quarkus-all-config.adoc +++ b/_generated-doc/main/config/quarkus-all-config.adoc @@ -10359,7 +10359,7 @@ ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_TEST_ARG_LINE+++` endif::add-copy-button-to-env-var[] -- -|list of string +|string | a|icon:lock[title=Fixed at build time] [[quarkus-core_quarkus-test-env-environment-variable-name]] [.property-path]##link:#quarkus-core_quarkus-test-env-environment-variable-name[`quarkus.test.env."environment-variable-name"`]## diff --git a/_generated-doc/main/config/quarkus-core_quarkus.test.adoc b/_generated-doc/main/config/quarkus-core_quarkus.test.adoc index 94924f61c8a..244b6a19fbe 100644 --- a/_generated-doc/main/config/quarkus-core_quarkus.test.adoc +++ b/_generated-doc/main/config/quarkus-core_quarkus.test.adoc @@ -423,7 +423,7 @@ ifndef::add-copy-button-to-env-var[] Environment variable: `+++QUARKUS_TEST_ARG_LINE+++` endif::add-copy-button-to-env-var[] -- -|list of string +|string | a|icon:lock[title=Fixed at build time] [[quarkus-core_quarkus-test-env-environment-variable-name]] [.property-path]##link:#quarkus-core_quarkus-test-env-environment-variable-name[`quarkus.test.env."environment-variable-name"`]## diff --git a/_versions/main/guides/kafka.adoc b/_versions/main/guides/kafka.adoc index ac01aac591d..147f0b6dc30 100644 --- a/_versions/main/guides/kafka.adoc +++ b/_versions/main/guides/kafka.adoc @@ -2956,9 +2956,7 @@ NOTE: If you use Hibernate Reactive, look at < emitter; + @Channel("kafka") MutinyEmitter emitter; @POST @Path("/fruits") - @Transactional // <1> - public CompletionStage storeAndSendToKafka(Fruit fruit) { // <2> + @Transactional // <1> + public void storeAndSendToKafka(Fruit fruit) { // <2> fruit.persist(); - return emitter.send(new FruitDto(fruit)); // <3> + emitter.sendAndAwait(new FruitDto(fruit)); // <3> } } ---- <1> As we are writing to the database, make sure we run inside a transaction -<2> The method receives the fruit instance to persist. It returns a `CompletionStage` which is used for the transaction demarcation. The transaction is committed when the return `CompletionStage` completes. In our case, it's when the message is written to Kafka. +<2> The method receives the fruit instance to persist. <3> Wrap the managed entity inside a Data transfer object and send it to Kafka. This makes sure that managed entity is not impacted by the Kafka serialization. +Then await the completion of the operation before returning. + +NOTE: You should not return a `CompletionStage` or `Uni` when using `@Transactional`, as all transaction commits will happen on a single thread, which impacts performance. [[writing-entities-managed-by-hibernate-reactive-to-kafka]] === Writing entities managed by Hibernate Reactive to Kafka @@ -3191,23 +3192,104 @@ public class FruitProducer { @Consumes(MediaType.APPLICATION_JSON) @Bulkhead(1) public Uni post(Fruit fruit) { - Context context = Vertx.currentContext(); // <2> - return sf.withTransaction(session -> // <3> - kafkaTx.withTransaction(emitter -> // <4> - session.persist(fruit).invoke(() -> emitter.send(fruit)) // <5> - ).emitOn(context::runOnContext) // <6> - ); + return sf.withTransaction(session -> // <2> + kafkaTx.withTransaction(emitter -> // <3> + session.persist(fruit).invoke(() -> emitter.send(fruit)) // <4> + )); } } ---- <1> Inject the Hibernate Reactive `SessionFactory`. -<2> Capture the caller Vert.x context. -<3> Begin a Hibernate Reactive transaction. -<4> Begin a Kafka transaction. -<5> Persist the payload and send the entity to Kafka. -<6> The Kafka transaction terminates on the Kafka producer sender thread. -We need to switch to the Vert.x context previously captured in order to terminate the Hibernate Reactive transaction on the same context we started it. +<2> Begin a Hibernate Reactive transaction. +<3> Begin a Kafka transaction. +<4> Persist the payload and send the entity to Kafka. + +Alternatively, you can use the `@WithTransaction` annotation to start a transaction and commit it when the method returns: + +[source, java] +---- +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.faulttolerance.Bulkhead; +import org.eclipse.microprofile.reactive.messaging.Channel; + +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.kafka.transactions.KafkaTransactions; + +@Path("/") +public class FruitProducer { + + @Channel("kafka") KafkaTransactions kafkaTx; + + @POST + @Path("/fruits") + @Consumes(MediaType.APPLICATION_JSON) + @Bulkhead(1) + @WithTransaction // <1> + public Uni post(Fruit fruit) { + return kafkaTx.withTransaction(emitter -> // <2> + fruit.persist().invoke(() -> emitter.send(fruit)) // <3> + ); + } +} +---- + +<1> Start a Hibernate Reactive transaction and commit it when the method returns. +<2> Begin a Kafka transaction. +<3> Persist the payload and send the entity to Kafka. + +[[chaining-kafka-transactions-with-hibernate-orm-transactions]] +=== Chaining Kafka Transactions with Hibernate ORM transactions + +While `KafkaTransactions` provide a reactive API on top of Mutiny to manage Kafka transactions, +you can still chain Kafka transactions with blocking Hibernate ORM transactions. + +[source, java] +---- +import jakarta.transaction.Transactional; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import org.eclipse.microprofile.reactive.messaging.Channel; + +import io.quarkus.logging.Log; +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.kafka.transactions.KafkaTransactions; + +@Path("/") +public class FruitProducer { + + @Channel("kafka") KafkaTransactions emitter; + + @POST + @Path("/fruits") + @Consumes(MediaType.APPLICATION_JSON) + @Bulkhead(1) + @Transactional // <1> + public void post(Fruit fruit) { + emitter.withTransaction(e -> { // <2> + // if id is attributed by the database, will need to flush to get it + // fruit.persistAndFlush(); + fruit.persist(); // <3> + Log.infov("Persisted fruit {0}", p); + e.send(p); // <4> + return Uni.createFrom().voidItem(); + }).await().indefinitely(); // <5> + } +} +---- + +<1> Start a Hibernate ORM transaction. The transaction is committed when the method returns. +<2> Begin a Kafka transaction. +<3> Persist the payload. +<4> Send the entity to Kafka inside the Kafka transaction. +<5> Wait on the returned `Uni` for the Kafka transaction to complete. == Logging diff --git a/_versions/main/guides/messaging.adoc b/_versions/main/guides/messaging.adoc index e7cc35d6d2b..478123e5f3a 100644 --- a/_versions/main/guides/messaging.adoc +++ b/_versions/main/guides/messaging.adoc @@ -356,6 +356,80 @@ public class MyProfileBean { } ---- +==== Pausable Channels + +Injected `@Channel` streams are not subscribed to by default, so the flow of messages is controlled by the application code using reactive streams and Mutiny APIs. +But for `@Incoming` methods, the flow of messages is controlled by the runtime. + +Pausable channels provide a mechanism to control message flow programmatically. +This is useful in scenarios where producers or consumers need to stop temporarily due to managing the lifecycle or performing maintenance operations. + +To use pausable channels, you need to activate it with the configuration property `pausable` set to `true`. + +[source, properties] +---- +mp.messaging.incoming.my-channel.pausable=true +# optional, by default the channel is NOT paused initially +mp.messaging.outgoing.my-channel.initially-paused=true +---- + +If a channel is configured to be pausable, you can get the `PausableChannel` by channel name from the `ChannelRegistry` programmatically, and pause or resume the channel as needed: + +[source, java] +---- +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.eclipse.microprofile.reactive.messaging.Incoming; + +import io.smallrye.reactive.messaging.ChannelRegistry; +import io.smallrye.reactive.messaging.PausableChannel; + +@ApplicationScoped +public class PausableController { + + @Inject + ChannelRegistry registry; + + @PostConstruct + public void resume() { + // Wait for the application to be ready + // Retrieve the pausable channel + PausableChannel pausable = registry.getPausable("my-channel"); + // Pause the processing of the messages + pausable.resume(); + } + + public void pause() { + // Retrieve the pausable channel + PausableChannel pausable = registry.getPausable("my-channel"); + // Pause the processing of the messages + pausable.pause(); + } + + @Incoming("my-channel") + void process(String message) { + // Process the message + } + +} +---- + +This feature is independent of connectors and can be in theory used with channels backed by any connector. +Note that pausing message consumption applies back-pressure on the underlying consumer which receives messages from the remote broker. + +[NOTE] +==== +Kafka consumers provide a similar feature to pause and resume the consumption of messages from topic-partitions. +The Quarkus Kafka connector allows xref:kafka.adoc#kafka-bare-clients[access to the underlying client] to pause/resume the consumption. + +However, by default, with the `pause-if-no-requests=true` configuration, +the connector handles automatically the back-pressure, +by the pausing and resuming the Kafka consumer based on downstream requests. +It is therefore recommended to use pausable channels with the default `pause-if-no-requests=true` configuration. +==== + ==== Multiple Outgoings and `@Broadcast` By default, messages transmitted in a channel are only dispatched to a single consumer. diff --git a/_versions/main/guides/tls-registry-reference.adoc b/_versions/main/guides/tls-registry-reference.adoc index 8ef99143c6c..1a9efd0e282 100644 --- a/_versions/main/guides/tls-registry-reference.adoc +++ b/_versions/main/guides/tls-registry-reference.adoc @@ -50,17 +50,17 @@ By specifying the `+quarkus.tls..*+` properties, you can adapt the TLS set [IMPORTANT] ==== -The default TLS configuration is not a fallback/global configuration. This means that each named TLS configuration -(or "TLS bucket") needs to provide its own properties. For instance, `quarkus.tls.reload-period` will only be applied -to the default TLS configuration. +The default TLS configuration is not a fallback or global configuration. +Each named TLS configuration, or "TLS bucket," must provide its own properties. +For instance, `quarkus.tls.reload-period` will only be applied to the default TLS configuration. ==== === Configuring HTTPS for a HTTP server To ensure secure client-server communication, the client is often required to verify the server's authenticity. -* The server must use a keystore that contains its certificate and private key -* The client needs to be configured with a truststore to validate the server's certificate +* The server must use a keystore that contains its certificate and private key. +* The client needs to be configured with a truststore to validate the server's certificate. During the TLS handshake, the server presents its certificate, which the client then validates. This prevents man-in-the-middle attacks and secures data transmission. @@ -149,8 +149,7 @@ This configuration enables mTLS by ensuring that both the server and client vali [[referencing-a-tls-configuration]] == Referencing a TLS configuration -To reference an example _named_ configuration that you created by using the `quarkus.tls..*` properties as explained in <> -, use the `tls-configuration-name` property as shown in the following examples: +To reference an example _named_ configuration that you created by using the `quarkus.tls..*` properties as explained in <>, use the `tls-configuration-name` property as shown in the following examples: .Example configuration for the core HTTP server: [source,properties] @@ -173,39 +172,31 @@ quarkus.smallrye-graphql-client.my-client.tls-configuration-name=MY_TLS_CONFIGUR [NOTE] ==== -When using the Typesafe GraphQL client with a certificate -reloading mechanism (see <>), it is essential to -override the bean's scope to `RequestScoped` (or another similar scope -shorter than application). This is because by default, the Typesafe client is an -application-scoped bean, so shortening the scope guarantees that new instances of the bean -created after a certificate reload will be configured with the latest -certificate. Dynamic clients are `@Dependent` scoped, so you should -inject them into components with an appropriate scope. +When using the Typesafe GraphQL client with a certificate reloading mechanism, as described in the <> section, it is essential to override the bean's scope to `RequestScoped` or another similar scope shorter than the application. +This is because, by default, the Typesafe client is an application-scoped bean. +Shortening the scope guarantees that new instances of the bean created after a certificate reload will be configured with the latest certificate. +Dynamic clients are `@Dependent` scoped; inject them into components with an appropriate scope. ==== === Referencing the default truststore of SunJSSE JDK distributions typically contain a truststore in the `$JAVA_HOME/lib/security/cacerts` file. -It is used as a default truststore by SunJSSE, the default implementation of Java Secure Socket Extension (JSSE). -SSL/TLS capabilities provided by SunJSSE are leveraged by various Java Runtime components, -such as `javax.net.ssl.HttpsURLConnection` and others. +This truststore is used as a default truststore by SunJSSE, the default implementation of the Java Secure Socket Extension (JSSE). +SSL/TLS capabilities provided by SunJSSE are leveraged by various Java Runtime components, such as `javax.net.ssl.HttpsURLConnection` and others. -Although Quarkus extensions typically do not honor the default truststore of SunJSSE, -it might still be practical to use it in some situations - be it migration from legacy technologies -or when running on a Linux distribution where the SunJSSE truststore is synchronized with the operating system truststore. +Although Quarkus extensions typically do not honor the default truststore of SunJSSE, it is still practical to use it in some situations. +This applies when migrating from legacy technologies or running on a Linux distribution where the SunJSSE truststore is synchronized with the operating system (OS). -To make the use of SunJSSE truststore easier, Quarkus TLS Registry provides a TLS configuration -under the name `javax.net.ssl` that mimics the default behavior of SunJSSE: +To simplify the use of the SunJSSE truststore, Quarkus TLS Registry provides a TLS configuration under the name `javax.net.ssl` that mimics the default behavior of SunJSSE: -. If the `javax.net.ssl.trustStore` system property is defined, then its value is honored as a truststore -. Otherwise, the paths `$JAVA_HOME/lib/security/jssecacerts` and `$JAVA_HOME/lib/security/cacerts` are checked - and the first existing file is used as a truststore -. Otherwise an `IllegalStateException` is thrown. +* If the `javax.net.ssl.trustStore` system property is defined, its value is honored as a truststore. +* Otherwise, the paths `$JAVA_HOME/lib/security/jssecacerts` and `$JAVA_HOME/lib/security/cacerts` are checked, and the first existing file is used as a truststore. +* If neither condition is met, an `IllegalStateException` is thrown. The password for opening the truststore is taken from the `javax.net.ssl.trustStorePassword` system property. -If it is not set, the default password `changeit` is used. +If this property is not set, the default password `changeit` is used. -`javax.net.ssl` can be used as a value for various `*.tls-configuration-name` properties, for example: +The `javax.net.ssl` configuration can be used as a value for various `*.tls-configuration-name` properties, as shown below: .Example configuration for a gRPC client: [source,properties] @@ -227,15 +218,15 @@ The following sections outline the various properties available for configuring === Key stores -Key stores are used to store private keys and the certificates. +Key stores store private keys and certificates. They are mainly used on the server side but can also be used on the client side when mTLS is used. ==== PEM keystores Privacy Enhanced Mail (PEM) keystores are composed of a list of file pairs: -* *The certificate file* - a `.crt` or `.pem` file -* *The private key file* - often a `.key` file +* *The certificate file* - a `.crt` or `.pem` file. +* *The private key file* - often a `.key` file. To configure a PEM keystore: [source,properties] @@ -265,11 +256,11 @@ This setting is important when using SNI, because it uses the first specified pa When using PEM keystore, the following formats are supported: -- PKCS#8 private key (unencrypted) -- PKCS#1 RSA private key (unencrypted) -- Encrypted PKCS#8 private key (encrypted with AES-128-CBC) +* PKCS#8 private key (unencrypted) +* PKCS#1 RSA private key (unencrypted) +* Encrypted PKCS#8 private key (encrypted with AES-128-CBC) -In the later case, the `quarkus.tls.key-store.pem.password` (or `quarkus.tls.key-store.pem..password`) property must be set to the password used to decrypt the private key: +In the later case, the `quarkus.tls.key-store.pem.password` or `quarkus.tls.key-store.pem..password` property must be set to the password used to decrypt the private key. [source,properties] ---- @@ -494,7 +485,7 @@ quarkus.tls.cipher-suites=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384 The TLS protocol versions are the list of protocols that can be used during the TLS handshake. Enabled TLS protocol versions are specified as an ordered list separated by commas. -The relevant configuration property is `quarkus.tls.protocols` (or `quarkus.tls..protocols` for named TLS configurations). +The relevant configuration property is `quarkus.tls.protocols` or `quarkus.tls..protocols` for named TLS configurations. It defaults to `TLSv1.3, TLSv1.2` if not configured. The available options are `TLSv1`, `TLSv1.1`, `TLSv1.2`, and `TLSv1.3`. @@ -534,8 +525,11 @@ ALPN is enabled by default. quarkus.tls.alpn=false ---- + -WARNING: Disabling ALPN is not recommended for non-experts, as it can lead to performance degradation, protocol negotiation issues, and unexpected behavior, particularly with protocols like HTTP/2. +[WARNING] +==== +Disabling ALPN is not recommended for non-experts, as it can lead to performance degradation, protocol negotiation issues, and unexpected behavior, particularly with protocols like HTTP/2. However, disabling ALPN can be useful for diagnosing native inconsistencies or testing performance in specific edge cases where protocol negotiation causes conflicts. +==== ==== Certificate Revocation List (CRL) @@ -677,18 +671,17 @@ If any of these checks fail, the application will not start. == Reloading certificates The `TlsConfiguration` obtained from the `TLSConfigurationRegistry` includes a mechanism for reloading certificates. -The `reload` method refreshes the keystores, truststores and CRLs, typically by reloading them from the file system. +The `reload` method refreshes the keystores, truststores, and CRLs, typically by reloading them from the file system. NOTE: The reload operation is not automatic and must be triggered manually. -Additionally, the `TlsConfiguration` implementation must support reloading (which is the case for the configured certificate). +Additionally, the `TlsConfiguration` implementation must support reloading, as is the case for the configured certificate. The `reload` method returns a `boolean` indicating whether the reload was successful. A value of `true` means the reload operation was successful, not necessarily that there were updates to the certificates. After a `TlsConfiguration` has been reloaded, servers and clients using this configuration may need to perform specific actions to apply the new certificates. -The recommended approach for informing clients and servers about the certificate reload is to fire a CDI event of -type `io.quarkus.tls.CertificateUpdatedEvent`. +The recommended approach for informing clients and servers about the certificate reload is to fire a CDI event of type `io.quarkus.tls.CertificateUpdatedEvent`. To do so, inject a CDI event of this type and fire it when a reload occurs. . Manually triggering a reload and firing a `CertificateUpdatedEvent`: @@ -742,9 +735,11 @@ quarkus.tls.http.key-store.pem.0.cert=tls.crt quarkus.tls.http.key-store.pem.0.key=tls.key ---- -IMPORTANT: Impacted server and client may need to listen to the `CertificateUpdatedEvent` to apply the new certificates. -This is automatically done for the Quarkus HTTP server (i.e. Quarkus REST server, gRPC server, Web Socket server) and -the management interface if it is enabled. +[IMPORTANT] +==== +Impacted servers and clients may need to listen to the `CertificateUpdatedEvent` to apply the new certificates. +This is automatically done for the Quarkus HTTP server, such as the Quarkus REST server, gRPC server, and WebSocket server, as well as the management interface if it is enabled. +==== NOTE: In Quarkus dev mode, when files are touched, it will trigger the `CertificateUpdatedEvent` much more frequently. @@ -1134,18 +1129,17 @@ When developing with TLS, you can use two types of certificates: * **Self-signed certificate**: The certificate is signed by the same entity that uses it. It is not trusted by default. -This type of certificate is typically used when a Certificate Authority (CA) is unavailable or you want a simple setup. -It is not suitable for production and should only be used for development. +This type of certificate is typically used when a Certificate Authority (CA) is unavailable or when a simple setup is needed. +It is not suitable for production and is intended only for development. * **CA-signed certificate**: The certificate is signed by a Certificate CA, a trusted entity. This certificate is trusted by default and is the standard choice for production environments. While you can use a self-signed certificate for local development, it has limitations. -Browsers and tools like `curl`, `wget`, and `httpie` typically do not trust self-signed certificates, requiring manual import of the CA in your OS. +Browsers and tools like `curl`, `wget`, and `httpie` typically do not trust self-signed certificates, requiring manual import of the CA in your operating system. -To avoid this issue, you can use a development CA to sign certificates and install the CA in the system truststore. +To avoid this issue, use a development CA to sign certificates and install the CA in the system truststore. This ensures that the system trusts all certificates signed by the CA. - Quarkus simplifies the generation of a development CA and the certificates that are signed by this CA. [[generate-a-development-ca]] @@ -1348,7 +1342,7 @@ quarkus.management.enabled=true [IMPORTANT] ==== .Port 80 -The Let's Encrypt ACME challenge requires that the application is reachable on port `80` (basically: `http://your-dns-name`). +The Let's Encrypt ACME challenge requires that the application is reachable on port `80`, essentially `http://your-dns-name`. Ensure the port `80` is accessible from the Internet. It might require an explicit security policy depending on your hosting provider. @@ -1364,7 +1358,7 @@ quarkus.http.insecure-requests=redirect ==== -The challenge is served from the primary HTTP interface (accessible from your DNS domain name). +The challenge is served from the primary HTTP interface, which is accessible from your DNS domain name. IMPORTANT: Do not start your application yet. @@ -1383,9 +1377,9 @@ quarkus tls lets-encrypt prepare --domain= + The `prepare` command does the following: -* Creates a `.letsencrypt` folder in your application's root directory -* Creates a self-signed domain certificate and private key for your application configured in the previous <> step to be able to start and accept HTTPS requests -* Creates a `.env` configuration file in your application's root directory and configures the application to use the self-signed domain certificate and private key (until we get the Let's Encrypt certificate) +* Creates a `.letsencrypt` folder in your application's root directory. +* Creates a self-signed domain certificate and private key for your application configured in the previous <> step to start and accept HTTPS requests. +* Creates a `.env` configuration file in the root directory of your application and configures it to use the self-signed domain certificate and private key until you obtain the Let's Encrypt certificate. + The following snippet shows an example of the generated `.env` file: + @@ -1434,8 +1428,11 @@ Use `https://localhost:8443/` if you choose not to enable a management router in * Issues a Let's Encrypt certificate request. * Interacts with the Quarkus application to resolve ACME challenges. + -NOTE: When the Let's Encrypt certificate chain and private key have been successfully acquired, they are converted to PEM format and copied to your application's `.letsencrypt` folder. +[NOTE] +==== +When the Let's Encrypt certificate chain and private key have been successfully acquired, they are converted to PEM format and copied to your application's `.letsencrypt` folder. The TLS registry is informed that a new certificate and private key are ready and reloads them automatically. +==== + . Access your application's endpoint using `https://your-domain-name:8443/` again. Confirm in the browser that the Let's Encrypt certificate authority is now signing your domain certificate.