diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 5b6af78e7d60e..377f177f4e550 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -121,7 +121,7 @@ 9.2.1 2.3.2 2.3.232 + and the dependency jts-core needs to be updated in extensions/jdbc/jdbc-h2/runtime/pom.xml --> 42.7.4 3.4.1 8.3.0 diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java index e76f388238511..75e582f7bedc7 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/NativeConfig.java @@ -342,6 +342,11 @@ default String getEffectiveImage() { * If errors should be reported at runtime. This is a more relaxed setting, however it is not recommended as it * means * your application may fail at runtime if an unsupported feature is used by accident. + * + * Note that the use of this flag may result in build time failures due to {@code ClassNotFoundException}s. + * Reason most likely being that the Quarkus extension already optimized it away or do not actually need it. + * In such cases you should explicitly add the corresponding dependency providing the missing classes as a + * dependency to your project. */ @WithDefault("false") boolean reportErrorsAtRuntime(); diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc index 64ba4bf0e3a39..654b06364b895 100644 --- a/docs/src/main/asciidoc/qute-reference.adoc +++ b/docs/src/main/asciidoc/qute-reference.adoc @@ -2228,7 +2228,7 @@ Quarkus provides a set of built-in extension methods. TIP: A map value can be also accessed directly: `{map.myKey}`. Use the bracket notation for keys that are not legal identifiers: `{map['my key']}`. -==== Lists +===== Lists * `get(index)`: Returns the element at the specified position in a list ** `{list.get(0)}` @@ -2654,6 +2654,79 @@ class DetailResource { WARNING: Unlike with `@Inject` the templates obtained via `RestTemplate` are not validated, i.e. the build does not fail if a template does not exist. +[[vertx_integration]] +=== Vert.x Integration + +If you want to use `io.vertx.core.json.JsonObject` as data in your templates, then you will need to add the `quarkus-vertx` extension to your build file if not already part of your dependencies (most applications use this extension by default). + + +[source,xml,role="primary maven-dependency"] +.pom.xml +---- + + io.quarkus + quarkus-vertx + +---- + +[source,gradle,role="secondary gradle-dependency"] +.build.gradle +---- +implementation("io.quarkus:quarkus-vertx") +---- + +With this dependency included, we have a special value resolver for `io.vertx.core.json.JsonObject` which makes it possible to access the properties of a JSON object in a template: + +.src/main/resources/templates/foo.txt +[source,text] +---- +{tool.name} +{tool.fieldNames} +{tool.fields} +{tool.size} +{tool.empty} +{tool.isEmpty} +{tool.get('name')} +{tool.containsKey('name')} +---- + +.QuteVertxIntegration.java +[source,java] +---- +import java.util.HashMap; +import jakarta.inject.Inject; +import io.vertx.core.json.JsonObject; +import io.quarkus.qute.Template; + +public class QuteVertxIntegration { + + @Inject + Template foo; + + public String render() { + HashMap toolMap = new Map(); + toolMap.put("name", "Roq"); + JsonObject jsonObject = new JsonObject(toolMap); + return foo.data("tool", jsonObject).render(); + } +} +---- + +The `QuteVertxIntegration#render()` output should look like: + +[source,text] +---- +Roq +[name] +[name] +1 +false +false +Roq +true +---- + + === Development Mode In the development mode, all files located in `src/main/resources/templates` are watched for changes. diff --git a/docs/src/main/asciidoc/security-jwt.adoc b/docs/src/main/asciidoc/security-jwt.adoc index 781a24f71987e..3b5515a5f7691 100644 --- a/docs/src/main/asciidoc/security-jwt.adoc +++ b/docs/src/main/asciidoc/security-jwt.adoc @@ -817,7 +817,7 @@ To prevent it, set `quarkus.smallrye-jwt.blocking-authentication=true`. === Token Propagation -Please see the xref:security-openid-connect-client-reference.adoc#token-propagation[Token Propagation] section about the Bearer access token propagation to the downstream services. +Please see the xref:security-openid-connect-client-reference.adoc#token-propagation-rest[Token Propagation] section about the Bearer access token propagation to the downstream services. [[integration-testing]] === Testing diff --git a/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc b/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc index 5d180dfdd6c62..d5e9dfe2b9953 100644 --- a/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc +++ b/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc @@ -625,7 +625,7 @@ In fact, the last code example, showing the injected `UserInfo`, is a concrete e But what about propagating access tokens to some custom services ? It is very easy to achieve in Quarkus, both for the authorization code and bearer token flows. All you need to do is to create a REST Client interface for calling the service requiring a Bearer token access and annotate it with `@AccessToken` and the access token arriving to the front-end endpoint as the Auth0 Bearer access token or acquired by Quarkus after completing the Auth0 authorization code flow, will be propagated to the target microservice. This is as easy as it can get. For examples of propagating access tokens, see the following sections in this tutorial. -For more information about token propagation, see xref:security-openid-connect-client-reference.adoc#reactive-token-propagation[OIDC token propagation]. +For more information about token propagation, see xref:security-openid-connect-client-reference.adoc#token-propagation-rest[OIDC token propagation]. [[jwt-access-tokens]] === Access tokens in JWT format diff --git a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc index 3052aa4ecffe2..eb04efd1c8efd 100644 --- a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc @@ -475,7 +475,7 @@ quarkus.oidc.introspection-path=/protocol/openid-connect/tokens/introspect === Token propagation -For information about bearer access token propagation to the downstream services, see the xref:security-openid-connect-client-reference.adoc#token-propagation[Token propagation] section of the Quarkus "OpenID Connect (OIDC) and OAuth2 client and filters reference" guide. +For information about bearer access token propagation to the downstream services, see the xref:security-openid-connect-client-reference.adoc#token-propagation-rest[Token propagation] section of the Quarkus "OpenID Connect (OIDC) and OAuth2 client and filters reference" guide. === JWT token certificate chain diff --git a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc index 3fd51fe7d7b08..5e64a01bbd8af 100644 --- a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc @@ -1497,7 +1497,7 @@ TIP: You can listen to other security events as described in the xref:security-c === Propagating tokens to downstream services -For information about Authorization Code Flow access token propagation to downstream services, see the xref:security-openid-connect-client-reference.adoc#token-propagation[Token Propagation] section. +For information about Authorization Code Flow access token propagation to downstream services, see the xref:security-openid-connect-client-reference.adoc#token-propagation-rest[Token Propagation] section. == Integration considerations diff --git a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc index 6e3677c0fd4ad..e1b9f2b4fc566 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc @@ -131,7 +131,7 @@ quarkus.oidc-client.grant.type=refresh Then you can use the `OidcClient.refreshTokens` method with a provided refresh token to get the access token. -Using the `urn:ietf:params:oauth:grant-type:token-exchange` or `urn:ietf:params:oauth:grant-type:jwt-bearer` grants might be required if you are building a complex microservices application and want to avoid the same `Bearer` token be propagated to and used by more than one service. See <> and <> for more details. +Using the `urn:ietf:params:oauth:grant-type:token-exchange` or `urn:ietf:params:oauth:grant-type:jwt-bearer` grants might be required if you are building a complex microservices application and want to avoid the same `Bearer` token be propagated to and used by more than one service. See <> and <> for more details. Using `OidcClient` to support the `authorization code` grant might be required if, for some reason, you cannot use the xref:security-oidc-code-flow-authentication.adoc[Quarkus OIDC extension] to support Authorization Code Flow. If there is a very good reason for you to implement Authorization Code Flow, then you can configure `OidcClient` as follows: @@ -1102,10 +1102,10 @@ public class OidcRequestCustomizer implements OidcRequestFilter { } ---- -[[token-propagation-reactive]] -== Token Propagation Reactive +[[token-propagation-rest]] +== Token Propagation for Quarkus REST -The `quarkus-rest-client-oidc-token-propagation` extension provides a REST Client, `io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter`, that simplifies the propagation of authentication information. This client propagates the xref:security-oidc-bearer-token-authentication.adoc[bearer token] present in the currently active request or the token acquired from the xref:security-oidc-code-flow-authentication.adoc[authorization code flow mechanism] as the HTTP `Authorization` header's `Bearer` scheme value. +The `quarkus-rest-client-oidc-token-propagation` extension provides a REST Client filter, `io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter`, that simplifies the propagation of authentication information. This client propagates the xref:security-oidc-bearer-token-authentication.adoc[bearer token] present in the currently active request or the token acquired from the xref:security-oidc-code-flow-authentication.adoc[authorization code flow mechanism] as the HTTP `Authorization` header's `Bearer` scheme value. You can selectively register `AccessTokenRequestReactiveFilter` by using either `io.quarkus.oidc.token.propagation.AccessToken` or `org.eclipse.microprofile.rest.client.annotation.RegisterProvider` annotation, for example: @@ -1178,8 +1178,8 @@ quarkus.resteasy-client-oidc-token-propagation.exchange-token=true `AccessTokenRequestReactiveFilter` uses a default `OidcClient` by default. A named `OidcClient` can be selected with a `quarkus.rest-client-oidc-token-propagation.client-name` configuration property or with the `io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient` annotation attribute. -[[token-propagation]] -== Token Propagation +[[token-propagation-resteasy]] +== Token Propagation for RESTEasy Classic The `quarkus-resteasy-client-oidc-token-propagation` extension provides two Jakarta REST `jakarta.ws.rs.client.ClientRequestFilter` class implementations that simplify the propagation of authentication information. `io.quarkus.oidc.token.propagation.AccessTokenRequestFilter` propagates the xref:security-oidc-bearer-token-authentication.adoc[Bearer token] present in the current active request or the token acquired from the xref:security-oidc-code-flow-authentication.adoc[Authorization code flow mechanism], as the HTTP `Authorization` header's `Bearer` scheme value. @@ -1333,24 +1333,6 @@ As mentioned, use `AccessTokenRequestFilter` if you work with Keycloak or an Ope You can generate the tokens as described in xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing[OpenID Connect Bearer Token Integration testing] section. Prepare the REST test endpoints. You can have the test front-end endpoint, which uses the injected MP REST client with a registered token propagation filter, call the downstream endpoint. For example, see the `integration-tests/resteasy-client-oidc-token-propagation` in the `main` Quarkus repository. -[[reactive-token-propagation]] -== Token Propagation Reactive - -Add the following Maven Dependency: - -[source,xml] ----- - - io.quarkus - quarkus-rest-client-oidc-token-propagation - ----- - -The `quarkus-rest-client-oidc-token-propagation` extension provides `io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter` which can be used to propagate the current `Bearer` or `Authorization Code Flow` access tokens. - -The `quarkus-rest-client-oidc-token-propagation` extension (as opposed to the non-reactive `quarkus-resteasy-client-oidc-token-propagation` extension) does not currently support the exchanging or resigning of the tokens before the propagation. -However, these features might be added in the future. - ifndef::no-quarkus-oidc-client-graphql[] [[quarkus-oidc-client-graphql]] == GraphQL client integration diff --git a/docs/src/main/asciidoc/security-openid-connect-providers.adoc b/docs/src/main/asciidoc/security-openid-connect-providers.adoc index 667ae69b54b4e..72ac3bbc4c77a 100644 --- a/docs/src/main/asciidoc/security-openid-connect-providers.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-providers.adoc @@ -584,7 +584,7 @@ As mentioned in the xref:security-oidc-code-flow-authentication.adoc[OIDC code f It is this access token that has to be propagated to services such as `Google Calendar`, or `Spotify Playlists` for the currently authenticated user to be able to use such services. You do not have to bring provider-specific libraries in order to achieve this, but instead you can use a reactive `Token Propagation` filter, which can be bound to a given REST client with a simple annotation. -For more information, see the Quarkus xref:security-openid-connect-client-reference.adoc#token-propagation-reactive[Access token propagation] guide. +For more information, see the Quarkus xref:security-openid-connect-client-reference.adoc#token-propagation-rest[Access token propagation] guide. For example, after you have configured the <> provider, you can have events added to the user's Google Calendar by using a REST client as shown in the following example: diff --git a/docs/src/main/asciidoc/tls-registry-reference.adoc b/docs/src/main/asciidoc/tls-registry-reference.adoc index a279f1f3988d7..1a9cdc0ded910 100644 --- a/docs/src/main/asciidoc/tls-registry-reference.adoc +++ b/docs/src/main/asciidoc/tls-registry-reference.adoc @@ -3,9 +3,10 @@ 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 //// +[id="tls-registry-reference"] = TLS registry reference include::_attributes.adoc[] -:categories: network +:categories: web :summary: TLS registry configuration and usage :numbered: :sectnums: @@ -559,7 +560,7 @@ The `reload` method returns a `boolean` indicating whether the reload was succes 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 is to fire a CDI event (`CertificateReloadedEvent`) that servers and clients can listen to and make the necessary changes: +The recommended approach is to fire a CDI event (`CertificateUpdatedEvent`) that servers and clients can listen to and make the necessary changes: [source, java] ---- @@ -569,12 +570,12 @@ TlsConfigurationRegistry registry; public void reload() { TlsConfiguration config = registry.get("name").orElseThrow(); if (config.reload()) { - event.fire(new CertificateReloadedEvent("name", config)); + event.fire(new CertificateUpdatedEvent("name", config)); } } // In the server or client code -public void onReload(@Observes CertificateReloadedEvent event) { +public void onReload(@Observes CertificateUpdatedEvent event) { if ("name".equals(event.getName())) { server.updateSSLOptions(event.tlsConfiguration().getSSLOptions()); // Or update the SSLContext. @@ -588,7 +589,7 @@ These APIs provide a way to implement custom certificate reloading. The TLS registry includes a built-in mechanism for periodically checking the file system for changes and reloading certificates. You can configure periodic certificate reloading by using properties. -The `reload-period` property specifies the interval for reloading certificates and will emit a `CertificateReloadedEvent` each time certificates are reloaded. +The `reload-period` property specifies the interval for reloading certificates and will emit a `CertificateUpdatedEvent` each time certificates are reloaded. [source, properties] ---- @@ -606,7 +607,7 @@ quarkus.tls.http.key-store.pem.0.cert=tls.crt quarkus.tls.http.key-store.pem.0.key=tls.key ---- -Remember that the impacted server and client may need to listen to the `CertificateReloadedEvent` to apply the new certificates. +Remember that the 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, including the management interface if it is enabled. == Using Kubernetes secrets or cert-manager @@ -1180,6 +1181,27 @@ quarkus.tls.lets-encrypt.enabled=true 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`). +Ensure the port `80` is accessible from the Internet. +It might require an explicit security policy depending on your hosting provider. + +We also recommend setting `quarkus.http.insecure-requests` to `redirect` to redirect all HTTP requests to HTTPS. +The ACME challenge accepts self-signed certificates and up to 10 redirections: + +[source, properties] +---- +quarkus.tls.lets-encrypt.enabled=true +quarkus.management.enabled=true +quarkus.http.insecure-requests=redirect +---- + +==== + +[[lets-encrypt-prepare]] + The challenge is served from the primary HTTP interface (accessible from your DNS domain name). IMPORTANT: Do not start your application yet. @@ -1305,5 +1327,6 @@ Now, because ngrok only forwards ACME challenges over HTTP, start ngrok as follo ngrok http --domain 8080 --scheme http <1> ---- <1> `8080` is the localhost HTTP port your application is listening on. +Note that the application will be accessible from `http://YOUR-NGROK-DOMAIN` on port `80` but redirected to your local machine on port `8080`. You can now test the Quarkus Let's Encrypt ACME feature from your local machine. diff --git a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java index 2558695960f14..2ab94cb59d7c6 100644 --- a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java +++ b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java @@ -87,6 +87,7 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveFieldBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem; import io.quarkus.deployment.logging.LogCleanupFilterBuildItem; import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; import io.quarkus.deployment.recording.RecorderContext; @@ -594,6 +595,12 @@ public void build( hibernateValidatorBuildTimeConfig))); } + @BuildStep + public RuntimeReinitializedClassBuildItem reinitClockProviderSystemTimezone() { + return new RuntimeReinitializedClassBuildItem( + "io.quarkus.hibernate.validator.runtime.clockprovider.HibernateValidatorClockProviderSystemZoneIdHolder"); + } + @BuildStep void indexAdditionalConstrainedClasses(List additionalConstrainedClasses, BuildProducer additionalConstrainedClassesIndex) { diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java index 40429cc069436..baaafa562bfec 100644 --- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java @@ -31,6 +31,7 @@ import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.arc.runtime.BeanContainerListener; import io.quarkus.hibernate.validator.ValidatorFactoryCustomizer; +import io.quarkus.hibernate.validator.runtime.clockprovider.RuntimeReinitializedDefaultClockProvider; import io.quarkus.hibernate.validator.runtime.jaxrs.ResteasyConfigSupport; import io.quarkus.runtime.LocalesBuildTimeConfig; import io.quarkus.runtime.ShutdownContext; @@ -129,6 +130,11 @@ public void created(BeanContainer container) { InstanceHandle configuredClockProvider = Arc.container().instance(ClockProvider.class); if (configuredClockProvider.isAvailable()) { configuration.clockProvider(configuredClockProvider.get()); + } else { + // If user didn't provide a custom clock provider we want to set our own. + // This provider ensure the correct behavior in a native mode as it does not + // cache the time zone at a build time. + configuration.clockProvider(RuntimeReinitializedDefaultClockProvider.INSTANCE); } // Hibernate Validator-specific configuration diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/clockprovider/HibernateValidatorClockProviderSystemZoneIdHolder.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/clockprovider/HibernateValidatorClockProviderSystemZoneIdHolder.java new file mode 100644 index 0000000000000..3c7926b89efb0 --- /dev/null +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/clockprovider/HibernateValidatorClockProviderSystemZoneIdHolder.java @@ -0,0 +1,27 @@ +package io.quarkus.hibernate.validator.runtime.clockprovider; + +import java.time.ZoneId; + +/** + * A helper class holding a system timezone. + *

+ * It is reloaded at runtime to provide the runtime-system time zone + * to the constraints based on a {@link jakarta.validation.ClockProvider}. + *

+ * Note, that we do not hold the timezone constant in the clock provider itself as we need to "reinitialize" this class, + * so that the timezone is set to the actual runtime-system-timezone. + * Having a constant in the clock provider and asking to reload the provider class leads to native build failure: + *

+ * + * Error: An object of type 'io.quarkus.hibernate.validator.runtime.clockprovider.HibernateValidatorClockProvider' was found in + * the image heap. This type, however, is marked for initialization at image run time for the following reason: classes are + * initialized at run time by default. + * This is not allowed for correctness reasons: All objects that are stored in the image heap must be initialized at build time. + * + *

+ * And we do have instances of the clock provider/clock in the Hibernate Validator metadata as we eagerly initialize + * constraints. + */ +class HibernateValidatorClockProviderSystemZoneIdHolder { + static final ZoneId SYSTEM_ZONE_ID = ZoneId.systemDefault(); +} diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/clockprovider/RuntimeReinitializedDefaultClockProvider.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/clockprovider/RuntimeReinitializedDefaultClockProvider.java new file mode 100644 index 0000000000000..57df90b170717 --- /dev/null +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/clockprovider/RuntimeReinitializedDefaultClockProvider.java @@ -0,0 +1,45 @@ +package io.quarkus.hibernate.validator.runtime.clockprovider; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; + +import jakarta.validation.ClockProvider; + +/** + * A Quarkus-specific clock provider that can provide a clock based on a runtime system time zone. + */ +public class RuntimeReinitializedDefaultClockProvider implements ClockProvider { + + public static final RuntimeReinitializedDefaultClockProvider INSTANCE = new RuntimeReinitializedDefaultClockProvider(); + + private static final RuntimeReinitializedDefaultClock clock = new RuntimeReinitializedDefaultClock(); + + private RuntimeReinitializedDefaultClockProvider() { + } + + @Override + public Clock getClock() { + return clock; + } + + private static class RuntimeReinitializedDefaultClock extends Clock { + + @Override + public ZoneId getZone() { + // we delegate getting the zone id value to a helper class that is reinitialized at runtime + // allowing to pick up an actual runtime timezone. + return HibernateValidatorClockProviderSystemZoneIdHolder.SYSTEM_ZONE_ID; + } + + @Override + public Clock withZone(ZoneId zone) { + return Clock.system(zone); + } + + @Override + public Instant instant() { + return Instant.now(); + } + } +} diff --git a/extensions/jdbc/jdbc-h2/runtime/pom.xml b/extensions/jdbc/jdbc-h2/runtime/pom.xml index 1302bc0bdc7cd..aca531e1041c0 100644 --- a/extensions/jdbc/jdbc-h2/runtime/pom.xml +++ b/extensions/jdbc/jdbc-h2/runtime/pom.xml @@ -21,15 +21,6 @@ com.h2database h2 - - - org.apache.lucene - lucene-core - 9.7.0 - org.locationtech.jts jts-core diff --git a/extensions/jsonb/runtime/src/main/java/io/quarkus/jsonb/JsonbProducer.java b/extensions/jsonb/runtime/src/main/java/io/quarkus/jsonb/JsonbProducer.java index c8f7941b2adf0..4d191b7115550 100644 --- a/extensions/jsonb/runtime/src/main/java/io/quarkus/jsonb/JsonbProducer.java +++ b/extensions/jsonb/runtime/src/main/java/io/quarkus/jsonb/JsonbProducer.java @@ -11,6 +11,7 @@ import io.quarkus.arc.All; import io.quarkus.arc.DefaultBean; +import io.quarkus.jsonp.JsonProviderHolder; @Singleton public class JsonbProducer { @@ -30,6 +31,6 @@ public JsonbConfig jsonbConfig(@All List customizers) { @Singleton @DefaultBean public Jsonb jsonb(JsonbConfig jsonbConfig) { - return JsonbBuilder.create(jsonbConfig); + return JsonbBuilder.newBuilder().withProvider(JsonProviderHolder.jsonProvider()).withConfig(jsonbConfig).build(); } } diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaAdminClient.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaAdminClient.java index 1600561f3f1ef..0ced0504abcda 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaAdminClient.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaAdminClient.java @@ -1,6 +1,13 @@ package io.quarkus.kafka.client.runtime; -import java.util.*; +import static io.quarkus.kafka.client.runtime.KafkaRuntimeConfigProducer.TLS_CONFIG_NAME_KEY; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -33,8 +40,10 @@ void init() { Map conf = new HashMap<>(); conf.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, DEFAULT_ADMIN_CLIENT_TIMEOUT); for (Map.Entry entry : config.entrySet()) { - if (AdminClientConfig.configNames().contains(entry.getKey())) { - conf.put(entry.getKey(), entry.getValue().toString()); + String key = entry.getKey(); + // include TLS config name if it has been configured + if (TLS_CONFIG_NAME_KEY.equals(key) || AdminClientConfig.configNames().contains(key)) { + conf.put(key, entry.getValue().toString()); } } client = AdminClient.create(conf); diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java index 7490541fc174d..ff679cd61c80e 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaRuntimeConfigProducer.java @@ -16,6 +16,8 @@ @Singleton public class KafkaRuntimeConfigProducer { + public static final String TLS_CONFIG_NAME_KEY = "tls-configuration-name"; + // not "kafka.", because we also inspect env vars, which start with "KAFKA_" private static final String CONFIG_PREFIX = "kafka"; private static final String UI_CONFIG_PREFIX = CONFIG_PREFIX + ".ui"; @@ -45,7 +47,7 @@ public Map createKafkaRuntimeConfig(Config config, ApplicationCo .replace("_", "."); String value = config.getOptionalValue(propertyName, String.class).orElse(""); result.put(effectivePropertyName, value); - if (effectivePropertyName.equals("tls-configuration-name")) { + if (effectivePropertyName.equals(TLS_CONFIG_NAME_KEY)) { result.put("ssl.engine.factory.class", QuarkusKafkaSslEngineFactory.class.getName()); } } diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/tls/QuarkusKafkaSslEngineFactory.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/tls/QuarkusKafkaSslEngineFactory.java index 42aab3be3e781..1ebd9d795aed1 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/tls/QuarkusKafkaSslEngineFactory.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/tls/QuarkusKafkaSslEngineFactory.java @@ -1,5 +1,7 @@ package io.quarkus.kafka.client.tls; +import static io.quarkus.kafka.client.runtime.KafkaRuntimeConfigProducer.TLS_CONFIG_NAME_KEY; + import java.io.IOException; import java.security.KeyStore; import java.util.Map; @@ -92,7 +94,11 @@ public void close() throws IOException { @Override public void configure(Map configs) { - String tlsConfigName = (String) configs.get("tls-configuration-name"); + String tlsConfigName = (String) configs.get(TLS_CONFIG_NAME_KEY); + if (tlsConfigName == null) { + throw new IllegalArgumentException( + "The 'tls-configuration-name' property is required for Kafka Quarkus TLS Registry integration."); + } Instance tlsConfig = CDI.current().getBeanManager().createInstance() .select(TlsConfigurationRegistry.class); @@ -118,7 +124,7 @@ public void configure(Map configs) { * @param configs the Kafka client configuration */ public static void checkForOtherSslConfigs(Map configs) { - String tlsConfigName = (String) configs.get("tls-configuration-name"); + String tlsConfigName = (String) configs.get(TLS_CONFIG_NAME_KEY); for (String sslConfig : KAFKA_SSL_CONFIGS) { if (configs.containsKey(sslConfig)) { log.warnf( diff --git a/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java b/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java index 604017f831591..570ab735c8f07 100644 --- a/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java +++ b/extensions/kafka-streams/runtime/src/main/java/io/quarkus/kafka/streams/runtime/KafkaStreamsProducer.java @@ -1,5 +1,6 @@ package io.quarkus.kafka.streams.runtime; +import static io.quarkus.kafka.client.runtime.KafkaRuntimeConfigProducer.TLS_CONFIG_NAME_KEY; import static io.quarkus.kafka.streams.runtime.KafkaStreamsRuntimeConfig.DEFAULT_KAFKA_BROKER; import java.net.InetSocketAddress; @@ -362,6 +363,10 @@ private static void waitForTopicsToBeCreated(Admin adminClient, Collection first diff --git a/extensions/oidc-common/runtime/pom.xml b/extensions/oidc-common/runtime/pom.xml index 13b0ddb973f1a..f05f78506e1ed 100644 --- a/extensions/oidc-common/runtime/pom.xml +++ b/extensions/oidc-common/runtime/pom.xml @@ -71,6 +71,9 @@ ${project.version} + + -AlegacyConfigRoot=true + diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/test/RenderedResultsDisabledTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/test/RenderedResultsDisabledTest.java index 867778d4ee832..805586405f21a 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/test/RenderedResultsDisabledTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/test/RenderedResultsDisabledTest.java @@ -1,5 +1,6 @@ package io.quarkus.qute.deployment.test; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import jakarta.enterprise.inject.Instance; @@ -10,6 +11,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.qute.RenderedResults; +import io.quarkus.qute.Template; import io.quarkus.test.QuarkusUnitTest; public class RenderedResultsDisabledTest { @@ -25,9 +27,13 @@ public class RenderedResultsDisabledTest { @Inject Instance renderedResults; + @Inject + Template foo; + @Test public void testRenderedResultsNotRegistered() { assertTrue(renderedResults.isUnsatisfied()); + assertEquals("Morna", foo.data("name", "Morna").render()); } } diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/TemplateProducer.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/TemplateProducer.java index 55cf3151aec9a..ae7d0519d5e94 100644 --- a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/TemplateProducer.java +++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/TemplateProducer.java @@ -68,7 +68,7 @@ public class TemplateProducer { templateVariants.put(entry.getKey(), var); } this.templateVariants = Collections.unmodifiableMap(templateVariants); - this.renderedResults = launchMode == LaunchMode.TEST ? renderedResults.get() : null; + this.renderedResults = launchMode == LaunchMode.TEST && renderedResults.isResolvable() ? renderedResults.get() : null; this.injectedTemplates = launchMode == LaunchMode.DEVELOPMENT ? Collections.synchronizedList(new ArrayList<>()) : null; LOGGER.debugf("Initializing Qute variant templates: %s", templateVariants); } diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-welcome.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-welcome.js index 74ee266f324e9..42814c4dd7c3c 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-welcome.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-welcome.js @@ -1,10 +1,16 @@ import { LitElement, html, css } from 'lit'; import { devuiState } from 'devui-state'; +import { JsonRpc } from 'jsonrpc'; +import '@vaadin/progress-bar'; +import '@vaadin/grid'; +import { columnBodyRenderer } from '@vaadin/grid/lit.js'; +import '@vaadin/grid/vaadin-grid-sort-column.js'; /** * This component shows the welcome screen */ export class QwcWelcome extends LitElement { + jsonRpc = new JsonRpc("devui-endpoints"); static styles = css` a { color: inherit; } @@ -109,14 +115,20 @@ export class QwcWelcome extends LitElement { } `; - static properties = {}; + static properties = { + _info: {state: true} + }; constructor() { super(); + this._info = null; } connectedCallback() { super.connectedCallback(); + this.jsonRpc.getJsonContent().then(jsonRpcResponse => { + this._info = jsonRpcResponse.result; + }); } render() { @@ -187,6 +199,7 @@ export class QwcWelcome extends LitElement { Static assets: ${devuiState.welcomeData.resourcesDir}/META-INF/resources/ Code: ${devuiState.welcomeData.sourceDir} + ${this._renderEndpoints()}

${this._renderSelectedExtensions()} @@ -202,6 +215,52 @@ export class QwcWelcome extends LitElement {
`; } + _renderEndpoints(){ + if (this._info) { + const typeTemplates = []; + for (const [type, list] of Object.entries(this._info)) { + if(type !== "Additional endpoints") + typeTemplates.push(html`${this._renderType(type,list)}`); + } + return html`${typeTemplates}`; + }else{ + return html` +
+
Fetching information...
+ +
+ `; + } + } + + _renderType(type, items){ + return html`

${type}

+ + + + + + + `; + } + + _uriRenderer(endpoint) { + if (endpoint.uri) { + return html`${endpoint.uri}`; + } + } + + _descriptionRenderer(endpoint) { + if (endpoint.description) { + return html`${endpoint.description}`; + } + } + _renderSelectedExtensions(){ if(devuiState.welcomeData.selectedExtensions){ return html`

Selected extensions

diff --git a/integration-tests/hibernate-validator/pom.xml b/integration-tests/hibernate-validator/pom.xml index edc6814da7d6f..6e02b8854f6ad 100644 --- a/integration-tests/hibernate-validator/pom.xml +++ b/integration-tests/hibernate-validator/pom.xml @@ -217,6 +217,24 @@ en + + + + test-nondefault-timezone + + + --env TZ=Europe/Helsinki + + + + integration-test + verify + + + diff --git a/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/HibernateValidatorTestResource.java b/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/HibernateValidatorTestResource.java index d286eeb5446d3..12a223688dfc3 100644 --- a/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/HibernateValidatorTestResource.java +++ b/integration-tests/hibernate-validator/src/main/java/io/quarkus/it/hibernate/validator/HibernateValidatorTestResource.java @@ -4,6 +4,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.time.LocalDateTime; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -23,6 +24,7 @@ import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.Digits; import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.PastOrPresent; import jakarta.validation.constraints.Pattern; import jakarta.validation.groups.ConvertGroup; import jakarta.ws.rs.Consumes; @@ -321,6 +323,17 @@ public MyBeanWithGroups testRestEndPointValidationGroups_Delete(@PathParam("id") return result; } + @GET + @Path("/rest-end-point-clock-based-constraints") + @Produces(MediaType.TEXT_PLAIN) + public String testClockBasedConstraints() { + ResultBuilder result = new ResultBuilder(); + + result.append(formatViolations(validator.validate(new Task()))); + + return result.build(); + } + private String formatViolations(Set> violations) { if (violations.isEmpty()) { return "passed"; @@ -447,4 +460,9 @@ private static class NestedBeanWithoutConstraints { @SuppressWarnings("unused") private String property; } + + public static class Task { + @PastOrPresent + public LocalDateTime created = LocalDateTime.now(); + } } diff --git a/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java b/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java index 880841201ec35..8ee69d6a73368 100644 --- a/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java +++ b/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java @@ -532,4 +532,12 @@ public void testRestEndPointValidationGroups_result() { response.body(containsString("must not be null")); } } + + @Test + void testClockBasedConstraints() { + RestAssured.when() + .get("/hibernate-validator/test/rest-end-point-clock-based-constraints") + .then() + .body(is("passed")); + } } diff --git a/integration-tests/kafka-ssl/src/main/java/io/quarkus/it/kafka/ssl/SslKafkaEndpoint.java b/integration-tests/kafka-ssl/src/main/java/io/quarkus/it/kafka/ssl/SslKafkaEndpoint.java index 8f1f00f7df085..0a6fff74a6c50 100644 --- a/integration-tests/kafka-ssl/src/main/java/io/quarkus/it/kafka/ssl/SslKafkaEndpoint.java +++ b/integration-tests/kafka-ssl/src/main/java/io/quarkus/it/kafka/ssl/SslKafkaEndpoint.java @@ -4,6 +4,7 @@ import java.util.Collections; import java.util.Map; import java.util.Properties; +import java.util.concurrent.ExecutionException; import jakarta.inject.Inject; import jakarta.ws.rs.GET; @@ -17,6 +18,7 @@ import org.apache.kafka.common.serialization.IntegerDeserializer; import org.apache.kafka.common.serialization.StringDeserializer; +import io.quarkus.kafka.client.runtime.KafkaAdminClient; import io.smallrye.common.annotation.Identifier; /** @@ -29,8 +31,13 @@ public class SslKafkaEndpoint { @Identifier("default-kafka-broker") Map kafkaConfig; + @Inject + KafkaAdminClient adminClient; + @GET - public String get(@QueryParam("format") CertificateFormat format) { + public String get(@QueryParam("format") CertificateFormat format) throws ExecutionException, InterruptedException { + // prevent admin client to be removed + adminClient.getTopics(); Consumer consumer = createConsumer(format); final ConsumerRecords records = consumer.poll(Duration.ofMillis(60000)); if (records.isEmpty()) {