From d9dc0ffec3527b2cd8b75fdd7274d19d4cd58d3d Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 26 Aug 2021 13:48:12 +0300 Subject: [PATCH 001/138] Add an actionable message when attribute of multipart is a file and not accessed as such Closes: #19677 --- .../MultipartPopulatorGenerator.java | 4 ++-- .../runtime/multipart/MultipartSupport.java | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/MultipartPopulatorGenerator.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/MultipartPopulatorGenerator.java index f05f9dd80b16bf..e02091633f9024 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/MultipartPopulatorGenerator.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/MultipartPopulatorGenerator.java @@ -394,11 +394,11 @@ static String generate(ClassInfo multipartClassInfo, ClassOutput classOutput, In String.class, Class.class, java.lang.reflect.Type.class, MediaType.class, - ResteasyReactiveRequestContext.class), + ResteasyReactiveRequestContext.class, String.class), formStrValueHandle, populate.readStaticField(typeField), populate.readStaticField(genericTypeField), populate.readStaticField(mediaTypeField), - rrCtxHandle)); + rrCtxHandle, formAttrNameHandle)); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/multipart/MultipartSupport.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/multipart/MultipartSupport.java index 979fb51e18c845..1ee6f0fe2278be 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/multipart/MultipartSupport.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/multipart/MultipartSupport.java @@ -20,6 +20,7 @@ import javax.ws.rs.ext.MessageBodyReader; import org.jboss.logging.Logger; +import org.jboss.resteasy.reactive.multipart.FileUpload; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.core.ServerSerialisers; import org.jboss.resteasy.reactive.server.core.multipart.DefaultFileUpload; @@ -31,6 +32,7 @@ /** * This class isn't used directly, it is however used by generated code meant to deal with multipart forms. */ +@SuppressWarnings("unused") public final class MultipartSupport { private static final Logger log = Logger.getLogger(RequestDeserializeHandler.class); @@ -42,8 +44,22 @@ private MultipartSupport() { @SuppressWarnings({ "unchecked", "rawtypes" }) public static Object convertFormAttribute(String value, Class type, Type genericType, MediaType mediaType, - ResteasyReactiveRequestContext context) { + ResteasyReactiveRequestContext context, String attributeName) { if (value == null) { + FormData formData = context.getFormData(); + if (formData != null) { + Collection fileUploadsForName = formData.get(attributeName); + if (fileUploadsForName != null) { + for (FormData.FormValue fileUpload : fileUploadsForName) { + if (fileUpload.isFileItem()) { + log.debug("Attribute '" + attributeName + + "' of the multipart request is a file and therefore its value is not set. To obtain the contents of the file, use type '" + + FileUpload.class + "' as the field type."); + break; + } + } + } + } return null; } From 55daca504a5a6d982714d93780df5bc2e0e84e7b Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 30 Aug 2021 07:46:50 +0300 Subject: [PATCH 002/138] Provide actionable error message when non-static inner class used as JAX-RS resource Closes: #19757 --- .../reactive/server/test/InnerClassTest.java | 44 +++++++++++++++++++ .../common/processor/EndpointIndexer.java | 4 ++ 2 files changed, 48 insertions(+) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/InnerClassTest.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/InnerClassTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/InnerClassTest.java new file mode 100644 index 00000000000000..1513476f85ecff --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/InnerClassTest.java @@ -0,0 +1,44 @@ +package io.quarkus.resteasy.reactive.server.test; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.function.Supplier; + +import javax.enterprise.inject.spi.DeploymentException; +import javax.ws.rs.Path; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.common.annotation.Blocking; + +public class InnerClassTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(Resource.class); + } + }).setExpectedException(DeploymentException.class); + + @Test + public void test() { + fail("Should never have been called"); + } + + @Path("test") + public class Resource { + + @Path("hello") + @Blocking + public String hello() { + return "hello"; + } + } +} diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java index 517018911779c5..0ed75c29ce9afb 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java @@ -199,6 +199,10 @@ public ResourceClass createEndpoints(ClassInfo classInfo) { try { String path = scannedResourcePaths.get(classInfo.name()); ResourceClass clazz = new ResourceClass(); + if ((classInfo.enclosingClass() != null) && !Modifier.isStatic(classInfo.flags())) { + throw new DeploymentException( + "Non static nested resources classes are not supported: '" + classInfo.name() + "'"); + } clazz.setClassName(classInfo.name().toString()); if (path != null) { if (path.endsWith("/")) { From 392cc6f3b22329ff39e0785ea7b94ca627200262 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 27 Aug 2021 15:09:21 +0100 Subject: [PATCH 003/138] Document how to test OIDC with DevServices and minor updates to quarkus-test-keycloak-server --- .../security-openid-connect-dev-services.adoc | 41 +++++-- ...ity-openid-connect-web-authentication.adoc | 48 ++++++++- .../asciidoc/security-openid-connect.adoc | 100 +++++++++++++++++- .../CodeFlowDevModeDefaultTenantTestCase.java | 8 +- ...ication-dev-mode-default-tenant.properties | 5 +- .../it/keycloak/OidcTokenPropagationTest.java | 2 +- .../KeycloakTestClient.java | 7 +- .../KeycloakTestResourceLifecycleManager.java | 1 + 8 files changed, 181 insertions(+), 31 deletions(-) rename test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/{server => client}/KeycloakTestClient.java (92%) diff --git a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc index 32c07fdac2dfc8..0f6e5f479beb65 100644 --- a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc @@ -11,7 +11,8 @@ This guide covers the Dev Services for OpenId Connect (OIDC) Keycloak provider a == Introduction -Quarkus introduces an experimental `Dev Services For Keycloak` feature which is enabled by default when the `quarkus-oidc` extension is started in dev mode. It starts a Keycloak container and initializes it by registering the existing Keycloak realm or creating a new realm with the client and users for you to start developing your Quarkus application secured by Keycloak immediately. It will restart a container when the `application.properties` or the realm file changes have been detected. +Quarkus introduces an experimental `Dev Services For Keycloak` feature which is enabled by default when the `quarkus-oidc` extension is started in dev mode with `mvn quarkus:dev` and when the integration tests are running in test mode, but only when no `quarkus.oidc.auth-server-url` property is configured. +It starts a Keycloak container for both the dev and/or test modes and initializes them by registering the existing Keycloak realm or creating a new realm with the client and users for you to start developing your Quarkus application secured by Keycloak immediately. It will restart the container when the `application.properties` or the realm file changes have been detected. Additionally, link:dev-ui[Dev UI] available at http://localhost:8080/q/dev[/q/dev] supports this feature with a Keycloak specific page which helps to acquire the tokens from Keycloak and test your Quarkus application. @@ -28,20 +29,32 @@ $ mvn quarkus:dev 2021-06-04 16:22:47,629 INFO [🐳 .io/keycloak/keycloak:14.0.0]] (build-38) Container quay.io/keycloak/keycloak:14.0.0 is starting: 6469f6db9cec2c855fcc6c8db4273944cc9d69e8f6803a0b47eb2d5b8f5b94fd 2021-06-04 16:22:47,643 INFO [org.tes.con.wai.str.HttpWaitStrategy] (build-38) /elastic_lovelace: Waiting for 60 seconds for URL: http://localhost:32812/auth (where port 32812 maps to container port 8080) 2021-06-04 16:23:07,665 INFO [🐳 .io/keycloak/keycloak:14.0.0]] (build-38) Container quay.io/keycloak/keycloak:14.0.0 started in PT5.500489S +2021-06-04 16:23:07,666 INFO [io.qua.oid.dep.dev.key.KeycloakDevServicesProcessor] (build-38) Dev Services for Keycloak started. ... -2021-06-04 16:23:11,155 INFO [io.quarkus] (Quarkus Main Thread) security-openid-connect-quickstart 1.0.0-SNAPSHOT on JVM (powered by Quarkus 999-SNAPSHOT) started in 25.968s. Listening on: http://localhost:8080 -2021-06-04 16:23:11,157 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated. ---- The `quay.io/keycloak/keycloak:14.0.0` Keycloak image is used by default to start a container. `quarkus.keycloak.devservices.image-name` can be used to change the Keycloak image used. +Note that by default, `Dev Services for Keycloak` will not start a new container if it finds a container with a `quarkus-dev-service-keycloak` label and connect to it if this label's value matches the value of the `quarkus.keycloak.devservces.service-name` property (default value is `quarkus`). In such cases you will see a slighty different output: + +[source,shell] +---- +$ mvn quarkus:dev + +2021-08-27 18:42:43,530 INFO [io.qua.dev.com.ContainerLocator] (build-15) Dev Services container found: 48fee151a31ddfe32c39965be8f61108587b25ed2f66cdc18bb926d9e2e570c5 (quay.io/keycloak/keycloak:14.0.0). Connecting to: 0.0.0.0:32797. +2021-08-27 18:42:43,600 INFO [io.qua.oid.dep.dev.key.KeycloakDevServicesProcessor] (build-15) Dev Services for Keycloak started. +... +---- + +Note that you can disable sharing the containers with `quarkus.keycloak.devservices.shared=false`. + Now open the main link:http://localhost:8080/q/dev[Dev UI page] and you will see the `OpenId Connect Card` linking to a `Keycloak` page: image::dev-ui-oidc-keycloak-card.png[alt=Dev UI OpenId Connect Card,role="center"] Click on the `Provider: Keycloak` link and you will see a Keycloak page which will be presented slightly differently depending on how `Dev Services for Keycloak` feature has been configured. -=== Testing Service Applications +=== Developing Service Applications By default the Keycloak page can be used to support the development of a link:security-openid-connect[Quarkus OIDC service application]. @@ -63,8 +76,17 @@ Finally you can click a `Logged in` option if you'd like to log out and authenti [NOTE] ==== -You may need to register a redirect URI for the authorization code flow initiated by Dev UI for Keycloak to work. -Select a `Keycloak Admin` option in the right top corner, login as `admin:admin`, select the test realm and the client which Dev UI for Keycloak is configured with and add `http://localhost:8080/q/dev/io.quarkus.quarkus-oidc/provider` to `Valid Redirect URIs`. +You may need to register a redirect URI for the authorization code flow initiated by Dev UI for Keycloak to work because Keycloak may enforce that the authenticated users are redirected only to the configured redirect URI. It is recommended to do in production to avoid the users being redirected to the wrong endpoints in case which might happen if the correct `redirect_uri` parameter in the authentication request URI has been manipulated. + +If Keycloak does enforce it then you will see an authentication error informing you that the `redirect_uri` value is wrong. + +In this case select the `Keycloak Admin` option in the right top corner, login as `admin:admin`, select the test realm and the client which Dev UI for Keycloak is configured with and add `http://localhost:8080/q/dev/io.quarkus.quarkus-oidc/provider` to `Valid Redirect URIs`. If you used `-Dquarkus.http.port` when starting Quarkus then change `8080` to the value of `quarkus.http.port`. + +If the container is shared between multiple applications running on different ports then you will need to register `redirect_uri` values for each of these applications. + +You can set the `redirect_uri` value to `*` only for the test purposes, especially when the containers are shared between multiple applications. + +`*` `redirect_uri` value is set by `Dev Services for Keycloak` when it creates a default realm, if no custom realm is imported. ==== ==== Implicit Grant @@ -109,6 +131,13 @@ image::dev-ui-keycloak-sign-in-to-service.png[alt=Dev UI OpenId Connect Keycloak Set a relative service endpoint path, click on `Sign In To Service` and you will be redirected to Keycloak to enter a username and password in a new browser tab and get a response from the Quarkus application. +=== Running the tests + +You can run the tests against a Keycloak container started in a test mode in a link:continuous-testing[Continuous Testing] mode. + +It is also recommended to run the integration tests against Keycloak using `Dev Services for Keycloak`. +Please see link:security-openid-connect#integration-testing-keycloak-devservices[Testing OpenId Connect Service Applications with Dev Services] and link:security-openid-connect-web-authentication#integration-testing-keycloak-devservices[Testing OpenId Connect WebApp Applications with Dev Services] for more information. + === Keycloak Initialization You do not need to configure `quarkus-oidc-keycloak` to start developing your Quarkus Keycloak `OIDC` applications with the only exception being that `quarkus.oidc.application-type=web-app` has to be set in `application.properties` to give the `Keycloak` page a hint it needs to show an option to `Sign In To Service`. 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 0a6f47b01151c7..ec0c6ea9578f65 100644 --- a/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc @@ -582,7 +582,7 @@ It applies to ID tokens but also to access tokens in a JWT format if the `web-ap Please see link:security-openid-connect-client#token-propagation[Token Propagation] section about the Authorization Code Flow access token propagation to the downstream services. [[oidc-provider-client-authentication]] -=== Oidc Provider Client Authentication +== Oidc Provider Client Authentication `quarkus.oidc.runtime.OidcProviderClient` is used when a remote request to an OpenId Connect Provider has to be done. It has to authenticate to the OpenId Connect Provider when the authorization code has to be exchanged for the ID, access and refresh tokens, when the ID and access tokens have to be refreshed or introspected. @@ -784,10 +784,46 @@ Additionally, `OidcWiremockTestResource` set token issuer and audience to `https `OidcWiremockTestResource` can be used to emulate all OpenId Connect providers. +[[integration-testing-keycloak-devservices]] +=== Dev Services for Keycloak + +Using link:security-openid-connect-dev-services[Dev Services for Keycloak] is recommended for the integration testing against Keycloak. +`Dev Services for Keycloak` will launch and initialize a test container: it will create a `quarkus` realm, a `quarkus-app` client (`secret` secret) and add `alice` (`admin` and `user` roles) and `bob` (`user` role) users, where all of these properties can be customized. + +First prepare `application.properties`. You can start with a completely empty `application.properties` as `Dev Services for Keycloak` will register `quarkus.oidc.auth-server-url` pointing to the running test container as well as `quarkus.oidc.client-id=quarkus-app` and `quarkus.oidc.credentials.secret=secret`. + +But if you already have all the required `quarkus-oidc` properties configured then you only need to associate `quarkus.oidc.auth-server-url` with the `prod` profile for `Dev Services for Keycloak`to start a container, for example: + +[source,properties] +---- +%prod.quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/quarkus +---- + +If a custom realm file has to be imported into Keycloak before running the tests then you can configure `Dev Services for Keycloak` as follows: + +[source,properties] +---- +%prod.quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/quarkus +quarkus.keycloak.devservices.realm-path=quarkus-realm.json +---- + +Finally write a test code the same way as it is described in the <> section above. +The only difference is that `@QuarkusTestResource` is no longer needed: + +[source, java] +---- +@QuarkusTest +public class CodeFlowAuthorizationTest { +} +---- + [[integration-testing-keycloak]] -=== Keycloak +=== KeycloakTestResourceLifecycleManager -If you work with Keycloak then you can test against a live Keycloak instance by adding the following dependency: +If you need to do the integration testing against Keycloak then you are encouraged to do it with <>. +Use `KeycloakTestResourceLifecycleManager` for your tests only if there is a good reason not to use `Dev Services for Keycloak`. + +Start with adding the following dependency: [source,xml] ---- @@ -798,7 +834,9 @@ If you work with Keycloak then you can test against a live Keycloak instance by ---- -and configure `maven.surefire.plugin` as follows: +which provides `io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager` - an implementaion of `io.quarkus.test.common.QuarkusTestResourceLifecycleManager` which starts a Keycloak container. + +And configure `maven.surefire.plugin` as follows: [source,xml] ---- @@ -909,5 +947,5 @@ include::{generated-dir}/config/quarkus-oidc.adoc[opts=optional] * https://openid.net/connect/[OpenID Connect] * https://tools.ietf.org/html/rfc7519[JSON Web Token] * link:security-openid-connect-client[Quarkus - Using OpenID Connect and OAuth2 Client and Filters to manage access tokens] -* link:security-openid-connect-dev-services[Dev Services for OpenId Connect] +* link:security-openid-connect-dev-services[Dev Services for Keycloak] * link:security[Quarkus Security] diff --git a/docs/src/main/asciidoc/security-openid-connect.adoc b/docs/src/main/asciidoc/security-openid-connect.adoc index 38fac3a4ef2b5b..f86abda1dcf0f1 100644 --- a/docs/src/main/asciidoc/security-openid-connect.adoc +++ b/docs/src/main/asciidoc/security-openid-connect.adoc @@ -508,7 +508,7 @@ Note it is also recommended to use `quarkus.oidc.token.audience` property to ver Please see link:security-openid-connect-client#token-propagation[Token Propagation] section about the Bearer access token propagation to the downstream services. [[oidc-provider-authentication]] -=== Oidc Provider Client Authentication +== Oidc Provider Client Authentication `quarkus.oidc.runtime.OidcProviderClient` is used when a remote request to an OpenId Connect Provider has to be done. If the bearer token has to be introspected then `OidcProviderClient` has to authenticate to the OpenId Connect Provider. Please see link:security-openid-connect-web-authentication#oidc-provider-client-authentication[OidcProviderClient Authentication] for more information about all the supported authentication options. @@ -602,10 +602,98 @@ public class BearerTokenAuthorizationTest { Testing your `quarkus-oidc` `service` application with `OidcWiremockTestResource` provides the best coverage as even the communication channel is tested against the Wiremock HTTP stubs. `OidcWiremockTestResource` will be enhanced going forward to support more complex Bearer token test scenarios. +[[integration-testing-keycloak-devservices]] +=== Dev Services for Keycloak + +Using link:security-openid-connect-dev-services[Dev Services for Keycloak] is recommended for the integration testing against Keycloak. +`Dev Services for Keycloak` will launch and initialize a test container: it will create a `quarkus` realm, a `quarkus-app` client (`secret` secret) and add `alice` (`admin` and `user` roles) and `bob` (`user` role) users, where all of these properties can be customized. + +First you need to add the following dependency: + +[source,xml] +---- + + io.quarkus + quarkus-test-keycloak-server + test + +---- + +which provides a utility class `io.quarkus.test.keycloak.client.KeycloakTestClient` you can use in tests for acquiring the access tokens. + +Next prepare your `application.properties`. You can start with a completely empty `application.properties` as `Dev Services for Keycloak` will register `quarkus.oidc.auth-server-url` pointing to the running test container as well as `quarkus.oidc.client-id=quarkus-app` and `quarkus.oidc.credentials.secret=secret`. + +But if you already have all the required `quarkus-oidc` properties configured then you only need to associate `quarkus.oidc.auth-server-url` with the `prod` profile for `Dev Services for Keycloak`to start a container, for example: + +[source,properties] +---- +%prod.quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/quarkus +---- + +If a custom realm file has to be imported into Keycloak before running the tests then you can configure `Dev Services for Keycloak` as follows: + +[source,properties] +---- +%prod.quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/quarkus +quarkus.keycloak.devservices.realm-path=quarkus-realm.json +---- + +Finally write your test which will be executed in JVM mode: + +[source,java] +---- +package org.acme.security.openid.connect; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.keycloak.client.KeycloakTestClient; +import io.restassured.RestAssured; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class BearerTokenAuthenticationTest { + + KeycloakTestClient keycloakClient = new KeycloakTestClient(); + + @Test + public void testAdminAccess() { + RestAssured.given().auth().oauth2(getAccessToken("alice")) + .when().get("/api/admin") + .then() + .statusCode(200); + RestAssured.given().auth().oauth2(getAccessToken("bob")) + .when().get("/api/admin") + .then() + .statusCode(403); + } + + protected String getAccessToken(String userName) { + return keycloakClient.getAccessToken(userName); + } +} +---- + +and in native mode: + +[source,java] +---- +package org.acme.security.openid.connect; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class NativeBearerTokenAuthenticationIT extends BearerTokenAuthenticationTest { +} +---- + +Please see link:security-openid-connect-dev-services[Dev Services for Keycloak] for more information about the way it is initialized and configured. + [[integration-testing-keycloak]] -=== Keycloak +=== KeycloakTestResourceLifecycleManager + +If you need to do some integration testing against Keycloak then you are encouraged to do it with <>. +Use `KeycloakTestResourceLifecycleManager` for your tests only if there is a good reason not to use `Dev Services for Keycloak`. -If you work with Keycloak then you can test against a live Keycloak instance by adding the following dependency: +Start with adding the following dependency: [source,xml] ---- @@ -616,7 +704,9 @@ If you work with Keycloak then you can test against a live Keycloak instance by ---- -and configure `maven.surefire.plugin` as follows: +which provides `io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager` - an implementaion of `io.quarkus.test.common.QuarkusTestResourceLifecycleManager` which starts a Keycloak container. + +And configure the Maven Surefire plugin as follows: [source,xml] ---- @@ -917,5 +1007,5 @@ Note Quarkus `web-app` applications always require `quarkus.oidc.client-id` prop * https://openid.net/connect/[OpenID Connect] * https://tools.ietf.org/html/rfc7519[JSON Web Token] * link:security-openid-connect-client[Quarkus - Using OpenID Connect and OAuth2 Client and Filters to manage access tokens] -* link:security-openid-connect-dev-services[Dev Services for OpenId Connect] +* link:security-openid-connect-dev-services[Dev Services for Keycloak] * link:security[Quarkus Security] diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java index fe82852eafb169..5cb10d6bf605f9 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java @@ -40,15 +40,15 @@ public void testAccessAndRefreshTokenInjectionDevMode() throws IOException, Inte try { webClient.getPage("http://localhost:8080/protected"); - fail("Exception is expected because auth-server-url is not available and the authentication can not be completed"); + fail("Exception is expected because by default the bearer token is required"); } catch (FailingHttpStatusCodeException ex) { // Reported by Quarkus - assertEquals(500, ex.getStatusCode()); + assertEquals(401, ex.getStatusCode()); } - // Enable auth-server-url + // Enable 'web-app' application type test.modifyResourceFile("application.properties", - s -> s.replace("#quarkus.oidc.auth-server-url", "quarkus.oidc.auth-server-url")); + s -> s.replace("#quarkus.oidc.application-type=web-app", "quarkus.oidc.application-type=web-app")); HtmlPage page = webClient.getPage("http://localhost:8080/protected"); diff --git a/extensions/oidc/deployment/src/test/resources/application-dev-mode-default-tenant.properties b/extensions/oidc/deployment/src/test/resources/application-dev-mode-default-tenant.properties index 989e5ebc777c83..2853d435e676f6 100644 --- a/extensions/oidc/deployment/src/test/resources/application-dev-mode-default-tenant.properties +++ b/extensions/oidc/deployment/src/test/resources/application-dev-mode-default-tenant.properties @@ -1,9 +1,6 @@ -#quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus quarkus.oidc.client-id=quarkus-web-app quarkus.oidc.credentials.secret=secret -quarkus.oidc.application-type=web-app - -quarkus.keycloak.devservices.enabled=false +#quarkus.oidc.application-type=web-app quarkus.log.category."com.gargoylesoftware.htmlunit.javascript.host.css.CSSStyleSheet".level=FATAL diff --git a/integration-tests/oidc-token-propagation/src/test/java/io/quarkus/it/keycloak/OidcTokenPropagationTest.java b/integration-tests/oidc-token-propagation/src/test/java/io/quarkus/it/keycloak/OidcTokenPropagationTest.java index 758e65299401cc..1f025d7793c838 100644 --- a/integration-tests/oidc-token-propagation/src/test/java/io/quarkus/it/keycloak/OidcTokenPropagationTest.java +++ b/integration-tests/oidc-token-propagation/src/test/java/io/quarkus/it/keycloak/OidcTokenPropagationTest.java @@ -7,7 +7,7 @@ import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.keycloak.server.KeycloakTestClient; +import io.quarkus.test.keycloak.client.KeycloakTestClient; import io.restassured.RestAssured; @QuarkusTest diff --git a/test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/server/KeycloakTestClient.java b/test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/client/KeycloakTestClient.java similarity index 92% rename from test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/server/KeycloakTestClient.java rename to test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/client/KeycloakTestClient.java index f1120c07776b86..5c551dd34c4c05 100644 --- a/test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/server/KeycloakTestClient.java +++ b/test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/client/KeycloakTestClient.java @@ -1,11 +1,10 @@ -package io.quarkus.test.keycloak.server; +package io.quarkus.test.keycloak.client; import org.eclipse.microprofile.config.ConfigProvider; import org.keycloak.representations.AccessTokenResponse; import io.quarkus.runtime.configuration.ConfigurationException; import io.quarkus.test.common.DevServicesContext; -import io.quarkus.test.junit.QuarkusIntegrationTest; import io.restassured.RestAssured; public class KeycloakTestClient implements DevServicesContext.ContextAware { @@ -24,10 +23,6 @@ public KeycloakTestClient() { } - public KeycloakTestClient(QuarkusIntegrationTest.Context testContext) { - this.testContext = testContext; - } - public String getAccessToken(String userName) { return getAccessToken(userName, getClientId()); } diff --git a/test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/server/KeycloakTestResourceLifecycleManager.java b/test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/server/KeycloakTestResourceLifecycleManager.java index f64f8cfac0c3b1..dd8c8eb963c436 100644 --- a/test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/server/KeycloakTestResourceLifecycleManager.java +++ b/test-framework/keycloak-server/src/main/java/io/quarkus/test/keycloak/server/KeycloakTestResourceLifecycleManager.java @@ -95,6 +95,7 @@ public Map start() { Map conf = new HashMap<>(); conf.put("keycloak.url", KEYCLOAK_SERVER_URL); + conf.put("quarkus.oidc.auth-server-url", KEYCLOAK_SERVER_URL + "/realms/" + KEYCLOAK_REALM); return conf; } From 5db60deb47613729b97453025cd4af7a0cbfe0c9 Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Sat, 28 Aug 2021 23:06:51 +0200 Subject: [PATCH 004/138] Fix wrong TestInfo test method in @Before/AfterEach Fixes #19747 --- .../it/main/QuarkusTestCallbacksTestCase.java | 35 ++++++++++++++++--- ...leAnnotationCheckerBeforeEachCallback.java | 3 ++ .../test/junit/QuarkusTestExtension.java | 25 ++++++++----- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java index 404630cb8cec56..af121205b21c2a 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java @@ -3,11 +3,16 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.lang.reflect.Method; -import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; @@ -22,11 +27,29 @@ @QuarkusTest public class QuarkusTestCallbacksTestCase { + @BeforeEach + void beforeEachWithTestInfo(TestInfo testInfo) throws NoSuchMethodException { + checkBeforeOrAfterEachTestInfo(testInfo, "beforeEachWithTestInfo"); + } + + @AfterEach + void afterEachWithTestInfo(TestInfo testInfo) throws NoSuchMethodException { + checkBeforeOrAfterEachTestInfo(testInfo, "afterEachWithTestInfo"); + } + + private void checkBeforeOrAfterEachTestInfo(TestInfo testInfo, String unexpectedMethodName) throws NoSuchMethodException { + assertNotNull(testInfo); + String testMethodName = testInfo.getTestMethod().get().getName(); + assertNotEquals(testMethodName, + QuarkusTestCallbacksTestCase.class.getDeclaredMethod(unexpectedMethodName, TestInfo.class)); + assertTrue(testMethodName.startsWith("test")); + } + @Test @TestAnnotation @Order(1) public void testTestMethodHasAnnotation() { - + assertTrue(SimpleAnnotationCheckerBeforeEachCallback.testAnnotationChecked); } @Test @@ -36,11 +59,13 @@ public void testBeforeClass() { } @Test + @TestAnnotation @Order(3) public void testInfoTestCase(TestInfo testInfo) throws NoSuchMethodException { - Assertions.assertEquals(testInfo.getTestClass().get(), QuarkusTestCallbacksTestCase.class); - Assertions.assertEquals(testInfo.getTestMethod().get(), - QuarkusTestCallbacksTestCase.class.getDeclaredMethod("testInfoTestCase", TestInfo.class)); + assertEquals(testInfo.getTestClass().get(), QuarkusTestCallbacksTestCase.class); + Method testMethod = testInfo.getTestMethod().get(); + assertEquals(testMethod, QuarkusTestCallbacksTestCase.class.getDeclaredMethod("testInfoTestCase", TestInfo.class)); + assertEquals(1, testMethod.getAnnotationsByType(TestAnnotation.class).length); } @Target({ METHOD }) diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java b/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java index 85927763f10852..8e9447609046b7 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java @@ -7,6 +7,8 @@ public class SimpleAnnotationCheckerBeforeEachCallback implements QuarkusTestBeforeEachCallback { + static boolean testAnnotationChecked; + @Override public void beforeEach(QuarkusTestMethodContext context) { // make sure that this comes into play only for the test we care about @@ -26,5 +28,6 @@ public void beforeEach(QuarkusTestMethodContext context) { throw new IllegalStateException( "Expected to find annotation @TestAnnotation on method test method testTestMethodHasAnnotation"); } + testAnnotationChecked = true; } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java index 6a4574d813a6da..deb9fc5f555409 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -196,6 +197,8 @@ public void run() { } }; + private final IdentityHashMap tcclMethodCache = new IdentityHashMap<>(); + static { ClassLoader classLoader = QuarkusTestExtension.class.getClassLoader(); if (classLoader instanceof QuarkusClassLoader) { @@ -430,6 +433,7 @@ public void close() throws IOException { } tm.close(); } finally { + tcclMethodCache.clear(); GroovyCacheCleaner.clearGroovyCache(); if (hangTaskKey != null) { hangTaskKey.cancel(true); @@ -988,13 +992,13 @@ private Object runExtensionMethod(ReflectiveInvocationContext invocation try { Class testClassFromTCCL = Class.forName(extensionContext.getRequiredTestClass().getName(), true, Thread.currentThread().getContextClassLoader()); - Method newMethod = determineTCCLExtensionMethod(invocationContext, testClassFromTCCL); + Method newMethod = determineTCCLExtensionMethod(invocationContext.getExecutable(), testClassFromTCCL); boolean methodFromEnclosing = false; // this is needed to support before*** and after*** methods that are part of class that encloses the test class // (the test class is in this case a @Nested test) if ((newMethod == null) && (testClassFromTCCL.getEnclosingClass() != null)) { testClassFromTCCL = testClassFromTCCL.getEnclosingClass(); - newMethod = determineTCCLExtensionMethod(invocationContext, testClassFromTCCL); + newMethod = determineTCCLExtensionMethod(invocationContext.getExecutable(), testClassFromTCCL); methodFromEnclosing = true; } if (newMethod == null) { @@ -1035,8 +1039,9 @@ private Object runExtensionMethod(ReflectiveInvocationContext invocation cloneRequired = false; } else if (TestInfo.class.isAssignableFrom(theclass)) { TestInfo info = (TestInfo) arg; + Method newTestMethod = determineTCCLExtensionMethod(info.getTestMethod().get(), testClassFromTCCL); replacement = new TestInfoImpl(info.getDisplayName(), info.getTags(), Optional.of(testClassFromTCCL), - Optional.of(newMethod)); + Optional.of(newTestMethod)); } else if (clonePattern.matcher(className).matches()) { cloneRequired = true; } else { @@ -1089,13 +1094,16 @@ private Object runExtensionMethod(ReflectiveInvocationContext invocation } } - private Method determineTCCLExtensionMethod(ReflectiveInvocationContext invocationContext, Class c) + private Method determineTCCLExtensionMethod(Method originalMethod, Class c) throws ClassNotFoundException { - Method newMethod = null; + Method newMethod = tcclMethodCache.get(originalMethod); + if (newMethod != null) { + return newMethod; + } while (c != Object.class) { - if (c.getName().equals(invocationContext.getExecutable().getDeclaringClass().getName())) { + if (c.getName().equals(originalMethod.getDeclaringClass().getName())) { try { - Class[] originalParameterTypes = invocationContext.getExecutable().getParameterTypes(); + Class[] originalParameterTypes = originalMethod.getParameterTypes(); List> parameterTypesFromTccl = new ArrayList<>(originalParameterTypes.length); for (Class type : originalParameterTypes) { if (type.isPrimitive()) { @@ -1106,7 +1114,7 @@ private Method determineTCCLExtensionMethod(ReflectiveInvocationContext Thread.currentThread().getContextClassLoader())); } } - newMethod = c.getDeclaredMethod(invocationContext.getExecutable().getName(), + newMethod = c.getDeclaredMethod(originalMethod.getName(), parameterTypesFromTccl.toArray(new Class[0])); break; } catch (NoSuchMethodException ignored) { @@ -1115,6 +1123,7 @@ private Method determineTCCLExtensionMethod(ReflectiveInvocationContext } c = c.getSuperclass(); } + tcclMethodCache.put(originalMethod, newMethod); return newMethod; } From 870c730dcd7f08b899dba6611c1fce1d607b9356 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 30 Aug 2021 14:16:12 +0300 Subject: [PATCH 005/138] Provide actionable error message when RESTEasy can't pick Provider constructor This can happen when a provider is registered via a Dynamic Feature. Unfortunately the error message can not contain the class which causes the problem because it is not passed into the method, the class itself is not present in fields which can be accessed at this point and the StackWalker does not have access to parameters Closes: #19756 --- .../resteasy/common/runtime/QuarkusInjectorFactory.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java index cc4bf24082679e..695c001f11b966 100644 --- a/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java +++ b/extensions/resteasy-classic/resteasy-common/runtime/src/main/java/io/quarkus/resteasy/common/runtime/QuarkusInjectorFactory.java @@ -28,6 +28,10 @@ public class QuarkusInjectorFactory extends InjectorFactoryImpl { @SuppressWarnings("rawtypes") @Override public ConstructorInjector createConstructor(Constructor constructor, ResteasyProviderFactory providerFactory) { + if (constructor == null) { + throw new IllegalStateException( + "Unable to locate proper constructor for dynamically registered provider. Make sure the class has a no-args constructor and that it uses '@Context' for field injection if necessary."); + } log.debugf("Create constructor: %s", constructor); return new QuarkusConstructorInjector(constructor, super.createConstructor(constructor, providerFactory)); } From 2e6fb151707164ce2f44b0d202d70baf7992ba59 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Mon, 30 Aug 2021 13:43:27 +0200 Subject: [PATCH 006/138] fix issue in dev ui logstream that register listener on all select elements. Signed-off-by:Phillip Kruger --- .../deployment/src/main/resources/dev-static/js/logstream.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-static/js/logstream.js b/extensions/vertx-http/deployment/src/main/resources/dev-static/js/logstream.js index aa3e4cd1f74f29..86fcb0883c56d6 100644 --- a/extensions/vertx-http/deployment/src/main/resources/dev-static/js/logstream.js +++ b/extensions/vertx-http/deployment/src/main/resources/dev-static/js/logstream.js @@ -736,7 +736,7 @@ function populateLoggerLevelModal(loggerNamesArray, levelNamesArray){ tbodyLevels.append(row); } - $('select').on('change', function() { + $('.logleveldropdown').on('change', function() { changeLogLevel(this.value, $(this).find('option:selected').text()); }); @@ -764,7 +764,7 @@ function getTextClass(level){ function createDropdown(name, level, levelNamesArray){ - var dd = ""; // Populate the dropdown for (var i = 0; i < levelNamesArray.length; i++) { var selected = ""; From b0fbae4f50393344d532cc948cf7cfc27bd22256 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 30 Aug 2021 15:38:33 +0200 Subject: [PATCH 007/138] Remove two classes that has been moved to test-framework/artemis --- .../it/artemis/ArtemisTestResource.java | 39 ------------------- .../it/artemis/ArtemisTestResource.java | 39 ------------------- 2 files changed, 78 deletions(-) delete mode 100644 integration-tests/artemis-core/src/test/java/io/quarkus/it/artemis/ArtemisTestResource.java delete mode 100644 integration-tests/artemis-jms/src/test/java/io/quarkus/it/artemis/ArtemisTestResource.java diff --git a/integration-tests/artemis-core/src/test/java/io/quarkus/it/artemis/ArtemisTestResource.java b/integration-tests/artemis-core/src/test/java/io/quarkus/it/artemis/ArtemisTestResource.java deleted file mode 100644 index 822bd492209d15..00000000000000 --- a/integration-tests/artemis-core/src/test/java/io/quarkus/it/artemis/ArtemisTestResource.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.quarkus.it.artemis; - -import java.nio.file.Paths; -import java.util.Collections; -import java.util.Map; - -import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; -import org.apache.commons.io.FileUtils; - -import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; - -public class ArtemisTestResource implements QuarkusTestResourceLifecycleManager { - - private EmbeddedActiveMQ embedded; - - @Override - public Map start() { - try { - FileUtils.deleteDirectory(Paths.get("./target/artemis").toFile()); - embedded = new EmbeddedActiveMQ(); - embedded.start(); - } catch (Exception e) { - throw new RuntimeException("Could not start embedded ActiveMQ server", e); - } - return Collections.emptyMap(); - } - - @Override - public void stop() { - if (embedded == null) { - return; - } - try { - embedded.stop(); - } catch (Exception e) { - throw new RuntimeException("Could not stop embedded ActiveMQ server", e); - } - } -} diff --git a/integration-tests/artemis-jms/src/test/java/io/quarkus/it/artemis/ArtemisTestResource.java b/integration-tests/artemis-jms/src/test/java/io/quarkus/it/artemis/ArtemisTestResource.java deleted file mode 100644 index 822bd492209d15..00000000000000 --- a/integration-tests/artemis-jms/src/test/java/io/quarkus/it/artemis/ArtemisTestResource.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.quarkus.it.artemis; - -import java.nio.file.Paths; -import java.util.Collections; -import java.util.Map; - -import org.apache.activemq.artemis.core.server.embedded.EmbeddedActiveMQ; -import org.apache.commons.io.FileUtils; - -import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; - -public class ArtemisTestResource implements QuarkusTestResourceLifecycleManager { - - private EmbeddedActiveMQ embedded; - - @Override - public Map start() { - try { - FileUtils.deleteDirectory(Paths.get("./target/artemis").toFile()); - embedded = new EmbeddedActiveMQ(); - embedded.start(); - } catch (Exception e) { - throw new RuntimeException("Could not start embedded ActiveMQ server", e); - } - return Collections.emptyMap(); - } - - @Override - public void stop() { - if (embedded == null) { - return; - } - try { - embedded.stop(); - } catch (Exception e) { - throw new RuntimeException("Could not stop embedded ActiveMQ server", e); - } - } -} From 979d0c7730d77216309328249c671e24f2d708c1 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Mon, 30 Aug 2021 15:38:39 +0200 Subject: [PATCH 008/138] Show Dev UI Tile even if there is no Guide. Signed-off-by:Phillip Kruger --- .../vertx/http/deployment/devmode/console/DevConsole.java | 4 +++- .../resources/dev-templates/tags/nonActionableExtension.html | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsole.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsole.java index e251597a536a1f..b7942c1ddd55bb 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsole.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/DevConsole.java @@ -175,9 +175,11 @@ public void sendMainPage(RoutingContext event) { Template simpleTemplate = engine.getTemplate(namespace + "/embedded.html"); boolean hasConsoleEntry = simpleTemplate != null; boolean hasGuide = metadata.containsKey("guide"); + boolean isUnlisted = metadata.containsKey("unlisted") + && (metadata.get("unlisted").equals(true) || metadata.get("unlisted").equals("true")); loaded.put("hasConsoleEntry", hasConsoleEntry); loaded.put("hasGuide", hasGuide); - if (hasConsoleEntry || hasGuide) { + if (!isUnlisted) { if (hasConsoleEntry) { Map data = new HashMap<>(); data.putAll(globalData); diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/tags/nonActionableExtension.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/tags/nonActionableExtension.html index 46f101d8d54260..739970bd2bca7b 100644 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/tags/nonActionableExtension.html +++ b/extensions/vertx-http/deployment/src/main/resources/dev-templates/tags/nonActionableExtension.html @@ -1,4 +1,3 @@ -{#if it.metadata?? && !it.metadata.unlisted??}
@@ -31,5 +30,4 @@
{/if}
-
-{/if} \ No newline at end of file + \ No newline at end of file From e419671f069e69baec3227d87c218b68d702d2c1 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 30 Aug 2021 17:02:38 +0300 Subject: [PATCH 009/138] Use the proper config values when pulling base image for native build using Jib Fixes: #19771 --- .../container/image/jib/deployment/JibProcessor.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java index 83e01cdd95028e..5b6dd138834425 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java @@ -161,7 +161,7 @@ public void buildFromNative(ContainerImageConfig containerImageConfig, JibConfig "The native binary produced by the build is not a Linux binary and therefore cannot be used in a Linux container image. Consider adding \"quarkus.native.container-build=true\" to your configuration"); } - JibContainerBuilder jibContainerBuilder = createContainerBuilderFromNative(containerImageConfig, jibConfig, + JibContainerBuilder jibContainerBuilder = createContainerBuilderFromNative(jibConfig, nativeImage, containerImageLabels); setUser(jibConfig, jibContainerBuilder); setPlatforms(jibConfig, jibContainerBuilder); @@ -517,7 +517,7 @@ private JibContainerBuilder createContainerBuilderFromLegacyJar(JibConfig jibCon } } - private JibContainerBuilder createContainerBuilderFromNative(ContainerImageConfig containerImageConfig, JibConfig jibConfig, + private JibContainerBuilder createContainerBuilderFromNative(JibConfig jibConfig, NativeImageBuildItem nativeImageBuildItem, List containerImageLabels) { List entrypoint; @@ -532,8 +532,8 @@ private JibContainerBuilder createContainerBuilderFromNative(ContainerImageConfi try { AbsoluteUnixPath workDirInContainer = AbsoluteUnixPath.get("/work"); JibContainerBuilder jibContainerBuilder = Jib - .from(toRegistryImage(ImageReference.parse(jibConfig.baseNativeImage), containerImageConfig.username, - containerImageConfig.password)) + .from(toRegistryImage(ImageReference.parse(jibConfig.baseNativeImage), jibConfig.baseRegistryUsername, + jibConfig.baseRegistryPassword)) .addFileEntriesLayer(FileEntriesLayer.builder() .addEntry(nativeImageBuildItem.getPath(), workDirInContainer.resolve(BINARY_NAME_IN_CONTAINER), FilePermissions.fromOctalString("775")) From 7d1a76a63b155714956b8f6b5b36bb69da8194cf Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 30 Aug 2021 09:30:26 +0300 Subject: [PATCH 010/138] Use testcontainers (if applicable) to determine if Docker working Fixes: #19741 --- .../quarkus/deployment/IsDockerWorking.java | 185 +++++++++++++----- 1 file changed, 131 insertions(+), 54 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java b/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java index 1fce4a2260d01d..038cb3ea28ebbc 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java @@ -6,11 +6,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; +import java.util.List; import java.util.function.BooleanSupplier; import java.util.function.Function; +import java.util.function.Supplier; import org.jboss.logging.Logger; @@ -20,90 +23,164 @@ public class IsDockerWorking implements BooleanSupplier { private static final Logger LOGGER = Logger.getLogger(IsDockerWorking.class.getName()); - private final boolean silent; + private final List strategies; public IsDockerWorking() { this(false); } public IsDockerWorking(boolean silent) { - this.silent = silent; + this.strategies = List.of(new TestContainersStrategy(silent), new DockerHostStrategy(), + new DockerBinaryStrategy(silent)); } @Override public boolean getAsBoolean() { - //remote docker detection - //we don't want to pull in the docker API here - //so we just see if the DOCKER_HOST is set and we can connect to it - //we can't actually verify it is docker listening on the other end - String dockerHost = System.getenv("DOCKER_HOST"); - if (dockerHost != null && !dockerHost.startsWith("unix:")) { - try { - URI url = new URI(dockerHost); - try (Socket s = new Socket(url.getHost(), url.getPort())) { - return true; - } catch (IOException e) { - LOGGER.warnf("Unable to connect to DOCKER_HOST URI %s, make sure docker is running on the specified host", - dockerHost); - } - } catch (URISyntaxException e) { - LOGGER.warnf("Unable to parse DOCKER_HOST URI %s, it will be ignored for working docker detection", dockerHost); + for (Strategy strategy : strategies) { + Result result = strategy.get(); + if (result == Result.AVAILABLE) { + return true; } } + return false; + } - try { - if (!ExecUtil.execSilent("docker", "-v")) { - LOGGER.warn("'docker -v' returned an error code. Make sure your Docker binary is correct"); - return false; - } - } catch (Exception e) { - LOGGER.warnf("No Docker binary found or general error: %s", e); - return false; + public static class IsDockerRunningSilent extends IsDockerWorking { + public IsDockerRunningSilent() { + super(true); } + } - try { - OutputFilter filter = new OutputFilter(); - if (ExecUtil.exec(new File("."), filter, "docker", "version", "--format", "'{{.Server.Version}}'")) { - LOGGER.debugf("Docker daemon found. Version: %s", filter.getOutput()); - return true; - } else { + private interface Strategy extends Supplier { + + } + + /** + * Delegates the check to testcontainers (if the latter is on the classpath) + */ + private static class TestContainersStrategy implements Strategy { + + private final boolean silent; + + private TestContainersStrategy(boolean silent) { + this.silent = silent; + } + + @Override + public Result get() { + try { + Class dockerClientFactoryClass = Thread.currentThread().getContextClassLoader() + .loadClass("org.testcontainers.DockerClientFactory"); + Object dockerClientFactoryInstance = dockerClientFactoryClass.getMethod("instance").invoke(null); + boolean isAvailable = (boolean) dockerClientFactoryClass.getMethod("isDockerAvailable") + .invoke(dockerClientFactoryInstance); + return isAvailable ? Result.AVAILABLE : Result.UNAVAILABLE; + } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { if (!silent) { - LOGGER.warn("Could not determine version of Docker daemon"); + LOGGER.debug("Unable to use testcontainers to determine if Docker is working", e); + } + return Result.UNKNOWN; + } + } + } + + /** + * Detection using a remote host socket + * We don't want to pull in the docker API here, so we just see if the DOCKER_HOST is set + * and if we can connect to it. + * We can't actually verify it is docker listening on the other end. + * Furthermore, this does not support Unix Sockets + */ + private static class DockerHostStrategy implements Strategy { + + @Override + public Result get() { + + String dockerHost = System.getenv("DOCKER_HOST"); + if (dockerHost != null && !dockerHost.startsWith("unix:")) { + try { + URI url = new URI(dockerHost); + try (Socket s = new Socket(url.getHost(), url.getPort())) { + return Result.AVAILABLE; + } catch (IOException e) { + LOGGER.warnf( + "Unable to connect to DOCKER_HOST URI %s, make sure docker is running on the specified host", + dockerHost); + } + } catch (URISyntaxException e) { + LOGGER.warnf("Unable to parse DOCKER_HOST URI %s, it will be ignored for working docker detection", + dockerHost); } - return false; } - } catch (Exception e) { - LOGGER.warn("Unexpected error occurred while determining Docker daemon version", e); - return false; + return Result.UNKNOWN; } } - public static class OutputFilter implements Function { - private final StringBuilder builder = new StringBuilder(); + private static class DockerBinaryStrategy implements Strategy { + + private final boolean silent; + + private DockerBinaryStrategy(boolean silent) { + this.silent = silent; + } @Override - public Runnable apply(InputStream is) { - return () -> { - try (InputStreamReader isr = new InputStreamReader(is); - BufferedReader reader = new BufferedReader(isr)) { + public Result get() { + try { + if (!ExecUtil.execSilent("docker", "-v")) { + LOGGER.warn("'docker -v' returned an error code. Make sure your Docker binary is correct"); + return Result.UNKNOWN; + } + } catch (Exception e) { + LOGGER.warnf("No Docker binary found or general error: %s", e); + return Result.UNKNOWN; + } - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - builder.append(line); + try { + OutputFilter filter = new OutputFilter(); + if (ExecUtil.exec(new File("."), filter, "docker", "version", "--format", "'{{.Server.Version}}'")) { + LOGGER.debugf("Docker daemon found. Version: %s", filter.getOutput()); + return Result.AVAILABLE; + } else { + if (!silent) { + LOGGER.warn("Could not determine version of Docker daemon"); } - } catch (IOException e) { - throw new RuntimeException("Error reading stream.", e); + return Result.UNAVAILABLE; } - }; + } catch (Exception e) { + LOGGER.warn("Unexpected error occurred while determining Docker daemon version", e); + return Result.UNKNOWN; + } } - public String getOutput() { - return builder.toString(); + public static class OutputFilter implements Function { + private final StringBuilder builder = new StringBuilder(); + + @Override + public Runnable apply(InputStream is) { + return () -> { + + try (InputStreamReader isr = new InputStreamReader(is); + BufferedReader reader = new BufferedReader(isr)) { + + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + builder.append(line); + } + } catch (IOException e) { + throw new RuntimeException("Error reading stream.", e); + } + }; + } + + public String getOutput() { + return builder.toString(); + } } } - public static class IsDockerRunningSilent extends IsDockerWorking { - public IsDockerRunningSilent() { - super(true); - } + private enum Result { + AVAILABLE, + UNAVAILABLE, + UNKNOWN } } From 95c6533df0d906a56ef23bae7fe4271f2cf5762e Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Mon, 30 Aug 2021 18:46:51 +0200 Subject: [PATCH 011/138] Some Swagger UI Screenshots updates in the Guides Signed-off-by:Phillip Kruger --- .../openapi-swaggerui-guide-screenshot01.png | Bin 64406 -> 67946 bytes .../openapi-swaggerui-guide-screenshot02.png | Bin 75716 -> 78515 bytes .../reactive-routes-guide-screenshot01.png | Bin 37923 -> 49345 bytes .../images/spring-web-guide-screenshot01.png | Bin 54268 -> 64063 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/src/main/asciidoc/images/openapi-swaggerui-guide-screenshot01.png b/docs/src/main/asciidoc/images/openapi-swaggerui-guide-screenshot01.png index 41e2f61e433fa9401ebbe2e6f72fa8f86211b67c..1b590c7e847f8d111b36622e293b5c4360cbd27b 100644 GIT binary patch literal 67946 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 literal 64406 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Û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∋%^T&G&HvtzQvu&c=KV! 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

?3P}5d=poy*U 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

X6S5DAiS)9Lx%WeC0 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 XDC1>{^4E=2mLUxFO@FNgJ$(Kj4X4H5 literal 75716 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 diff --git a/docs/src/main/asciidoc/images/spring-web-guide-screenshot01.png b/docs/src/main/asciidoc/images/spring-web-guide-screenshot01.png index 32a107b416a1a737c533269ed50cf501cb3c2c4e..13e131ca758aa206aaa47fff518b9873134afa3c 100644 GIT binary patch literal 64063 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�+?VuUjJ+z5ApsJTocmeM*{{4WY1zYdiQ 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�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 C5kyA- literal 54268 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

  • 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 From 3b9ef1d1bc5cab301441aac704f1c9df75ac23d2 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 30 Aug 2021 20:20:55 +0200 Subject: [PATCH 012/138] Upgrade build-reporting-maven-extension to 1.0.4 Fixes module path relativization for incremental builds. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b5d22660f27966..73c52d788391e8 100644 --- a/pom.xml +++ b/pom.xml @@ -138,7 +138,7 @@ io.quarkus.bot build-reporting-maven-extension - 1.0.3 + 1.0.4 From 913d86abf28253fa5ced412d00e4b2288a952367 Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Mon, 30 Aug 2021 21:54:14 +0200 Subject: [PATCH 013/138] Add timestamp to each StepTiming message --- .../main/java/io/quarkus/runtime/util/StepTiming.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/util/StepTiming.java b/core/runtime/src/main/java/io/quarkus/runtime/util/StepTiming.java index 7b73ecb03c20a6..7d4f4b5c65d221 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/util/StepTiming.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/util/StepTiming.java @@ -1,5 +1,9 @@ package io.quarkus.runtime.util; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; + import io.quarkus.runtime.StartupContext; public class StepTiming { @@ -23,8 +27,10 @@ public static void printStepTime(StartupContext startupContext) { } long stepTimingStop = System.currentTimeMillis(); String currentBuildStepName = startupContext.getCurrentBuildStepName(); - System.out - .println("Build step " + currentBuildStepName + " completed in: " + (stepTimingStop - stepTimingStart) + "ms"); + System.out.printf("%1$tF %1$tT,%1$tL Build step %2$s completed in: %3$sms%n", + LocalDateTime.ofInstant(Instant.ofEpochMilli(stepTimingStop), ZoneId.systemDefault()), + currentBuildStepName, + stepTimingStop - stepTimingStart); stepTimingStart = System.currentTimeMillis(); } } From 9c553639dd19035c4936c4f9ec89be16dac78d6f Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Fri, 27 Aug 2021 11:55:08 +1000 Subject: [PATCH 014/138] Fix "Response head already sent" Fixes #19621 --- .../http/runtime/QuarkusErrorHandler.java | 82 +++++++++++-------- .../web/mutiny/NdjsonMultiRouteTest.java | 2 +- .../vertx/web/mutiny/SSEMultiRouteTest.java | 2 +- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java index 04fae63b0f3965..6484de2a3f34d8 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/QuarkusErrorHandler.java @@ -41,40 +41,49 @@ public QuarkusErrorHandler(boolean showStack) { @Override public void handle(RoutingContext event) { - if (event.failure() == null) { - event.response().setStatusCode(event.statusCode()); - event.response().end(); - return; - } - //this can happen if there is no auth mechanisms - if (event.failure() instanceof UnauthorizedException) { - HttpAuthenticator authenticator = event.get(HttpAuthenticator.class.getName()); - if (authenticator != null) { - authenticator.sendChallenge(event).subscribe().with(new Consumer() { - @Override - public void accept(Boolean aBoolean) { - event.response().end(); - } - }, new Consumer() { - @Override - public void accept(Throwable throwable) { - event.fail(throwable); - } - }); - } else { + try { + if (event.failure() == null) { + event.response().setStatusCode(event.statusCode()); + event.response().end(); + return; + } + //this can happen if there is no auth mechanisms + if (event.failure() instanceof UnauthorizedException) { + HttpAuthenticator authenticator = event.get(HttpAuthenticator.class.getName()); + if (authenticator != null) { + authenticator.sendChallenge(event).subscribe().with(new Consumer() { + @Override + public void accept(Boolean aBoolean) { + event.response().end(); + } + }, new Consumer() { + @Override + public void accept(Throwable throwable) { + event.fail(throwable); + } + }); + } else { + event.response().setStatusCode(HttpResponseStatus.UNAUTHORIZED.code()).end(); + } + return; + } + if (event.failure() instanceof ForbiddenException) { + event.response().setStatusCode(HttpResponseStatus.FORBIDDEN.code()).end(); + return; + } + if (event.failure() instanceof AuthenticationFailedException) { + //generally this should be handled elsewhere + //but if we get to this point bad things have happened + //so it is better to send a response than to hang event.response().setStatusCode(HttpResponseStatus.UNAUTHORIZED.code()).end(); + return; + } + } catch (IllegalStateException e) { + //ignore this if the response is already started + if (!event.response().ended()) { + //could be that just the head is committed + event.response().end(); } - return; - } - if (event.failure() instanceof ForbiddenException) { - event.response().setStatusCode(HttpResponseStatus.FORBIDDEN.code()).end(); - return; - } - if (event.failure() instanceof AuthenticationFailedException) { - //generally this should be handled elsewhere - //but if we get to this point bad things have happened - //so it is better to send a response than to hang - event.response().setStatusCode(HttpResponseStatus.UNAUTHORIZED.code()).end(); return; } @@ -100,6 +109,15 @@ public void accept(Throwable throwable) { } else { log.errorf(exception, "HTTP Request to %s failed, error id: %s", event.request().uri(), uuid); } + //we have logged the error + //now lets see if we can actually send a response + //if not we just return + if (event.response().ended()) { + return; + } else if (event.response().headWritten()) { + event.response().end(); + return; + } String accept = event.request().getHeader("Accept"); if (accept != null && accept.contains("application/json")) { event.response().headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=utf-8"); diff --git a/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/mutiny/NdjsonMultiRouteTest.java b/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/mutiny/NdjsonMultiRouteTest.java index 97e31d960907cc..8c2d652896705c 100644 --- a/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/mutiny/NdjsonMultiRouteTest.java +++ b/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/mutiny/NdjsonMultiRouteTest.java @@ -47,7 +47,7 @@ public void testNdjsonMultiRoute() { // We get the item followed by the exception when().get("/hello-and-fail").then().statusCode(200) .body(containsString("\"Hello\"")) - .body(containsString("boom")); + .body(not(containsString("boom"))); when().get("/void").then().statusCode(204).body(hasLength(0)); diff --git a/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/mutiny/SSEMultiRouteTest.java b/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/mutiny/SSEMultiRouteTest.java index 6c998dbf1a1067..3dd1656631f1ef 100644 --- a/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/mutiny/SSEMultiRouteTest.java +++ b/extensions/vertx-web/deployment/src/test/java/io/quarkus/vertx/web/mutiny/SSEMultiRouteTest.java @@ -44,7 +44,7 @@ public void testSSEMultiRoute() { // We get the item followed by the exception when().get("/hello-and-fail").then().statusCode(200) .body(containsString("id: 0")) - .body(containsString("boom")); + .body(not(containsString("boom"))); when().get("/buffer").then().statusCode(200) .body(is("data: Buffer\nid: 0\n\n")) From 4af2bf89df3a8623d610b0389586ba5cdbf3b703 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 21:06:57 +0000 Subject: [PATCH 015/138] Bump awssdk.version from 2.17.29 to 2.17.30 Bumps `awssdk.version` from 2.17.29 to 2.17.30. Updates `software.amazon.awssdk:bom` from 2.17.29 to 2.17.30 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.29...2.17.30) Updates `apache-client` from 2.17.29 to 2.17.30 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:apache-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index f1c39ced2103be..5a28a7cab7d310 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -145,7 +145,7 @@ 3.10.0 1.6 2.9.1 - 2.17.29 + 2.17.30 2.40.0 1.4.2 1.5.30 From 25ab92601ba13d2fe083d48578028e69d4ec2bc8 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Tue, 31 Aug 2021 09:47:43 +1000 Subject: [PATCH 016/138] Update Dev UI with the correct state when resuming Fixes #19777 --- ...ntinuousTestingWebSocketTestListener.java} | 40 ++++++++++++------- .../devmode/tests/TestsProcessor.java | 4 +- 2 files changed, 28 insertions(+), 16 deletions(-) rename extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/{ContinuousTestingWebSocketListener.java => ContinuousTestingWebSocketTestListener.java} (56%) diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketListener.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketTestListener.java similarity index 56% rename from extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketListener.java rename to extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketTestListener.java index 703dfe9514f448..2d83492a8186b3 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketListener.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketTestListener.java @@ -11,7 +11,9 @@ import io.quarkus.deployment.dev.testing.TestRunResults; import io.quarkus.dev.testing.ContinuousTestingWebsocketListener; -public class ContinuousTestingWebSocketListener implements TestListener { +public class ContinuousTestingWebSocketTestListener implements TestListener { + + private volatile ContinuousTestingWebsocketListener.State lastState; @Override public void listenerRegistered(TestController testController) { @@ -20,9 +22,18 @@ public void listenerRegistered(TestController testController) { @Override public void testsEnabled() { - ContinuousTestingWebsocketListener - .setLastState( - new ContinuousTestingWebsocketListener.State(true, true, 0L, 0L, 0L, 0L, false, false, false, true)); + if (lastState == null) { + ContinuousTestingWebsocketListener + .setLastState(new ContinuousTestingWebsocketListener.State(true, false, + 0, 0, 0, 0, + ContinuousTestingWebsocketListener.getLastState().isBrokenOnly, + ContinuousTestingWebsocketListener.getLastState().isTestOutput, + ContinuousTestingWebsocketListener.getLastState().isInstrumentationBasedReload, + ContinuousTestingWebsocketListener.getLastState().isLiveReload)); + } else { + ContinuousTestingWebsocketListener + .setLastState(lastState); + } } @Override @@ -46,17 +57,18 @@ public void testComplete(TestResult result) { @Override public void runComplete(TestRunResults testRunResults) { + lastState = new ContinuousTestingWebsocketListener.State(true, false, + testRunResults.getPassedCount() + + testRunResults.getFailedCount() + + testRunResults.getSkippedCount(), + testRunResults.getPassedCount(), + testRunResults.getFailedCount(), testRunResults.getSkippedCount(), + ContinuousTestingWebsocketListener.getLastState().isBrokenOnly, + ContinuousTestingWebsocketListener.getLastState().isTestOutput, + ContinuousTestingWebsocketListener.getLastState().isInstrumentationBasedReload, + ContinuousTestingWebsocketListener.getLastState().isLiveReload); ContinuousTestingWebsocketListener.setLastState( - new ContinuousTestingWebsocketListener.State(true, false, - testRunResults.getPassedCount() + - testRunResults.getFailedCount() + - testRunResults.getSkippedCount(), - testRunResults.getPassedCount(), - testRunResults.getFailedCount(), testRunResults.getSkippedCount(), - ContinuousTestingWebsocketListener.getLastState().isBrokenOnly, - ContinuousTestingWebsocketListener.getLastState().isTestOutput, - ContinuousTestingWebsocketListener.getLastState().isInstrumentationBasedReload, - ContinuousTestingWebsocketListener.getLastState().isLiveReload)); + lastState); } @Override diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java index fe0a98105eb8bc..6bc93507d3b807 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java @@ -23,7 +23,7 @@ import io.quarkus.devconsole.spi.DevConsoleTemplateInfoBuildItem; import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; -import io.quarkus.vertx.http.deployment.devmode.console.ContinuousTestingWebSocketListener; +import io.quarkus.vertx.http.deployment.devmode.console.ContinuousTestingWebSocketTestListener; import io.quarkus.vertx.http.runtime.devmode.DevConsoleRecorder; import io.quarkus.vertx.http.runtime.devmode.Json; import io.vertx.core.Handler; @@ -61,7 +61,7 @@ public void setupTestRoutes( .route("dev/test") .handler(recorder.continousTestHandler(shutdownContextBuildItem)) .build()); - testListenerBuildItemBuildProducer.produce(new TestListenerBuildItem(new ContinuousTestingWebSocketListener())); + testListenerBuildItemBuildProducer.produce(new TestListenerBuildItem(new ContinuousTestingWebSocketTestListener())); } } From 28fbb44d4ce49a25595ef27431a103543c633563 Mon Sep 17 00:00:00 2001 From: Matej Novotny Date: Thu, 26 Aug 2021 22:12:15 +0200 Subject: [PATCH 017/138] Arc - pre-destroy and post construct shouldn't be considered as candidates for around invoke interception. Add a test for various kinds of interception together. --- .../io/quarkus/arc/processor/Methods.java | 4 ++ ...MultipleInterceptionTypesTogetherTest.java | 53 +++++++++++++++++++ .../test/interceptors/complex/MyBinding.java | 17 ++++++ .../interceptors/complex/MyInterceptor.java | 50 +++++++++++++++++ .../test/interceptors/complex/SomeBean.java | 33 ++++++++++++ 5 files changed, 157 insertions(+) create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MultipleInterceptionTypesTogetherTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MyBinding.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MyInterceptor.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/SomeBean.java diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java index d0769ac4639f5a..5505104f632aa8 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java @@ -495,6 +495,10 @@ public boolean test(MethodInfo method) { // The algorithm we use to detect these methods is best effort, i.e. there might be use cases where the detection fails return hasImplementation(method); } + if (method.hasAnnotation(DotNames.POST_CONSTRUCT) || method.hasAnnotation(DotNames.PRE_DESTROY)) { + // @PreDestroy and @PostConstruct methods declared on the bean are NOT candidates for around invoke interception + return true; + } if (isOverridenByBridgeMethod(method)) { return true; } diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MultipleInterceptionTypesTogetherTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MultipleInterceptionTypesTogetherTest.java new file mode 100644 index 00000000000000..2c6dabf1a0ffa4 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MultipleInterceptionTypesTogetherTest.java @@ -0,0 +1,53 @@ +package io.quarkus.arc.test.interceptors.complex; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.arc.test.ArcTestContainer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class MultipleInterceptionTypesTogetherTest { + + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding.class, SomeBean.class, MyInterceptor.class); + + @Test + public void testInterceptionIsInvokedWhenAppropriate() { + SomeBean.reset(); + MyInterceptor.reset(); + // assert initial state + Assertions.assertEquals(false, SomeBean.postConstructInvoked.get()); + Assertions.assertEquals(false, SomeBean.preDestroyInvoked.get()); + Assertions.assertEquals(false, MyInterceptor.aroundConstructInvoked.get()); + Assertions.assertEquals(false, MyInterceptor.postConstructInvoked.get()); + Assertions.assertEquals(false, MyInterceptor.preDestroyInvoked.get()); + Assertions.assertEquals(false, MyInterceptor.aroundInvokeInvoked.get()); + // create bean instance, no method was invoked so far + InstanceHandle instance = Arc.container().instance(SomeBean.class); + SomeBean bean = instance.get(); + // assert lifecycle callback were invoked but no around invoke was triggered + Assertions.assertEquals(true, SomeBean.postConstructInvoked.get()); + Assertions.assertEquals(false, SomeBean.preDestroyInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.aroundConstructInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.postConstructInvoked.get()); + Assertions.assertEquals(false, MyInterceptor.preDestroyInvoked.get()); + Assertions.assertEquals(false, MyInterceptor.aroundInvokeInvoked.get()); + // invoke bean method and assert around invoke was triggered + bean.ping(); + Assertions.assertEquals(true, SomeBean.postConstructInvoked.get()); + Assertions.assertEquals(false, SomeBean.preDestroyInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.aroundConstructInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.postConstructInvoked.get()); + Assertions.assertEquals(false, MyInterceptor.preDestroyInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.aroundInvokeInvoked.get()); + // trigger bean destruction and assert lifecycle interceptors were triggered + instance.destroy(); + Assertions.assertEquals(true, SomeBean.postConstructInvoked.get()); + Assertions.assertEquals(true, SomeBean.preDestroyInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.aroundConstructInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.postConstructInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.preDestroyInvoked.get()); + Assertions.assertEquals(true, MyInterceptor.aroundInvokeInvoked.get()); + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MyBinding.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MyBinding.java new file mode 100644 index 00000000000000..21b0676c746308 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MyBinding.java @@ -0,0 +1,17 @@ +package io.quarkus.arc.test.interceptors.complex; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.interceptor.InterceptorBinding; + +@Target({ TYPE, METHOD }) +@Retention(RUNTIME) +@Documented +@InterceptorBinding +public @interface MyBinding { +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MyInterceptor.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MyInterceptor.java new file mode 100644 index 00000000000000..c61548c310a04a --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/MyInterceptor.java @@ -0,0 +1,50 @@ +package io.quarkus.arc.test.interceptors.complex; + +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.Priority; +import javax.interceptor.AroundConstruct; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InvocationContext; + +@Interceptor +@Priority(1) +@MyBinding +public class MyInterceptor { + + public static AtomicBoolean preDestroyInvoked = new AtomicBoolean(false); + public static AtomicBoolean postConstructInvoked = new AtomicBoolean(false); + public static AtomicBoolean aroundConstructInvoked = new AtomicBoolean(false); + public static AtomicBoolean aroundInvokeInvoked = new AtomicBoolean(false); + + @PreDestroy + public void preDestroy(InvocationContext ic) { + preDestroyInvoked.set(true); + } + + @PostConstruct + public void postConstruct(InvocationContext ic) { + postConstructInvoked.set(true); + } + + @AroundConstruct + public void aroundConstruct(InvocationContext ic) throws Exception { + aroundConstructInvoked.set(true); + ic.proceed(); + } + + @AroundInvoke + public Object aroundInvoke(InvocationContext ic) throws Exception { + aroundInvokeInvoked.set(true); + return ic.proceed(); + } + + public static void reset() { + preDestroyInvoked.set(false); + postConstructInvoked.set(false); + aroundConstructInvoked.set(false); + aroundInvokeInvoked.set(false); + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/SomeBean.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/SomeBean.java new file mode 100644 index 00000000000000..4de05da30ffb93 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/complex/SomeBean.java @@ -0,0 +1,33 @@ +package io.quarkus.arc.test.interceptors.complex; + +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Singleton; + +@Singleton +@MyBinding +public class SomeBean { + + public static AtomicBoolean preDestroyInvoked = new AtomicBoolean(false); + public static AtomicBoolean postConstructInvoked = new AtomicBoolean(false); + + @PreDestroy + public void preDestroy() { + preDestroyInvoked.set(true); + } + + @PostConstruct + public void postConstruct() { + postConstructInvoked.set(true); + } + + public void ping() { + + } + + public static void reset() { + preDestroyInvoked.set(false); + postConstructInvoked.set(false); + } +} From 236dbd01a029b2fdc4467e0c3e41647d1815a3f5 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Tue, 31 Aug 2021 09:44:58 +0200 Subject: [PATCH 018/138] OpenAPI Auto security based on extensions Signed-off-by: Phillip Kruger --- .../oidc/deployment/OidcBuildStep.java | 9 ++ .../jwt/deployment/SmallRyeJwtProcessor.java | 6 ++ .../deployment/SmallRyeOpenApiConfig.java | 6 ++ .../deployment/SmallRyeOpenApiProcessor.java | 77 ++++++++++++++++- .../runtime/OpenApiDocumentService.java | 26 +++--- .../openapi/runtime/OpenApiHandler.java | 7 +- .../openapi/runtime/OpenApiRecorder.java | 6 +- .../filter/AutoBasicSecurityFilter.java | 37 +++++++++ .../runtime/filter/AutoJWTSecurityFilter.java | 48 +++++++++++ .../runtime/filter/AutoSecurityFilter.java | 82 +++++++++++++++++++ .../openapi/runtime/filter/AutoUrl.java | 60 ++++++++++++++ .../filter/OpenIDConnectSecurityFilter.java | 71 ++++++++++++++++ .../deployment/HttpSecurityProcessor.java | 8 +- .../SecurityInformationBuildItem.java | 59 +++++++++++++ 14 files changed, 486 insertions(+), 16 deletions(-) create mode 100644 extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoBasicSecurityFilter.java create mode 100644 extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoJWTSecurityFilter.java create mode 100644 extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoSecurityFilter.java create mode 100644 extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoUrl.java create mode 100644 extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/OpenIDConnectSecurityFilter.java create mode 100644 extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/SecurityInformationBuildItem.java diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java index b0b958807c0da4..fad473c1b05b78 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java @@ -38,6 +38,7 @@ import io.quarkus.oidc.runtime.TenantConfigBean; import io.quarkus.runtime.TlsConfig; import io.quarkus.vertx.core.deployment.CoreVertxBuildItem; +import io.quarkus.vertx.http.deployment.SecurityInformationBuildItem; import io.smallrye.jwt.auth.cdi.ClaimValueProducer; import io.smallrye.jwt.auth.cdi.CommonJwtProducer; import io.smallrye.jwt.auth.cdi.JsonValueProducer; @@ -51,6 +52,14 @@ FeatureBuildItem featureBuildItem() { return new FeatureBuildItem(Feature.OIDC); } + @BuildStep(onlyIf = IsEnabled.class) + public void provideSecurityInformation(BuildProducer securityInformationProducer) { + // TODO: By default quarkus.oidc.application-type = service + // Also look at other options (web-app, hybrid) + securityInformationProducer + .produce(SecurityInformationBuildItem.OPENIDCONNECT("quarkus.oidc.auth-server-url")); + } + @BuildStep(onlyIf = IsEnabled.class) AdditionalBeanBuildItem jwtClaimIntegration(Capabilities capabilities) { if (!capabilities.isPresent(Capability.JWT)) { diff --git a/extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java b/extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java index a4aeafe88a9819..3fe6668cc1282c 100644 --- a/extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java +++ b/extensions/smallrye-jwt/deployment/src/main/java/io/quarkus/smallrye/jwt/deployment/SmallRyeJwtProcessor.java @@ -37,6 +37,7 @@ import io.quarkus.smallrye.jwt.runtime.auth.JwtPrincipalProducer; import io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator; import io.quarkus.smallrye.jwt.runtime.auth.RawOptionalClaimCreator; +import io.quarkus.vertx.http.deployment.SecurityInformationBuildItem; import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm; import io.smallrye.jwt.algorithm.SignatureAlgorithm; import io.smallrye.jwt.auth.cdi.ClaimValueProducer; @@ -69,6 +70,11 @@ EnableAllSecurityServicesBuildItem security() { return new EnableAllSecurityServicesBuildItem(); } + @BuildStep(onlyIf = IsEnabled.class) + public void provideSecurityInformation(BuildProducer securityInformationProducer) { + securityInformationProducer.produce(SecurityInformationBuildItem.JWT()); + } + /** * Register the CDI beans that are needed by the MP-JWT extension * diff --git a/extensions/smallrye-openapi-common/deployment/src/main/java/io/quarkus/smallrye/openapi/common/deployment/SmallRyeOpenApiConfig.java b/extensions/smallrye-openapi-common/deployment/src/main/java/io/quarkus/smallrye/openapi/common/deployment/SmallRyeOpenApiConfig.java index ee2c9295eb5833..22a8546f728d44 100644 --- a/extensions/smallrye-openapi-common/deployment/src/main/java/io/quarkus/smallrye/openapi/common/deployment/SmallRyeOpenApiConfig.java +++ b/extensions/smallrye-openapi-common/deployment/src/main/java/io/quarkus/smallrye/openapi/common/deployment/SmallRyeOpenApiConfig.java @@ -70,6 +70,12 @@ public final class SmallRyeOpenApiConfig { @ConfigItem(defaultValue = "true") public boolean autoAddTags; + /** + * This will automatically add security based on the security extension included (if any). + */ + @ConfigItem(defaultValue = "true") + public boolean autoAddSecurity; + /** * Add a scheme value to the Basic HTTP Security Scheme */ diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java index 3689aef33ab9e4..b8c40f5650d6df 100644 --- a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java +++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java @@ -76,9 +76,14 @@ import io.quarkus.smallrye.openapi.runtime.OpenApiDocumentService; import io.quarkus.smallrye.openapi.runtime.OpenApiRecorder; import io.quarkus.smallrye.openapi.runtime.OpenApiRuntimeConfig; +import io.quarkus.smallrye.openapi.runtime.filter.AutoBasicSecurityFilter; +import io.quarkus.smallrye.openapi.runtime.filter.AutoJWTSecurityFilter; +import io.quarkus.smallrye.openapi.runtime.filter.AutoUrl; +import io.quarkus.smallrye.openapi.runtime.filter.OpenIDConnectSecurityFilter; import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; +import io.quarkus.vertx.http.deployment.SecurityInformationBuildItem; import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem; import io.quarkus.vertx.http.runtime.HttpConfiguration; import io.smallrye.openapi.api.OpenApiConfig; @@ -168,9 +173,11 @@ RouteBuildItem handler(LaunchModeBuildItem launch, BuildProducer displayableEndpoints, OpenApiRecorder recorder, NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, + List securityInformationBuildItems, OpenApiRuntimeConfig openApiRuntimeConfig, ShutdownContextBuildItem shutdownContext, SmallRyeOpenApiConfig openApiConfig, + OpenApiFilteredIndexViewBuildItem apiFilteredIndexViewBuildItem, HttpConfiguration httpConfiguration) { /* * Ugly Hack @@ -187,7 +194,17 @@ RouteBuildItem handler(LaunchModeBuildItem launch, recorder.setupClDevMode(shutdownContext); } - Handler handler = recorder.handler(openApiRuntimeConfig, httpConfiguration); + OASFilter autoSecurityFilter = null; + if (openApiConfig.autoAddSecurity) { + // Only add the security if there are secured endpoints + OASFilter autoRolesAllowedFilter = getAutoRolesAllowedFilter(openApiConfig.securitySchemeName, + apiFilteredIndexViewBuildItem, openApiConfig); + if (autoRolesAllowedFilter != null) { + autoSecurityFilter = getAutoSecurityFilter(securityInformationBuildItems, openApiConfig); + } + } + + Handler handler = recorder.handler(openApiRuntimeConfig, httpConfiguration, autoSecurityFilter); return nonApplicationRootPathBuildItem.routeBuilder() .route(openApiConfig.path) .routeConfigKey("quarkus.smallrye-openapi.path") @@ -277,6 +294,64 @@ void addSecurityFilter(BuildProducer addToOpenA } + private OASFilter getAutoSecurityFilter(List securityInformationBuildItems, + SmallRyeOpenApiConfig config) { + + // Auto add a security from security extension(s) + if (!config.securityScheme.isPresent() && securityInformationBuildItems != null + && !securityInformationBuildItems.isEmpty()) { + // This needs to be a filter in runtime as the config we use to auto configure is in runtime + for (SecurityInformationBuildItem securityInformationBuildItem : securityInformationBuildItems) { + SecurityInformationBuildItem.SecurityModel securityModel = securityInformationBuildItem.getSecurityModel(); + switch (securityModel) { + case jwt: + return new AutoJWTSecurityFilter( + config.securitySchemeName, + config.securitySchemeDescription, + config.jwtSecuritySchemeValue, + config.jwtBearerFormat); + case basic: + return new AutoBasicSecurityFilter( + config.securitySchemeName, + config.securitySchemeDescription, + config.basicSecuritySchemeValue); + case oidc: + Optional maybeInfo = securityInformationBuildItem + .getOpenIDConnectInformation(); + + if (maybeInfo.isPresent()) { + SecurityInformationBuildItem.OpenIDConnectInformation info = maybeInfo.get(); + + AutoUrl authorizationUrl = new AutoUrl( + config.oidcOpenIdConnectUrl.orElse(null), + info.getUrlConfigKey(), + "/protocol/openid-connect/auth"); + + AutoUrl refreshUrl = new AutoUrl( + config.oidcOpenIdConnectUrl.orElse(null), + info.getUrlConfigKey(), + "/protocol/openid-connect/token"); + + AutoUrl tokenUrl = new AutoUrl( + config.oidcOpenIdConnectUrl.orElse(null), + info.getUrlConfigKey(), + "/protocol/openid-connect/token/introspect"); + + return new OpenIDConnectSecurityFilter( + config.securitySchemeName, + config.securitySchemeDescription, + authorizationUrl, refreshUrl, tokenUrl); + + } + break; + default: + break; + } + } + } + return null; + } + private OASFilter getAutoRolesAllowedFilter(String securitySchemeName, OpenApiFilteredIndexViewBuildItem apiFilteredIndexViewBuildItem, SmallRyeOpenApiConfig config) { diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiDocumentService.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiDocumentService.java index c9625b408becfe..95f19a6f1f1bf3 100644 --- a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiDocumentService.java +++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiDocumentService.java @@ -4,7 +4,6 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; -import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import org.eclipse.microprofile.config.Config; @@ -30,16 +29,15 @@ public class OpenApiDocumentService implements OpenApiDocumentHolder { private OpenApiDocumentHolder documentHolder; - @PostConstruct - void create() throws IOException { + void init(OASFilter autoSecurityFilter) { Config config = ConfigProvider.getConfig(); this.alwaysRunFilter = config.getOptionalValue("quarkus.smallrye-openapi.always-run-filter", Boolean.class) .orElse(Boolean.FALSE); if (alwaysRunFilter) { - this.documentHolder = new DynamicDocument(config); + this.documentHolder = new DynamicDocument(config, autoSecurityFilter); } else { - this.documentHolder = new StaticDocument(config); + this.documentHolder = new StaticDocument(config, autoSecurityFilter); } } @@ -59,7 +57,7 @@ class StaticDocument implements OpenApiDocumentHolder { private byte[] jsonDocument; private byte[] yamlDocument; - StaticDocument(Config config) { + StaticDocument(Config config, OASFilter autoFilter) { ClassLoader cl = OpenApiConstants.classLoader == null ? Thread.currentThread().getContextClassLoader() : OpenApiConstants.classLoader; try (InputStream is = cl.getResourceAsStream(OpenApiConstants.BASE_NAME + Format.JSON)) { @@ -72,6 +70,9 @@ class StaticDocument implements OpenApiDocumentHolder { document.reset(); document.config(openApiConfig); document.modelFromStaticFile(OpenApiProcessor.modelFromStaticFile(staticFile)); + if (autoFilter != null) { + document.filter(autoFilter); + } document.filter(OpenApiProcessor.getFilter(openApiConfig, cl)); document.initialize(); @@ -104,9 +105,10 @@ class DynamicDocument implements OpenApiDocumentHolder { private OpenAPI generatedOnBuild; private OpenApiConfig openApiConfig; - private OASFilter filter; + private OASFilter userFilter; + private OASFilter autoFilter; - DynamicDocument(Config config) { + DynamicDocument(Config config, OASFilter autoFilter) { ClassLoader cl = OpenApiConstants.classLoader == null ? Thread.currentThread().getContextClassLoader() : OpenApiConstants.classLoader; try (InputStream is = cl.getResourceAsStream(OpenApiConstants.BASE_NAME + Format.JSON)) { @@ -114,7 +116,8 @@ class DynamicDocument implements OpenApiDocumentHolder { try (OpenApiStaticFile staticFile = new OpenApiStaticFile(is, Format.JSON)) { this.generatedOnBuild = OpenApiProcessor.modelFromStaticFile(staticFile); this.openApiConfig = new OpenApiConfigImpl(config); - this.filter = OpenApiProcessor.getFilter(openApiConfig, cl); + this.userFilter = OpenApiProcessor.getFilter(openApiConfig, cl); + this.autoFilter = autoFilter; } } } catch (IOException ex) { @@ -153,7 +156,10 @@ private OpenApiDocument getOpenApiDocument() { document.reset(); document.config(this.openApiConfig); document.modelFromStaticFile(this.generatedOnBuild); - document.filter(this.filter); + if (this.autoFilter != null) { + document.filter(this.autoFilter); + } + document.filter(this.userFilter); document.initialize(); return document; } diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiHandler.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiHandler.java index 9be329c3afea77..c226b42b47fc3b 100644 --- a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiHandler.java +++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiHandler.java @@ -2,6 +2,8 @@ import java.util.List; +import org.eclipse.microprofile.openapi.OASFilter; + import io.quarkus.arc.Arc; import io.smallrye.openapi.runtime.io.Format; import io.vertx.core.Handler; @@ -32,9 +34,11 @@ public class OpenApiHandler implements Handler { } final boolean corsEnabled; + final OASFilter autoSecurityFilter; - public OpenApiHandler(boolean corsEnabled) { + public OpenApiHandler(boolean corsEnabled, OASFilter autoSecurityFilter) { this.corsEnabled = corsEnabled; + this.autoSecurityFilter = autoSecurityFilter; } @Override @@ -77,6 +81,7 @@ public void handle(RoutingContext event) { private OpenApiDocumentService getOpenApiDocumentService() { if (this.openApiDocumentService == null) { this.openApiDocumentService = Arc.container().instance(OpenApiDocumentService.class).get(); + this.openApiDocumentService.init(this.autoSecurityFilter); } return this.openApiDocumentService; } diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiRecorder.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiRecorder.java index be5250c3b0075e..3d3e3c196ee282 100644 --- a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiRecorder.java +++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/OpenApiRecorder.java @@ -5,6 +5,7 @@ import java.net.URL; import java.util.Enumeration; +import org.eclipse.microprofile.openapi.OASFilter; import org.eclipse.microprofile.openapi.spi.OASFactoryResolver; import io.quarkus.runtime.ShutdownContext; @@ -16,9 +17,10 @@ @Recorder public class OpenApiRecorder { - public Handler handler(OpenApiRuntimeConfig runtimeConfig, HttpConfiguration configuration) { + public Handler handler(OpenApiRuntimeConfig runtimeConfig, HttpConfiguration configuration, + OASFilter autoSecurityFilter) { if (runtimeConfig.enable) { - return new OpenApiHandler(configuration.corsEnabled); + return new OpenApiHandler(configuration.corsEnabled, autoSecurityFilter); } else { return new OpenApiNotFoundHandler(); } diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoBasicSecurityFilter.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoBasicSecurityFilter.java new file mode 100644 index 00000000000000..fc1850052c0504 --- /dev/null +++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoBasicSecurityFilter.java @@ -0,0 +1,37 @@ +package io.quarkus.smallrye.openapi.runtime.filter; + +import org.eclipse.microprofile.openapi.OASFactory; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; + +/** + * Add Basic Authentication security automatically based on the added security extensions + */ +public class AutoBasicSecurityFilter extends AutoSecurityFilter { + private String basicSecuritySchemeValue; + + public AutoBasicSecurityFilter() { + super(); + } + + public AutoBasicSecurityFilter(String securitySchemeName, String securitySchemeDescription, + String basicSecuritySchemeValue) { + super(securitySchemeName, securitySchemeDescription); + this.basicSecuritySchemeValue = basicSecuritySchemeValue; + } + + public String getBasicSecuritySchemeValue() { + return basicSecuritySchemeValue; + } + + public void setBasicSecuritySchemeValue(String basicSecuritySchemeValue) { + this.basicSecuritySchemeValue = basicSecuritySchemeValue; + } + + @Override + protected SecurityScheme getSecurityScheme() { + SecurityScheme securityScheme = OASFactory.createSecurityScheme(); + securityScheme.setType(SecurityScheme.Type.HTTP); + securityScheme.setScheme(basicSecuritySchemeValue); + return securityScheme; + } +} \ No newline at end of file diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoJWTSecurityFilter.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoJWTSecurityFilter.java new file mode 100644 index 00000000000000..c246fce55b844a --- /dev/null +++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoJWTSecurityFilter.java @@ -0,0 +1,48 @@ +package io.quarkus.smallrye.openapi.runtime.filter; + +import org.eclipse.microprofile.openapi.OASFactory; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; + +/** + * Add JWT Authentication security automatically based on the added security extensions + */ +public class AutoJWTSecurityFilter extends AutoSecurityFilter { + private String jwtSecuritySchemeValue; + private String jwtBearerFormat; + + public AutoJWTSecurityFilter() { + super(); + } + + public AutoJWTSecurityFilter(String securitySchemeName, String securitySchemeDescription, String jwtSecuritySchemeValue, + String jwtBearerFormat) { + super(securitySchemeName, securitySchemeDescription); + this.jwtSecuritySchemeValue = jwtSecuritySchemeValue; + this.jwtBearerFormat = jwtBearerFormat; + } + + public String getJwtSecuritySchemeValue() { + return jwtSecuritySchemeValue; + } + + public void setJwtSecuritySchemeValue(String jwtSecuritySchemeValue) { + this.jwtSecuritySchemeValue = jwtSecuritySchemeValue; + } + + public String getJwtBearerFormat() { + return jwtBearerFormat; + } + + public void setJwtBearerFormat(String jwtBearerFormat) { + this.jwtBearerFormat = jwtBearerFormat; + } + + @Override + protected SecurityScheme getSecurityScheme() { + SecurityScheme securityScheme = OASFactory.createSecurityScheme(); + securityScheme.setType(SecurityScheme.Type.HTTP); + securityScheme.setScheme(jwtSecuritySchemeValue); + securityScheme.setBearerFormat(jwtBearerFormat); + return securityScheme; + } +} diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoSecurityFilter.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoSecurityFilter.java new file mode 100644 index 00000000000000..00f4cd8c597cfb --- /dev/null +++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoSecurityFilter.java @@ -0,0 +1,82 @@ +package io.quarkus.smallrye.openapi.runtime.filter; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.openapi.OASFactory; +import org.eclipse.microprofile.openapi.OASFilter; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; +import org.jboss.logging.Logger; + +/** + * Auto add security + */ +public abstract class AutoSecurityFilter implements OASFilter { + private static final Logger log = Logger.getLogger(AutoSecurityFilter.class); + + private String securitySchemeName; + private String securitySchemeDescription; + + public AutoSecurityFilter() { + + } + + public AutoSecurityFilter(String securitySchemeName, String securitySchemeDescription) { + this.securitySchemeName = securitySchemeName; + this.securitySchemeDescription = securitySchemeDescription; + } + + public String getSecuritySchemeName() { + return securitySchemeName; + } + + public void setSecuritySchemeName(String securitySchemeName) { + this.securitySchemeName = securitySchemeName; + } + + public String getSecuritySchemeDescription() { + return securitySchemeDescription; + } + + public void setSecuritySchemeDescription(String securitySchemeDescription) { + this.securitySchemeDescription = securitySchemeDescription; + } + + @Override + public void filterOpenAPI(OpenAPI openAPI) { + // Make sure components are created + if (openAPI.getComponents() == null) { + openAPI.setComponents(OASFactory.createComponents()); + } + + Map securitySchemes = new HashMap<>(); + + // Add any existing security + if (openAPI.getComponents().getSecuritySchemes() != null + && !openAPI.getComponents().getSecuritySchemes().isEmpty()) { + securitySchemes.putAll(openAPI.getComponents().getSecuritySchemes()); + } + + SecurityScheme securityScheme = getSecurityScheme(); + securityScheme.setDescription(securitySchemeDescription); + securitySchemes.put(securitySchemeName, securityScheme); + openAPI.getComponents().setSecuritySchemes(securitySchemes); + } + + protected abstract SecurityScheme getSecurityScheme(); + + protected String getUrl(String configKey, String defaultValue, String shouldEndWith) { + Config c = ConfigProvider.getConfig(); + + String u = c.getOptionalValue(configKey, String.class).orElse(defaultValue); + + if (u != null && !u.endsWith(shouldEndWith)) { + u = u + shouldEndWith; + } + return u; + } + +} \ No newline at end of file diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoUrl.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoUrl.java new file mode 100644 index 00000000000000..8ee97b65c624d6 --- /dev/null +++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/AutoUrl.java @@ -0,0 +1,60 @@ +package io.quarkus.smallrye.openapi.runtime.filter; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; + +/** + * Create a URL from a config, or a default value + */ +public class AutoUrl { + + private String defaultValue; + private String configKey; + private String path; + + public AutoUrl() { + } + + public AutoUrl(String defaultValue, String configKey, String path) { + this.defaultValue = defaultValue; + this.configKey = configKey; + this.path = path; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public String getConfigKey() { + return configKey; + } + + public void setConfigKey(String configKey) { + this.configKey = configKey; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getFinalUrlValue() { + Config c = ConfigProvider.getConfig(); + + String u = c.getOptionalValue(this.configKey, String.class).orElse(defaultValue); + + if (u != null && path != null && !u.endsWith(path)) { + u = u + path; + } + + return u; + } + +} diff --git a/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/OpenIDConnectSecurityFilter.java b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/OpenIDConnectSecurityFilter.java new file mode 100644 index 00000000000000..cbc18a5c320b73 --- /dev/null +++ b/extensions/smallrye-openapi/runtime/src/main/java/io/quarkus/smallrye/openapi/runtime/filter/OpenIDConnectSecurityFilter.java @@ -0,0 +1,71 @@ +package io.quarkus.smallrye.openapi.runtime.filter; + +import org.eclipse.microprofile.openapi.OASFactory; +import org.eclipse.microprofile.openapi.models.security.OAuthFlow; +import org.eclipse.microprofile.openapi.models.security.OAuthFlows; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; + +/** + * Add OAuth 2 (Implicit) Authentication security automatically based on the added security extensions + */ +public class OpenIDConnectSecurityFilter extends AutoSecurityFilter { + + private AutoUrl authorizationUrl; + private AutoUrl refreshUrl; + private AutoUrl tokenUrl; + + public OpenIDConnectSecurityFilter() { + super(); + } + + public OpenIDConnectSecurityFilter(String securitySchemeName, String securitySchemeDescription, + AutoUrl authorizationUrl, + AutoUrl refreshUrl, + AutoUrl tokenUrl) { + super(securitySchemeName, securitySchemeDescription); + this.authorizationUrl = authorizationUrl; + this.refreshUrl = refreshUrl; + this.tokenUrl = tokenUrl; + } + + public AutoUrl getAuthorizationUrl() { + return authorizationUrl; + } + + public void setAuthorizationUrl(AutoUrl authorizationUrl) { + this.authorizationUrl = authorizationUrl; + } + + public AutoUrl getRefreshUrl() { + return refreshUrl; + } + + public void setRefreshUrl(AutoUrl refreshUrl) { + this.refreshUrl = refreshUrl; + } + + public AutoUrl getTokenUrl() { + return tokenUrl; + } + + public void setTokenUrl(AutoUrl tokenUrl) { + this.tokenUrl = tokenUrl; + } + + @Override + protected SecurityScheme getSecurityScheme() { + SecurityScheme securityScheme = OASFactory.createSecurityScheme(); + + securityScheme.setType(SecurityScheme.Type.OAUTH2); + OAuthFlows oAuthFlows = OASFactory.createOAuthFlows(); + OAuthFlow oAuthFlow = OASFactory.createOAuthFlow(); + oAuthFlow.authorizationUrl(this.authorizationUrl.getFinalUrlValue()); + oAuthFlow.refreshUrl(this.refreshUrl.getFinalUrlValue()); + oAuthFlow.tokenUrl(this.tokenUrl.getFinalUrlValue()); + oAuthFlows.setImplicit(oAuthFlow); + securityScheme.setFlows(oAuthFlows); + + return securityScheme; + } + +} \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java index e6a7e0d54c6c51..a7b7956bf0068a 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java @@ -89,7 +89,8 @@ SyntheticBeanBuildItem initMtlsClientAuth( @Record(ExecutionTime.RUNTIME_INIT) SyntheticBeanBuildItem initBasicAuth( HttpSecurityRecorder recorder, - HttpBuildTimeConfig buildTimeConfig) { + HttpBuildTimeConfig buildTimeConfig, + BuildProducer securityInformationProducer) { if ((buildTimeConfig.auth.form.enabled || isMtlsClientAuthenticationEnabled(buildTimeConfig)) && !buildTimeConfig.auth.basic) { //if form auth is enabled and we are not then we don't install @@ -105,6 +106,7 @@ SyntheticBeanBuildItem initBasicAuth( && !buildTimeConfig.auth.basic) { //if not explicitly enabled we make this a default bean, so it is the fallback if nothing else is defined configurator.defaultBean(); + securityInformationProducer.produce(SecurityInformationBuildItem.BASIC()); } return configurator.done(); @@ -119,7 +121,8 @@ void setupAuthenticationMechanisms( Capabilities capabilities, BuildProducer beanContainerListenerBuildItemBuildProducer, HttpBuildTimeConfig buildTimeConfig, - List httpSecurityPolicyBuildItemList) { + List httpSecurityPolicyBuildItemList, + BuildProducer securityInformationProducer) { Map> policyMap = new HashMap<>(); for (HttpSecurityPolicyBuildItem e : httpSecurityPolicyBuildItemList) { if (policyMap.containsKey(e.getName())) { @@ -131,6 +134,7 @@ void setupAuthenticationMechanisms( if (buildTimeConfig.auth.form.enabled) { } else if (buildTimeConfig.auth.basic) { beanProducer.produce(AdditionalBeanBuildItem.unremovableOf(BasicAuthenticationMechanism.class)); + securityInformationProducer.produce(SecurityInformationBuildItem.BASIC()); } if (capabilities.isPresent(Capability.SECURITY)) { diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/SecurityInformationBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/SecurityInformationBuildItem.java new file mode 100644 index 00000000000000..f1b6c64aec8a43 --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/SecurityInformationBuildItem.java @@ -0,0 +1,59 @@ +package io.quarkus.vertx.http.deployment; + +import java.util.Optional; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * Contains information on the security model used in the application + */ +public final class SecurityInformationBuildItem extends MultiBuildItem { + + private final SecurityModel securityModel; + private final Optional openIDConnectInformation; + + public static SecurityInformationBuildItem BASIC() { + return new SecurityInformationBuildItem(SecurityModel.basic, Optional.empty()); + } + + public static SecurityInformationBuildItem JWT() { + return new SecurityInformationBuildItem(SecurityModel.jwt, Optional.empty()); + } + + public static SecurityInformationBuildItem OPENIDCONNECT(String urlConfigKey) { + return new SecurityInformationBuildItem(SecurityModel.oidc, + Optional.of(new OpenIDConnectInformation(urlConfigKey))); + } + + public SecurityInformationBuildItem(SecurityModel securityModel, + Optional openIDConnectInformation) { + this.securityModel = securityModel; + this.openIDConnectInformation = openIDConnectInformation; + } + + public SecurityModel getSecurityModel() { + return securityModel; + } + + public Optional getOpenIDConnectInformation() { + return openIDConnectInformation; + } + + public enum SecurityModel { + basic, + jwt, + oidc + } + + public static class OpenIDConnectInformation { + private final String urlConfigKey; + + public OpenIDConnectInformation(String urlConfigKey) { + this.urlConfigKey = urlConfigKey; + } + + public String getUrlConfigKey() { + return urlConfigKey; + } + } +} \ No newline at end of file From 7142e7913f0f476889b7353432d339ca14b2ad12 Mon Sep 17 00:00:00 2001 From: Marc Nuri Date: Tue, 31 Aug 2021 11:59:36 +0200 Subject: [PATCH 019/138] fix: K8s Service can be re-applied Signed-off-by: Marc Nuri --- .../deployment/KubernetesDeployer.java | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java index c358fb29ea50d5..362b63ae063578 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesDeployer.java @@ -11,11 +11,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Path; -import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -25,6 +26,7 @@ import io.dekorate.utils.Serialization; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.KubernetesList; +import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.openshift.api.model.Route; @@ -42,7 +44,6 @@ import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.kubernetes.client.deployment.KubernetesClientErrorHandler; import io.quarkus.kubernetes.client.spi.KubernetesClientBuildItem; -import io.quarkus.kubernetes.spi.KubernetesDeploymentTargetBuildItem; public class KubernetesDeployer { @@ -60,7 +61,7 @@ public void selectDeploymentTarget(ContainerImageInfoBuildItem containerImageInf Optional activeContainerImageCapability = ContainerImageCapabilitiesUtil .getActiveContainerImageCapability(capabilities); - if (!activeContainerImageCapability.isPresent()) { + if (activeContainerImageCapability.isEmpty()) { // we can't thrown an exception here, because it could prevent the Kubernetes resources from being generated return; } @@ -85,10 +86,10 @@ public void deploy(KubernetesClientBuildItem kubernetesClient, return; } - if (!selectedDeploymentTarget.isPresent()) { + if (selectedDeploymentTarget.isEmpty()) { - if (!ContainerImageCapabilitiesUtil - .getActiveContainerImageCapability(capabilities).isPresent()) { + if (ContainerImageCapabilitiesUtil + .getActiveContainerImageCapability(capabilities).isEmpty()) { throw new RuntimeException( "A Kubernetes deployment was requested but no extension was found to build a container image. Consider adding one of following extensions: " + CONTAINER_IMAGE_EXTENSIONS_STR + "."); @@ -158,11 +159,6 @@ private DeploymentTargetEntry determineDeploymentTarget( return selectedTarget; } - private Optional getOptionalDeploymentTarget( - List deploymentTargets, String name) { - return deploymentTargets.stream().filter(d -> name.equals(d.getName())).findFirst(); - } - private DeploymentResultBuildItem deploy(DeploymentTargetEntry deploymentTarget, KubernetesClient client, Path outputDir, OpenshiftConfig openshiftConfig, ApplicationInfoBuildItem applicationInfo) { @@ -174,12 +170,13 @@ private DeploymentResultBuildItem deploy(DeploymentTargetEntry deploymentTarget, try (FileInputStream fis = new FileInputStream(manifest)) { KubernetesList list = Serialization.unmarshalAsList(fis); - distinct(list.getItems()).forEach(i -> { - if (KNATIVE.equals(deploymentTarget.getName().toLowerCase())) { - client.resource(i).inNamespace(namespace).deletingExisting().createOrReplace(); - } else { - client.resource(i).inNamespace(namespace).createOrReplace(); + list.getItems().stream().filter(distinctByResourceKey()).forEach(i -> { + final var r = client.resource(i).inNamespace(namespace); + if (shouldDeleteExisting(deploymentTarget, i)) { + r.delete(); + r.waitUntilCondition(Objects::isNull, 10, TimeUnit.SECONDS); } + r.createOrReplace(); log.info("Applied: " + i.getKind() + " " + i.getMetadata().getName() + "."); }); @@ -221,13 +218,15 @@ private void printExposeInformation(KubernetesClient client, KubernetesList list } } - public static Predicate distictByResourceKey() { + private static boolean shouldDeleteExisting(DeploymentTargetEntry deploymentTarget, HasMetadata resource) { + return KNATIVE.equalsIgnoreCase(deploymentTarget.getName()) + || resource instanceof Service + || (Objects.equals("v1", resource.getApiVersion()) && Objects.equals("Service", resource.getKind())); + } + + private static Predicate distinctByResourceKey() { Map seen = new ConcurrentHashMap<>(); return t -> seen.putIfAbsent(t.getApiVersion() + "/" + t.getKind() + ":" + t.getMetadata().getName(), Boolean.TRUE) == null; } - - private static Collection distinct(Collection resources) { - return resources.stream().filter(distictByResourceKey()).collect(Collectors.toList()); - } } From 57d2f7cfd67c7690b7aba46b24360c43573fa52d Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 31 Aug 2021 12:50:18 +0200 Subject: [PATCH 020/138] Codestarts - gRPC - add index.html - resolves #19642 --- .../main/resources/META-INF/resources/index.entry.qute.html | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/grpc-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/grpc-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/grpc-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html new file mode 100644 index 00000000000000..0317faba622636 --- /dev/null +++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/grpc-codestart/base/src/main/resources/META-INF/resources/index.entry.qute.html @@ -0,0 +1,3 @@ +{#include index-entry} +{#body}This codestart implements a simple gRPC service that can be tested in the Dev UI (available in dev mode only). +{/include} \ No newline at end of file From 48b365f64a1cef35cffc627186e7e6ee9df47d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Tue, 6 Jul 2021 17:51:48 +0200 Subject: [PATCH 021/138] Liquibase MongoDB extension Fixes #18009, #9801 --- .github/native-tests.json | 2 +- bom/application/pom.xml | 16 + .../java/io/quarkus/deployment/Feature.java | 1 + devtools/bom-descriptor-json/pom.xml | 13 + docs/pom.xml | 13 + .../liquibase-mongodb/deployment/pom.xml | 48 +++ .../deployment/LiquibaseProcessor.java | 325 ++++++++++++++++++ extensions/liquibase-mongodb/pom.xml | 22 ++ extensions/liquibase-mongodb/runtime/pom.xml | 71 ++++ .../liquibase/LiquibaseMongodbFactory.java | 95 +++++ .../LiquibaseMongodbBuildTimeConfig.java | 16 + .../runtime/LiquibaseMongodbConfig.java | 68 ++++ .../runtime/LiquibaseMongodbRecorder.java | 63 ++++ .../runtime/graal/SubstituteStringUtil.java | 37 ++ .../resources/META-INF/quarkus-extension.yaml | 11 + extensions/pom.xml | 1 + integration-tests/liquibase-mongodb/pom.xml | 106 ++++++ .../quarkus/it/liquibase/mongodb/Fruit.java | 8 + .../it/liquibase/mongodb/FruitResource.java | 33 ++ .../src/main/resources/application.properties | 4 + .../main/resources/liquibase/changelog.xml | 23 ++ .../liquibase/mongodb/FruitResourceTest.java | 51 +++ integration-tests/pom.xml | 1 + 23 files changed, 1027 insertions(+), 1 deletion(-) create mode 100644 extensions/liquibase-mongodb/deployment/pom.xml create mode 100644 extensions/liquibase-mongodb/deployment/src/main/java/io/quarkus/liquibase/mongodb/deployment/LiquibaseProcessor.java create mode 100644 extensions/liquibase-mongodb/pom.xml create mode 100644 extensions/liquibase-mongodb/runtime/pom.xml create mode 100644 extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/LiquibaseMongodbFactory.java create mode 100644 extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbBuildTimeConfig.java create mode 100644 extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbConfig.java create mode 100644 extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbRecorder.java create mode 100644 extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/graal/SubstituteStringUtil.java create mode 100644 extensions/liquibase-mongodb/runtime/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 integration-tests/liquibase-mongodb/pom.xml create mode 100644 integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/Fruit.java create mode 100644 integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/FruitResource.java create mode 100644 integration-tests/liquibase-mongodb/src/main/resources/application.properties create mode 100644 integration-tests/liquibase-mongodb/src/main/resources/liquibase/changelog.xml create mode 100644 integration-tests/liquibase-mongodb/src/test/java/io/quarkus/it/liquibase/mongodb/FruitResourceTest.java diff --git a/.github/native-tests.json b/.github/native-tests.json index fb363a4eaa1110..e0b017c8e9c6cd 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -21,7 +21,7 @@ { "category": "Data3", "timeout": 70, - "test-modules": "flyway, hibernate-orm-panache, hibernate-orm-panache-kotlin, hibernate-orm-envers, liquibase", + "test-modules": "flyway, hibernate-orm-panache, hibernate-orm-panache-kotlin, hibernate-orm-envers, liquibase, liquibase-mongodb", "os-name": "ubuntu-latest" }, { diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 5a28a7cab7d310..ebfe0a06a2c6f1 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -159,6 +159,7 @@ 7.14.0 1.0.9 4.4.3 + 4.4.3 1.29 6.0.0 4.3.4 @@ -818,6 +819,16 @@ quarkus-liquibase-deployment ${project.version} + + io.quarkus + quarkus-liquibase-mongodb + ${project.version} + + + io.quarkus + quarkus-liquibase-mongodb-deployment + ${project.version} + io.quarkus quarkus-hibernate-orm @@ -5061,6 +5072,11 @@ + + org.liquibase.ext + liquibase-mongodb + ${liquibase-mongodb.version} + org.yaml snakeyaml diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java index 6ec6eeb0eba48b..4abf8590c92bff 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java @@ -62,6 +62,7 @@ public enum Feature { KUBERNETES, KUBERNETES_CLIENT, LIQUIBASE, + LIQUIBASE_MONGODB, LOGGING_GELF, MAILER, MICROMETER, diff --git a/devtools/bom-descriptor-json/pom.xml b/devtools/bom-descriptor-json/pom.xml index d18fd370ada258..00fe839c0311f9 100644 --- a/devtools/bom-descriptor-json/pom.xml +++ b/devtools/bom-descriptor-json/pom.xml @@ -1332,6 +1332,19 @@ + + io.quarkus + quarkus-liquibase-mongodb + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-logging-gelf diff --git a/docs/pom.xml b/docs/pom.xml index 0b26767dec399e..4b4dc0cf1b9968 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -1293,6 +1293,19 @@ + + io.quarkus + quarkus-liquibase-mongodb-deployment + ${project.version} + pom + test + + + * + * + + + io.quarkus quarkus-logging-gelf-deployment diff --git a/extensions/liquibase-mongodb/deployment/pom.xml b/extensions/liquibase-mongodb/deployment/pom.xml new file mode 100644 index 00000000000000..6cac71b48c6cd6 --- /dev/null +++ b/extensions/liquibase-mongodb/deployment/pom.xml @@ -0,0 +1,48 @@ + + + + quarkus-liquibase-mongodb-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-liquibase-mongodb-deployment + Quarkus - Liquibase MongoDB - Deployment + + + + io.quarkus + quarkus-liquibase-mongodb + + + io.quarkus + quarkus-mongodb-client-deployment + + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/liquibase-mongodb/deployment/src/main/java/io/quarkus/liquibase/mongodb/deployment/LiquibaseProcessor.java b/extensions/liquibase-mongodb/deployment/src/main/java/io/quarkus/liquibase/mongodb/deployment/LiquibaseProcessor.java new file mode 100644 index 00000000000000..ec9ddc2a910ded --- /dev/null +++ b/extensions/liquibase-mongodb/deployment/src/main/java/io/quarkus/liquibase/mongodb/deployment/LiquibaseProcessor.java @@ -0,0 +1,325 @@ +package io.quarkus.liquibase.mongodb.deployment; + +import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +import javax.enterprise.context.ApplicationScoped; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; +import org.jboss.logging.Logger; + +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem; +import io.quarkus.deployment.Feature; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Consume; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.ServiceStartBuildItem; +import io.quarkus.deployment.builditem.SystemPropertyBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; +import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; +import io.quarkus.deployment.util.ServiceUtil; +import io.quarkus.liquibase.LiquibaseMongodbFactory; +import io.quarkus.liquibase.runtime.LiquibaseMongodbBuildTimeConfig; +import io.quarkus.liquibase.runtime.LiquibaseMongodbConfig; +import io.quarkus.liquibase.runtime.LiquibaseMongodbRecorder; +import io.quarkus.mongodb.runtime.MongodbConfig; +import liquibase.change.Change; +import liquibase.change.DatabaseChangeProperty; +import liquibase.change.core.CreateProcedureChange; +import liquibase.change.core.CreateViewChange; +import liquibase.change.core.LoadDataChange; +import liquibase.change.core.SQLFileChange; +import liquibase.changelog.ChangeLogParameters; +import liquibase.changelog.ChangeSet; +import liquibase.changelog.DatabaseChangeLog; +import liquibase.exception.LiquibaseException; +import liquibase.parser.ChangeLogParser; +import liquibase.parser.ChangeLogParserFactory; +import liquibase.resource.ClassLoaderResourceAccessor; + +class LiquibaseProcessor { + + private static final Logger LOGGER = Logger.getLogger(LiquibaseProcessor.class); + + private static final String LIQUIBASE_BEAN_NAME_PREFIX = "liquibase_"; + + private static final DotName DATABASE_CHANGE_PROPERTY = DotName.createSimple(DatabaseChangeProperty.class.getName()); + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(Feature.LIQUIBASE_MONGODB); + } + + @BuildStep + public SystemPropertyBuildItem disableHub() { + // Don't block app startup with prompt: + // Do you want to see this operation's report in Liquibase Hub, which improves team collaboration? + // If so, enter your email. If not, enter [N] to no longer be prompted, or [S] to skip for now, but ask again next time (default "S"): + return new SystemPropertyBuildItem("liquibase.hub.mode", "off"); + } + + @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) + @Record(STATIC_INIT) + void nativeImageConfiguration( + LiquibaseMongodbRecorder recorder, + LiquibaseMongodbBuildTimeConfig liquibaseBuildConfig, + CombinedIndexBuildItem combinedIndex, + BuildProducer reflective, + BuildProducer resource, + BuildProducer services, + BuildProducer runtimeInitialized, + BuildProducer resourceBundle) { + + runtimeInitialized.produce(new RuntimeInitializedClassBuildItem(liquibase.diff.compare.CompareControl.class.getName())); + + reflective.produce(new ReflectiveClassBuildItem(false, true, false, + liquibase.change.AbstractSQLChange.class.getName(), + liquibase.database.jvm.JdbcConnection.class.getName())); + + reflective.produce(new ReflectiveClassBuildItem(true, true, true, + liquibase.parser.ChangeLogParserCofiguration.class.getName(), + liquibase.hub.HubServiceFactory.class.getName(), + liquibase.logging.core.DefaultLoggerConfiguration.class.getName(), + liquibase.configuration.GlobalConfiguration.class.getName(), + com.datical.liquibase.ext.config.LiquibaseProConfiguration.class.getName(), + liquibase.license.LicenseServiceFactory.class.getName(), + liquibase.executor.ExecutorService.class.getName(), + liquibase.change.ChangeFactory.class.getName(), + liquibase.logging.core.LogServiceFactory.class.getName(), + liquibase.logging.LogFactory.class.getName(), + liquibase.change.ColumnConfig.class.getName(), + liquibase.change.AddColumnConfig.class.getName(), + liquibase.change.core.LoadDataColumnConfig.class.getName(), + liquibase.sql.visitor.PrependSqlVisitor.class.getName(), + liquibase.sql.visitor.ReplaceSqlVisitor.class.getName(), + liquibase.sql.visitor.AppendSqlVisitor.class.getName(), + liquibase.sql.visitor.RegExpReplaceSqlVisitor.class.getName())); + + reflective.produce(new ReflectiveClassBuildItem(false, false, true, + liquibase.change.ConstraintsConfig.class.getName())); + + // register classes marked with @DatabaseChangeProperty for reflection + Set classesMarkedWithDatabaseChangeProperty = new HashSet<>(); + for (AnnotationInstance databaseChangePropertyInstance : combinedIndex.getIndex() + .getAnnotations(DATABASE_CHANGE_PROPERTY)) { + // the annotation is only supported on methods but let's be safe + AnnotationTarget annotationTarget = databaseChangePropertyInstance.target(); + if (annotationTarget.kind() == AnnotationTarget.Kind.METHOD) { + classesMarkedWithDatabaseChangeProperty.add(annotationTarget.asMethod().declaringClass().name().toString()); + } + } + reflective.produce( + new ReflectiveClassBuildItem(true, true, true, classesMarkedWithDatabaseChangeProperty.toArray(new String[0]))); + + resource.produce( + new NativeImageResourceBuildItem(getChangeLogs(liquibaseBuildConfig).toArray(new String[0]))); + + Stream.of(liquibase.change.Change.class, + liquibase.changelog.ChangeLogHistoryService.class, + liquibase.command.LiquibaseCommand.class, + liquibase.database.Database.class, + liquibase.database.DatabaseConnection.class, + liquibase.datatype.LiquibaseDataType.class, + liquibase.diff.compare.DatabaseObjectComparator.class, + liquibase.diff.DiffGenerator.class, + liquibase.diff.output.changelog.ChangeGenerator.class, + liquibase.executor.Executor.class, + liquibase.license.LicenseService.class, + liquibase.lockservice.LockService.class, + liquibase.logging.LogService.class, + liquibase.parser.ChangeLogParser.class, + liquibase.parser.NamespaceDetails.class, + liquibase.parser.SnapshotParser.class, + liquibase.precondition.Precondition.class, + liquibase.serializer.ChangeLogSerializer.class, + liquibase.serializer.SnapshotSerializer.class, + liquibase.servicelocator.ServiceLocator.class, + liquibase.snapshot.SnapshotGenerator.class, + liquibase.sqlgenerator.SqlGenerator.class, + liquibase.structure.DatabaseObject.class, + liquibase.hub.HubService.class) + .forEach(t -> addService(services, reflective, t, false)); + + // Register Precondition services, and the implementation class for reflection while also registering fields for reflection + addService(services, reflective, liquibase.precondition.Precondition.class, true); + + // liquibase XSD + resource.produce(new NativeImageResourceBuildItem( + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd", + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd", + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.7.xsd", + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd", + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.9.xsd", + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.10.xsd", + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.0.xsd", + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd", + "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd", + "www.liquibase.org/xml/ns/pro/liquibase-pro-3.8.xsd", + "www.liquibase.org/xml/ns/pro/liquibase-pro-3.9.xsd", + "www.liquibase.org/xml/ns/pro/liquibase-pro-3.10.xsd", + "www.liquibase.org/xml/ns/pro/liquibase-pro-4.0.xsd", + "www.liquibase.org/xml/ns/pro/liquibase-pro-4.1.xsd", + "liquibase.build.properties")); + + // liquibase resource bundles + resourceBundle.produce(new NativeImageResourceBundleBuildItem("liquibase/i18n/liquibase-core")); + } + + private void addService(BuildProducer services, + BuildProducer reflective, Class serviceClass, + boolean shouldRegisterFieldForReflection) { + try { + String service = "META-INF/services/" + serviceClass.getName(); + Set implementations = ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(), + service); + services.produce(new ServiceProviderBuildItem(serviceClass.getName(), implementations.toArray(new String[0]))); + + reflective.produce(new ReflectiveClassBuildItem(true, true, shouldRegisterFieldForReflection, + implementations.toArray(new String[0]))); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void createBeans(LiquibaseMongodbRecorder recorder, + LiquibaseMongodbConfig liquibaseMongodbConfig, + LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig, + MongodbConfig mongodbConfig, + BuildProducer syntheticBeanBuildItemBuildProducer) { + + SyntheticBeanBuildItem.ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem + .configure(LiquibaseMongodbFactory.class) + .scope(ApplicationScoped.class) // this is what the existing code does, but it doesn't seem reasonable + .setRuntimeInit() + .unremovable() + .supplier(recorder.liquibaseSupplier(liquibaseMongodbConfig, liquibaseMongodbBuildTimeConfig, mongodbConfig)); + + syntheticBeanBuildItemBuildProducer.produce(configurator.done()); + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + @Consume(SyntheticBeansRuntimeInitBuildItem.class) + ServiceStartBuildItem startLiquibase(LiquibaseMongodbRecorder recorder) { + // will actually run the actions at runtime + recorder.doStartActions(); + + return new ServiceStartBuildItem("liquibase-mongodb"); + } + + /** + * Collect the configured changeLog file for the default and all named datasources. + *

    + * A {@link LinkedHashSet} is used to avoid duplications. + */ + private List getChangeLogs(LiquibaseMongodbBuildTimeConfig liquibaseBuildConfig) { + ChangeLogParameters changeLogParameters = new ChangeLogParameters(); + ClassLoaderResourceAccessor classLoaderResourceAccessor = new ClassLoaderResourceAccessor( + Thread.currentThread().getContextClassLoader()); + + ChangeLogParserFactory changeLogParserFactory = ChangeLogParserFactory.getInstance(); + + Set resources = new LinkedHashSet<>(); + + resources.addAll(findAllChangeLogFiles(liquibaseBuildConfig.changeLog, changeLogParserFactory, + classLoaderResourceAccessor, changeLogParameters)); + + LOGGER.debugf("Liquibase changeLogs: %s", resources); + + return new ArrayList<>(resources); + } + + /** + * Finds all resource files for the given change log file + */ + private Set findAllChangeLogFiles(String file, ChangeLogParserFactory changeLogParserFactory, + ClassLoaderResourceAccessor classLoaderResourceAccessor, + ChangeLogParameters changeLogParameters) { + try { + ChangeLogParser parser = changeLogParserFactory.getParser(file, classLoaderResourceAccessor); + DatabaseChangeLog changelog = parser.parse(file, changeLogParameters, classLoaderResourceAccessor); + + if (changelog != null) { + Set result = new LinkedHashSet<>(); + // get all changeSet files + for (ChangeSet changeSet : changelog.getChangeSets()) { + result.add(changeSet.getFilePath()); + + changeSet.getChanges().stream() + .map(change -> extractChangeFile(change, changeSet.getFilePath())) + .forEach(changeFile -> changeFile.ifPresent(result::add)); + + // get all parents of the changeSet + DatabaseChangeLog parent = changeSet.getChangeLog(); + while (parent != null) { + result.add(parent.getFilePath()); + parent = parent.getParentChangeLog(); + } + } + result.add(changelog.getFilePath()); + return result; + } + } catch (LiquibaseException ex) { + throw new IllegalStateException(ex); + } + return Collections.emptySet(); + } + + private Optional extractChangeFile(Change change, String changeSetFilePath) { + String path = null; + Boolean relative = null; + if (change instanceof LoadDataChange) { + LoadDataChange loadDataChange = (LoadDataChange) change; + path = loadDataChange.getFile(); + relative = loadDataChange.isRelativeToChangelogFile(); + } else if (change instanceof SQLFileChange) { + SQLFileChange sqlFileChange = (SQLFileChange) change; + path = sqlFileChange.getPath(); + relative = sqlFileChange.isRelativeToChangelogFile(); + } else if (change instanceof CreateProcedureChange) { + CreateProcedureChange createProcedureChange = (CreateProcedureChange) change; + path = createProcedureChange.getPath(); + relative = createProcedureChange.isRelativeToChangelogFile(); + } else if (change instanceof CreateViewChange) { + CreateViewChange createViewChange = (CreateViewChange) change; + path = createViewChange.getPath(); + relative = createViewChange.getRelativeToChangelogFile(); + } + + // unrelated change or change does not reference a file (e.g. inline view) + if (path == null) { + return Optional.empty(); + } + // absolute file path or changeSet has no file path + if (relative == null || !relative || changeSetFilePath == null) { + return Optional.of(path); + } + + // relative file path needs to be resolved against changeSetFilePath + // notes: ClassLoaderResourceAccessor does not provide a suitable method and CLRA.getFinalPath() is not visible + return Optional.of(Paths.get(changeSetFilePath).resolveSibling(path).toString().replace('\\', '/')); + } +} diff --git a/extensions/liquibase-mongodb/pom.xml b/extensions/liquibase-mongodb/pom.xml new file mode 100644 index 00000000000000..f9432f43440cb1 --- /dev/null +++ b/extensions/liquibase-mongodb/pom.xml @@ -0,0 +1,22 @@ + + + + quarkus-extensions-parent + io.quarkus + 999-SNAPSHOT + ../pom.xml + + 4.0.0 + + quarkus-liquibase-mongodb-parent + Quarkus - Liquibase MongoDB + pom + + + runtime + deployment + + + \ No newline at end of file diff --git a/extensions/liquibase-mongodb/runtime/pom.xml b/extensions/liquibase-mongodb/runtime/pom.xml new file mode 100644 index 00000000000000..e027988136da56 --- /dev/null +++ b/extensions/liquibase-mongodb/runtime/pom.xml @@ -0,0 +1,71 @@ + + + + quarkus-liquibase-mongodb-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-liquibase-mongodb + Quarkus - Liquibase MongoDB - Runtime + Handle your MongoDB schema migrations with Liquibase + + + io.quarkus + quarkus-mongodb-client + + + org.liquibase + liquibase-core + + + org.yaml + snakeyaml + + + org.liquibase.ext + liquibase-mongodb + + + org.graalvm.nativeimage + svm + provided + + + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + io.quarkus.liquibase.mongodb + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/LiquibaseMongodbFactory.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/LiquibaseMongodbFactory.java new file mode 100644 index 00000000000000..5369cfdad6b3bf --- /dev/null +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/LiquibaseMongodbFactory.java @@ -0,0 +1,95 @@ +package io.quarkus.liquibase; + +import java.util.Map; +import java.util.regex.Pattern; + +import io.quarkus.liquibase.runtime.LiquibaseMongodbBuildTimeConfig; +import io.quarkus.liquibase.runtime.LiquibaseMongodbConfig; +import io.quarkus.mongodb.runtime.MongoClientConfig; +import liquibase.Contexts; +import liquibase.LabelExpression; +import liquibase.Liquibase; +import liquibase.database.Database; +import liquibase.database.DatabaseFactory; +import liquibase.resource.ClassLoaderResourceAccessor; +import liquibase.resource.ResourceAccessor; + +public class LiquibaseMongodbFactory { + + private final MongoClientConfig mongoClientConfig; + private final LiquibaseMongodbConfig liquibaseMongodbConfig; + private final LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig; + + //connection-string format, see https://docs.mongodb.com/manual/reference/connection-string/ + Pattern HAS_DB = Pattern.compile("(mongodb|mongodb\\+srv)://[^/]*/.*"); + + public LiquibaseMongodbFactory(LiquibaseMongodbConfig config, + LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig, MongoClientConfig mongoClientConfig) { + this.liquibaseMongodbConfig = config; + this.liquibaseMongodbBuildTimeConfig = liquibaseMongodbBuildTimeConfig; + this.mongoClientConfig = mongoClientConfig; + } + + public Liquibase createLiquibase() { + try { + ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(Thread.currentThread().getContextClassLoader()); + String connectionString = this.mongoClientConfig.connectionString.orElse("mongodb://localhost:27017"); + if (!HAS_DB.matcher(connectionString).matches()) { + connectionString += "/" + this.mongoClientConfig.database.orElseThrow( + () -> new IllegalArgumentException( + "Config property 'quarkus.mongodb.database' must be defined when no database " + + "exist in the connection string")); + } + Database database = DatabaseFactory.getInstance().openDatabase(connectionString, + this.mongoClientConfig.credentials.username.orElse(null), + this.mongoClientConfig.credentials.password.orElse(null), + null, resourceAccessor); + + ; + if (database != null) { + liquibaseMongodbConfig.liquibaseCatalogName.ifPresent(database::setLiquibaseCatalogName); + liquibaseMongodbConfig.liquibaseSchemaName.ifPresent(database::setLiquibaseSchemaName); + liquibaseMongodbConfig.liquibaseTablespaceName.ifPresent(database::setLiquibaseTablespaceName); + + if (liquibaseMongodbConfig.defaultCatalogName.isPresent()) { + database.setDefaultCatalogName(liquibaseMongodbConfig.defaultCatalogName.get()); + } + if (liquibaseMongodbConfig.defaultSchemaName.isPresent()) { + database.setDefaultSchemaName(liquibaseMongodbConfig.defaultSchemaName.get()); + } + } + Liquibase liquibase = new Liquibase(liquibaseMongodbBuildTimeConfig.changeLog, resourceAccessor, database); + + for (Map.Entry entry : liquibaseMongodbConfig.changeLogParameters.entrySet()) { + liquibase.getChangeLogParameters().set(entry.getKey(), entry.getValue()); + } + + return liquibase; + + } catch (Exception ex) { + throw new IllegalStateException(ex); + } + } + + public LiquibaseMongodbConfig getConfiguration() { + return liquibaseMongodbConfig; + } + + /** + * Creates the default labels base on the configuration + * + * @return the label expression + */ + public LabelExpression createLabels() { + return new LabelExpression(liquibaseMongodbConfig.labels.orElse(null)); + } + + /** + * Creates the default contexts base on the configuration + * + * @return the contexts + */ + public Contexts createContexts() { + return new Contexts(liquibaseMongodbConfig.contexts.orElse(null)); + } +} diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbBuildTimeConfig.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbBuildTimeConfig.java new file mode 100644 index 00000000000000..9bd5432a0f75e2 --- /dev/null +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbBuildTimeConfig.java @@ -0,0 +1,16 @@ +package io.quarkus.liquibase.runtime; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +/** + * The liquibase configuration + */ +@ConfigRoot(name = "liquibase-mongodb", phase = ConfigPhase.BUILD_TIME) +public class LiquibaseMongodbBuildTimeConfig { + + /** + * The change log file + */ + public String changeLog = "db/changeLog.xml"; +} diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbConfig.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbConfig.java new file mode 100644 index 00000000000000..6cab1009c19d7d --- /dev/null +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbConfig.java @@ -0,0 +1,68 @@ +package io.quarkus.liquibase.runtime; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + +/** + * The liquibase configuration + */ +@ConfigRoot(name = "liquibase-mongodb", phase = ConfigPhase.RUN_TIME) +public class LiquibaseMongodbConfig { + + /** + * The migrate at start flag + */ + public boolean migrateAtStart = false; + + /** + * The validate on update flag + */ + public boolean validateOnMigrate = true; + + /** + * The clean at start flag + */ + public boolean cleanAtStart = false; + + public Map changeLogParameters = null; + + /** + * The list of contexts + */ + public Optional> contexts = null; + + /** + * The list of labels + */ + public Optional> labels = null; + + /** + * The default catalog name + */ + public Optional defaultCatalogName = Optional.empty(); + + /** + * The default schema name + */ + public Optional defaultSchemaName = Optional.empty(); + + /** + * The liquibase tables catalog name + */ + public Optional liquibaseCatalogName = Optional.empty(); + + /** + * The liquibase tables schema name + */ + public Optional liquibaseSchemaName = Optional.empty(); + + /** + * The liquibase tables tablespace name + */ + public Optional liquibaseTablespaceName = Optional.empty(); + +} diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbRecorder.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbRecorder.java new file mode 100644 index 00000000000000..84759f8ac1e6cd --- /dev/null +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbRecorder.java @@ -0,0 +1,63 @@ +package io.quarkus.liquibase.runtime; + +import java.util.function.Supplier; + +import javax.enterprise.inject.Any; +import javax.enterprise.inject.UnsatisfiedResolutionException; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InjectableInstance; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.liquibase.LiquibaseMongodbFactory; +import io.quarkus.mongodb.runtime.MongodbConfig; +import io.quarkus.runtime.annotations.Recorder; +import liquibase.Liquibase; + +@Recorder +public class LiquibaseMongodbRecorder { + + public Supplier liquibaseSupplier(LiquibaseMongodbConfig config, + LiquibaseMongodbBuildTimeConfig buildTimeConfig, MongodbConfig mongodbConfig) { + return new Supplier() { + @Override + public LiquibaseMongodbFactory get() { + return new LiquibaseMongodbFactory(config, buildTimeConfig, mongodbConfig.defaultMongoClientConfig); + } + }; + } + + public void doStartActions() { + try { + InjectableInstance liquibaseFactoryInstance = Arc.container() + .select(LiquibaseMongodbFactory.class, Any.Literal.INSTANCE); + if (liquibaseFactoryInstance.isUnsatisfied()) { + return; + } + + for (InstanceHandle liquibaseFactoryHandle : liquibaseFactoryInstance.handles()) { + try { + LiquibaseMongodbFactory liquibaseFactory = liquibaseFactoryHandle.get(); + if (liquibaseFactory.getConfiguration().cleanAtStart) { + try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { + liquibase.dropAll(); + } + } + if (liquibaseFactory.getConfiguration().migrateAtStart) { + if (liquibaseFactory.getConfiguration().validateOnMigrate) { + try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { + liquibase.validate(); + } + } + try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { + liquibase.update(liquibaseFactory.createContexts(), liquibaseFactory.createLabels()); + } + } + } catch (UnsatisfiedResolutionException e) { + //ignore, the DS is not configured + } + } + } catch (Exception e) { + throw new IllegalStateException("Error starting Liquibase", e); + } + } +} diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/graal/SubstituteStringUtil.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/graal/SubstituteStringUtil.java new file mode 100644 index 00000000000000..d4f28a00e8464f --- /dev/null +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/graal/SubstituteStringUtil.java @@ -0,0 +1,37 @@ +package io.quarkus.liquibase.runtime.graal; + +import java.security.SecureRandom; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.InjectAccessors; +import com.oracle.svm.core.annotate.TargetClass; + +@TargetClass(className = "liquibase.util.StringUtil") +final class SubstituteStringUtil { + + @Alias + @InjectAccessors(SecureRandomAccessors.class) + private static SecureRandom rnd; + + public static final class SecureRandomAccessors { + + private static volatile SecureRandom volatileRandom; + + public static SecureRandom get() { + SecureRandom localVolatileRandom = volatileRandom; + if (localVolatileRandom == null) { + synchronized (SecureRandomAccessors.class) { + localVolatileRandom = volatileRandom; + if (localVolatileRandom == null) { + volatileRandom = localVolatileRandom = new SecureRandom(); + } + } + } + return localVolatileRandom; + } + + public static void set(SecureRandom rnd) { + throw new IllegalStateException("The setter for liquibase.util.StringUtil#rnd shouldn't be called."); + } + } +} diff --git a/extensions/liquibase-mongodb/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/liquibase-mongodb/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 00000000000000..8cacc679b79752 --- /dev/null +++ b/extensions/liquibase-mongodb/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,11 @@ +--- +artifact: ${project.groupId}:${project.artifactId}:${project.version} +name: "Liquibase MongoDB" +metadata: + keywords: + - "liquibase" + - "mongodb" + - "data" + categories: + - "data" + status: "experimental" \ No newline at end of file diff --git a/extensions/pom.xml b/extensions/pom.xml index 03e4923b78cb8f..d20956728e9eac 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -175,6 +175,7 @@ flyway liquibase + liquibase-mongodb vault diff --git a/integration-tests/liquibase-mongodb/pom.xml b/integration-tests/liquibase-mongodb/pom.xml new file mode 100644 index 00000000000000..dc1d8393d2d02e --- /dev/null +++ b/integration-tests/liquibase-mongodb/pom.xml @@ -0,0 +1,106 @@ + + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-integration-test-liquibase-mongodb + Quarkus - Integration Tests - Liquibase MongoDB + Module that contains Liquibase MongoDB related tests + + + + io.quarkus + quarkus-liquibase-mongodb + + + io.quarkus + quarkus-mongodb-panache + + + io.quarkus + quarkus-resteasy-jackson + + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-test-mongodb + test + + + + + io.quarkus + quarkus-liquibase-mongodb-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-mongodb-panache-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-resteasy-jackson-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + + \ No newline at end of file diff --git a/integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/Fruit.java b/integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/Fruit.java new file mode 100644 index 00000000000000..b5d0483404c0e2 --- /dev/null +++ b/integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/Fruit.java @@ -0,0 +1,8 @@ +package io.quarkus.it.liquibase.mongodb; + +import io.quarkus.mongodb.panache.PanacheMongoEntity; + +public class Fruit extends PanacheMongoEntity { + public String name; + public String color; +} diff --git a/integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/FruitResource.java b/integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/FruitResource.java new file mode 100644 index 00000000000000..cdbb0a4efc69c2 --- /dev/null +++ b/integration-tests/liquibase-mongodb/src/main/java/io/quarkus/it/liquibase/mongodb/FruitResource.java @@ -0,0 +1,33 @@ +package io.quarkus.it.liquibase.mongodb; + +import java.util.List; + +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.bson.types.ObjectId; + +@Path("/fruits") +@Produces(MediaType.APPLICATION_JSON) +public class FruitResource { + + @GET + public List list() { + return Fruit.listAll(); + } + + @GET + @Path("/{id}") + public Fruit get(@PathParam("id") String id) { + return Fruit.findById(new ObjectId(id)); + } + + @POST + public void save(Fruit fruit) { + fruit.persist(); + } +} diff --git a/integration-tests/liquibase-mongodb/src/main/resources/application.properties b/integration-tests/liquibase-mongodb/src/main/resources/application.properties new file mode 100644 index 00000000000000..9a40534908c6fb --- /dev/null +++ b/integration-tests/liquibase-mongodb/src/main/resources/application.properties @@ -0,0 +1,4 @@ +quarkus.mongodb.connection-string=mongodb://localhost:27017 +quarkus.mongodb.database=fruits +quarkus.liquibase-mongodb.change-log=liquibase/changelog.xml +quarkus.liquibase-mongodb.migrate-at-start=true \ No newline at end of file diff --git a/integration-tests/liquibase-mongodb/src/main/resources/liquibase/changelog.xml b/integration-tests/liquibase-mongodb/src/main/resources/liquibase/changelog.xml new file mode 100644 index 00000000000000..18bd43e5611b40 --- /dev/null +++ b/integration-tests/liquibase-mongodb/src/main/resources/liquibase/changelog.xml @@ -0,0 +1,23 @@ + + + + + + + + {color: 1} + {name: "colorIdx"} + + + + {"name":"orange", "color": "orange"} + + + + + \ No newline at end of file diff --git a/integration-tests/liquibase-mongodb/src/test/java/io/quarkus/it/liquibase/mongodb/FruitResourceTest.java b/integration-tests/liquibase-mongodb/src/test/java/io/quarkus/it/liquibase/mongodb/FruitResourceTest.java new file mode 100644 index 00000000000000..6f9a203d3ec4fe --- /dev/null +++ b/integration-tests/liquibase-mongodb/src/test/java/io/quarkus/it/liquibase/mongodb/FruitResourceTest.java @@ -0,0 +1,51 @@ +package io.quarkus.it.liquibase.mongodb; + +import static io.restassured.RestAssured.get; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import javax.inject.Inject; + +import org.bson.Document; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import com.mongodb.client.ListIndexesIterable; +import com.mongodb.client.MongoClient; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.mongodb.MongoTestResource; +import io.restassured.common.mapper.TypeRef; + +@QuarkusTest +@QuarkusTestResource(MongoTestResource.class) +@DisabledOnOs(OS.WINDOWS) +class FruitResourceTest { + + @Inject + MongoClient mongoClient; + + @Test + public void testTheEndpoint() { + // assert that a fruit exist as one has been created in the changelog + List list = get("/fruits").as(new TypeRef>() { + }); + Assertions.assertEquals(1, list.size()); + } + + @Test + public void validateTheIdx() { + // check that the index that the changelog created exist + ListIndexesIterable indexes = mongoClient.getDatabase("fruits").getCollection("Fruit").listIndexes(); + Set names = StreamSupport.stream(indexes.spliterator(), false) + .map(doc -> doc.getString("name")) + .collect(Collectors.toSet()); + Assertions.assertTrue(names.contains("colorIdx")); + } +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 85cd285c932c9c..fb683ab4e17823 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -180,6 +180,7 @@ elytron-undertow flyway liquibase + liquibase-mongodb oidc oidc-client oidc-client-reactive From 91433f8fd7bd3a4039180d06a1c6d2715bf30581 Mon Sep 17 00:00:00 2001 From: Robbie Gemmell Date: Tue, 31 Aug 2021 13:08:33 +0100 Subject: [PATCH 022/138] update to proton-j 0.33.9 --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 5a28a7cab7d310..e0edbdb8a77836 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -165,7 +165,7 @@ 4.3.1 1.2.1 2.17.0 - 0.33.8 + 0.33.9 3.14.9 5.1.2 0.1.0 From 20459edf035fc15d0431a336522b6ae2ec61ca45 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 30 Aug 2021 15:53:35 +0300 Subject: [PATCH 023/138] Allow RESTEasy Reactive to index JAX-RS Resources generated by other extensions --- .../JaxRsResourceIndexBuildItem.java | 23 ++++++ .../ResteasyReactiveCommonProcessor.java | 28 ++++++- .../spi/GeneratedJaxRsResourceBuildItem.java | 26 ++++++ .../GeneratedJaxRsResourceGizmoAdaptor.java | 20 +++++ .../GeneratedJaxRsResourceTest.java | 82 +++++++++++++++++++ 5 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/JaxRsResourceIndexBuildItem.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/GeneratedJaxRsResourceBuildItem.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/GeneratedJaxRsResourceGizmoAdaptor.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/generatedresource/GeneratedJaxRsResourceTest.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/JaxRsResourceIndexBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/JaxRsResourceIndexBuildItem.java new file mode 100644 index 00000000000000..7d69199960335c --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/JaxRsResourceIndexBuildItem.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.common.deployment; + +import org.jboss.jandex.IndexView; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * Represents the index that is going to be used to look up JAX-RS Resources. + * This index contains both code that is present on disk at build time, + * and code that is generated by other extensions. + */ +public final class JaxRsResourceIndexBuildItem extends SimpleBuildItem { + + private final IndexView indexView; + + public JaxRsResourceIndexBuildItem(IndexView indexView) { + this.indexView = indexView; + } + + public IndexView getIndexView() { + return indexView; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java index 4a8ca5145da6c7..41f0c5d41c517d 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java @@ -1,5 +1,7 @@ package io.quarkus.resteasy.reactive.common.deployment; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -17,8 +19,10 @@ import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.CompositeIndex; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; +import org.jboss.jandex.Indexer; import org.jboss.jandex.Type; import org.jboss.resteasy.reactive.common.jaxrs.RuntimeDelegateImpl; import org.jboss.resteasy.reactive.common.model.InterceptorContainer; @@ -36,6 +40,7 @@ import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.arc.deployment.GeneratedBeanBuildItem; import io.quarkus.arc.deployment.PreAdditionalBeanBuildTimeConditionBuildItem; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -48,6 +53,7 @@ import io.quarkus.resteasy.reactive.spi.AbstractInterceptorBuildItem; import io.quarkus.resteasy.reactive.spi.ContainerRequestFilterBuildItem; import io.quarkus.resteasy.reactive.spi.ContainerResponseFilterBuildItem; +import io.quarkus.resteasy.reactive.spi.GeneratedJaxRsResourceBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyReaderOverrideBuildItem; import io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem; @@ -212,14 +218,30 @@ private boolean namePresent(Set nameBindingNames, Set globalName return false; } + @BuildStep + JaxRsResourceIndexBuildItem resourceIndex(CombinedIndexBuildItem combinedIndex, + List generatedJaxRsResources, + BuildProducer generatedBeansProducer) throws IOException { + if (generatedJaxRsResources.isEmpty()) { + return new JaxRsResourceIndexBuildItem(combinedIndex.getComputingIndex()); + } + + Indexer indexer = new Indexer(); + for (GeneratedJaxRsResourceBuildItem generatedJaxRsResource : generatedJaxRsResources) { + indexer.index(new ByteArrayInputStream(generatedJaxRsResource.getData())); + generatedBeansProducer + .produce(new GeneratedBeanBuildItem(generatedJaxRsResource.getName(), generatedJaxRsResource.getData())); + } + return new JaxRsResourceIndexBuildItem(CompositeIndex.create(combinedIndex.getComputingIndex(), indexer.complete())); + } + @BuildStep void scanResources( - // TODO: We need to use this index instead of BeanArchiveIndexBuildItem to avoid build cycles. It it OK? - CombinedIndexBuildItem combinedIndexBuildItem, + JaxRsResourceIndexBuildItem jaxRsResourceIndexBuildItem, BuildProducer annotationsTransformerBuildItemBuildProducer, BuildProducer resourceScanningResultBuildItemBuildProducer) { - ResourceScanningResult res = ResteasyReactiveScanner.scanResources(combinedIndexBuildItem.getComputingIndex()); + ResourceScanningResult res = ResteasyReactiveScanner.scanResources(jaxRsResourceIndexBuildItem.getIndexView()); if (res == null) { return; } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/GeneratedJaxRsResourceBuildItem.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/GeneratedJaxRsResourceBuildItem.java new file mode 100644 index 00000000000000..3c8bb89853e846 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/spi-deployment/src/main/java/io/quarkus/resteasy/reactive/spi/GeneratedJaxRsResourceBuildItem.java @@ -0,0 +1,26 @@ +package io.quarkus.resteasy.reactive.spi; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * Represents a JAX-RS resource that is generated. + * Meant to be used by extension that generate JAX-RS resources as part of their build time processing + */ +public final class GeneratedJaxRsResourceBuildItem extends MultiBuildItem { + + private final String name; + private final byte[] data; + + public GeneratedJaxRsResourceBuildItem(String name, byte[] data) { + this.name = name; + this.data = data; + } + + public String getName() { + return name; + } + + public byte[] getData() { + return data; + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/GeneratedJaxRsResourceGizmoAdaptor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/GeneratedJaxRsResourceGizmoAdaptor.java new file mode 100644 index 00000000000000..331150eeb0b841 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/GeneratedJaxRsResourceGizmoAdaptor.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.gizmo.ClassOutput; +import io.quarkus.resteasy.reactive.spi.GeneratedJaxRsResourceBuildItem; + +public class GeneratedJaxRsResourceGizmoAdaptor implements ClassOutput { + + private final BuildProducer classOutput; + + public GeneratedJaxRsResourceGizmoAdaptor(BuildProducer classOutput) { + this.classOutput = classOutput; + } + + @Override + public void write(String className, byte[] bytes) { + classOutput.produce(new GeneratedJaxRsResourceBuildItem(className, bytes)); + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/generatedresource/GeneratedJaxRsResourceTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/generatedresource/GeneratedJaxRsResourceTest.java new file mode 100644 index 00000000000000..a5c97b442f7120 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/generatedresource/GeneratedJaxRsResourceTest.java @@ -0,0 +1,82 @@ +package io.quarkus.resteasy.reactive.server.test.generatedresource; + +import static io.restassured.RestAssured.when; + +import java.util.function.Consumer; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.hamcrest.Matchers; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.builder.BuildChainBuilder; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.gizmo.ClassCreator; +import io.quarkus.gizmo.ClassOutput; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.resteasy.reactive.server.deployment.GeneratedJaxRsResourceGizmoAdaptor; +import io.quarkus.resteasy.reactive.spi.GeneratedJaxRsResourceBuildItem; +import io.quarkus.test.QuarkusUnitTest; + +public class GeneratedJaxRsResourceTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(HelloResource.class)) + .addBuildChainCustomizer(buildCustomizer()); + + @Test + public void testRestPath() { + when().get("/hello").then().statusCode(200).body(Matchers.is("hello")); + when().get("/test").then().statusCode(200).body(Matchers.is("test")); + } + + protected static Consumer buildCustomizer() { + return new Consumer<>() { + /** + * This represents the extension that generates a JAX-RS resource like: + * + *

    +             * {@code
    +             *      @Path("/test')
    +             *      public class TestResource {
    +             *
    +             *          @GET
    +             *          public String test() {
    +             *              return "test";
    +             *          }
    +             *      }
    +             * }
    +             * 
    + */ + @Override + public void accept(BuildChainBuilder builder) { + builder.addBuildStep(context -> { + BuildProducer producer = context::produce; + ClassOutput classOutput = new GeneratedJaxRsResourceGizmoAdaptor(producer); + try (ClassCreator classCreator = ClassCreator.builder() + .classOutput(classOutput).className("com.example.TestResource") + .build()) { + classCreator.addAnnotation(Path.class).addValue("value", "test"); + MethodCreator methodCreator = classCreator.getMethodCreator("test", String.class); + methodCreator.addAnnotation(GET.class); + methodCreator.returnValue(methodCreator.load("test")); + } + }).produces(GeneratedJaxRsResourceBuildItem.class).build(); + } + }; + } + + @Path("hello") + public static class HelloResource { + + @GET + public String hello() { + return "hello"; + } + } +} From dd6a5f483345570e5affeb089def23f871aa3954 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 31 Aug 2021 09:15:14 +0300 Subject: [PATCH 024/138] Support non-parameterized List method param for Query Param RESTEasy Classic supports this, so let's remain compatible --- .../test/simple/RawListQueryParamTest.java | 56 +++++++++++++++++++ .../common/processor/EndpointIndexer.java | 7 +++ 2 files changed, 63 insertions(+) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/RawListQueryParamTest.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/RawListQueryParamTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/RawListQueryParamTest.java new file mode 100644 index 00000000000000..3fb6ae3effba1b --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/RawListQueryParamTest.java @@ -0,0 +1,56 @@ +package io.quarkus.resteasy.reactive.server.test.simple; + +import java.util.List; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.hamcrest.Matchers; +import org.jboss.resteasy.reactive.RestQuery; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class RawListQueryParamTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(HelloResource.class)); + + @Test + public void noQueryParams() { + RestAssured.get("/hello") + .then().statusCode(200).body(Matchers.equalTo("hello world")); + } + + @Test + public void singleQueryParam() { + RestAssured.get("/hello?name=foo") + .then().statusCode(200).body(Matchers.equalTo("hello foo")); + } + + @Test + public void multipleQueryParams() { + RestAssured.get("/hello?name=foo&name=bar") + .then().statusCode(200).body(Matchers.equalTo("hello foo,bar")); + } + + @Path("hello") + public static class HelloResource { + + @GET + @SuppressWarnings({ "rawtypes", "unchecked" }) + public String hello(@RestQuery("name") List names) { + if (names.isEmpty()) { + return "hello world"; + } + return "hello " + String.join(",", names); + } + + } +} diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java index 0ed75c29ce9afb..7620df556c91d9 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java @@ -1052,6 +1052,13 @@ && isContextType(paramType.asClassType())) { elementType = paramType.name().toString(); handleLocalDateParam(builder); typeHandled = true; + } else if (paramType.name().equals(LIST) && (type == ParameterType.QUERY)) { // RESTEasy Classic handles the non-generic List type + elementType = String.class.getName(); + typeHandled = true; + builder.setSingle(false); + if (convertible) { + handleListParam(existingConverters, errorLocation, hasRuntimeConverters, builder, elementType); + } } if (!typeHandled) { From 036125c872a35b374947746522c7cf065d1dd439 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 31 Aug 2021 17:12:58 +0300 Subject: [PATCH 025/138] Ensure that Violation DTO class is registered in native --- .../deployment/HibernateValidatorProcessor.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 b854dcada8f552..7c0363ef1a5f77 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 @@ -67,6 +67,7 @@ import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem; +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.logging.LogCleanupFilterBuildItem; @@ -349,9 +350,13 @@ NativeImageConfigBuildItem nativeImageConfig() { } @BuildStep - ExceptionMapperBuildItem mapper() { - return new ExceptionMapperBuildItem(ResteasyReactiveViolationExceptionMapper.class.getName(), - ValidationException.class.getName(), Priorities.USER + 1, true); + void exceptionMapper(BuildProducer exceptionMapperProducer, + BuildProducer reflectiveClassProducer) { + exceptionMapperProducer.produce(new ExceptionMapperBuildItem(ResteasyReactiveViolationExceptionMapper.class.getName(), + ValidationException.class.getName(), Priorities.USER + 1, true)); + reflectiveClassProducer.produce( + new ReflectiveClassBuildItem(true, true, ResteasyReactiveViolationExceptionMapper.ViolationReport.class, + ResteasyReactiveViolationExceptionMapper.ViolationReport.Violation.class)); } private static void contributeBuiltinConstraints(Set builtinConstraints, From 6e50ff2a3871e05fce057363c607071600910633 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 31 Aug 2021 14:59:25 +0300 Subject: [PATCH 026/138] Remove query parameters UriInfo#getAbsolutePath in RESTEasy Reactive This is mentioned by the method's Javadoc, even though the TCK doesn't test it --- .../test/resource/basic/UriInfoTest.java | 19 +++++++++++++++++- .../resource/GetAbsolutePathResource.java | 20 +++++++++++++++++++ .../reactive/server/jaxrs/UriInfoImpl.java | 8 +++++++- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GetAbsolutePathResource.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/UriInfoTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/UriInfoTest.java index fa6194b91bb728..6357946c627c04 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/UriInfoTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/UriInfoTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.GetAbsolutePathResource; import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.UriInfoEncodedQueryResource; import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.UriInfoEncodedTemplateResource; import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.UriInfoEscapedMatrParamResource; @@ -54,7 +55,7 @@ public JavaArchive get() { war.addClasses(UriInfoSimpleResource.class, UriInfoEncodedQueryResource.class, UriInfoQueryParamsResource.class, UriInfoSimpleSingletonResource.class, UriInfoEncodedTemplateResource.class, UriInfoEscapedMatrParamResource.class, - UriInfoEncodedTemplateResource.class); + UriInfoEncodedTemplateResource.class, GetAbsolutePathResource.class); return war; } }); @@ -167,4 +168,20 @@ private static void basicTest(String path, String testName) throws Exception { public void testQueryParamsMutability() throws Exception { basicTest("/queryParams?a=a,b", "UriInfoQueryParamsResource"); } + + @Test + @DisplayName("Test Get Absolute Path") + public void testGetAbsolutePath() throws Exception { + doTestGetAbsolutePath("/absolutePath", "unset"); + doTestGetAbsolutePath("/absolutePath?dummy=1234", "1234"); + doTestGetAbsolutePath("/absolutePath?foo=bar&dummy=1234", "1234"); + } + + private void doTestGetAbsolutePath(String path, String expectedDummyHeader) { + String absolutePathHeader = RestAssured.get(path) + .then() + .statusCode(200) + .header("dummy", expectedDummyHeader).extract().header("absolutePath"); + org.assertj.core.api.Assertions.assertThat(absolutePathHeader).endsWith("/absolutePath"); + } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GetAbsolutePathResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GetAbsolutePathResource.java new file mode 100644 index 00000000000000..b35f1bd895ada0 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/GetAbsolutePathResource.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.server.test.resource.basic.resource; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +@Path("absolutePath") +public class GetAbsolutePathResource { + + @GET + public Response response(@QueryParam("dummy") String dummy, @Context UriInfo uriInfo) { + return Response.ok() + .header("absolutePath", uriInfo.getAbsolutePath().toString()) + .header("dummy", dummy == null ? "unset" : dummy) + .build(); + } +} diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java index 5daaa95dca5758..13295cdd4c374f 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java @@ -90,7 +90,13 @@ public UriBuilder getRequestUriBuilder() { public URI getAbsolutePath() { try { // TCK says normalized - return new URI(currentRequest.getAbsoluteURI()).normalize(); + String effectiveURI = currentRequest.getAbsoluteURI(); + int queryParamsIndex = effectiveURI.indexOf('?'); + if (queryParamsIndex > 0) { + // the spec says that getAbsolutePath() does not contain query parameters + effectiveURI = effectiveURI.substring(0, queryParamsIndex); + } + return new URI(effectiveURI).normalize(); } catch (URISyntaxException e) { throw new RuntimeException(e); } From 2103bc8550a1792bef0b02710602ce279516bf74 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 31 Aug 2021 12:22:59 +0300 Subject: [PATCH 027/138] Fix issue with ResourceInfo injection in response filters --- .../deployment/CustomFilterGenerator.java | 3 +- .../test/customproviders/NoTargetTest.java | 90 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/NoTargetTest.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/CustomFilterGenerator.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/CustomFilterGenerator.java index b45231b36da205..73f6888dfd6938 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/CustomFilterGenerator.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/CustomFilterGenerator.java @@ -31,6 +31,7 @@ import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.container.ResourceInfo; import javax.ws.rs.core.Response; import org.jboss.jandex.DotName; @@ -410,7 +411,7 @@ private static ResultHandle getRRReqCtxHandle(MethodCreator filter, ResultHandle private static AssignableResultHandle getResourceInfoHandle(MethodCreator filterMethod, ResultHandle rrReqCtxHandle) { ResultHandle runtimeResourceHandle = GeneratorUtils.runtimeResourceHandle(filterMethod, rrReqCtxHandle); - AssignableResultHandle resourceInfo = filterMethod.createVariable(ResteasyReactiveResourceInfo.class); + AssignableResultHandle resourceInfo = filterMethod.createVariable(ResourceInfo.class); BranchResult ifNullBranch = filterMethod.ifNull(runtimeResourceHandle); ifNullBranch.trueBranch().assign(resourceInfo, ifNullBranch.trueBranch().readStaticField( FieldDescriptor.of(SimpleResourceInfo.NullValues.class, "INSTANCE", SimpleResourceInfo.NullValues.class))); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/NoTargetTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/NoTargetTest.java new file mode 100644 index 00000000000000..4d2bb9a21e58dd --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/customproviders/NoTargetTest.java @@ -0,0 +1,90 @@ +package io.quarkus.resteasy.reactive.server.test.customproviders; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.util.function.Supplier; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.container.ResourceInfo; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.server.ServerResponseFilter; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.http.Headers; + +public class NoTargetTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(HelloResource.class, ThrowingPreMatchFilter.class, DummyExceptionMapper.class); + } + }); + + @Path("hello") + public static class HelloResource { + + @GET + public String hello() { + return "hello"; + } + } + + @Test + public void test() { + Headers headers = RestAssured.get("/hello") + .then().statusCode(200).extract().headers(); + assertEquals("mapper", headers.get("source").getValue()); + assertEquals("NullValues", headers.get("resourceInfoClass").getValue()); + } + + public static class CustomResponseFilter { + + @ServerResponseFilter + public void filter(ContainerResponseContext responseContext, ResourceInfo resourceInfo) { + responseContext.getHeaders().add("resourceInfoClass", resourceInfo.getClass().getSimpleName()); + } + } + + @PreMatching + @Provider + public static class ThrowingPreMatchFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + throw new DummyException(); + } + } + + @Provider + public static class DummyExceptionMapper implements ExceptionMapper { + @Override + public Response toResponse(DummyException exception) { + return Response.ok().header("source", "mapper").build(); + } + } + + public static class DummyException extends RuntimeException { + public DummyException() { + super("dummy"); + setStackTrace(new StackTraceElement[0]); + } + } +} From e504dc1e202e61585769b2350d4be8e9bc391035 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 31 Aug 2021 11:40:56 +0300 Subject: [PATCH 028/138] Use proper type for method comparison in REST Links module --- .../reactive/links/deployment/LinksContainerFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java index 12c0c3eb196e2e..0a28fd73829975 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksContainerFactory.java @@ -120,7 +120,7 @@ private boolean isSameMethod(MethodInfo resourceMethodInfo, ResourceMethod resou List parameterTypes = resourceMethodInfo.parameters(); MethodParameter[] parameters = resourceMethod.getParameters(); for (int i = 0; i < parameters.length; i++) { - if (!parameterTypes.get(i).name().equals(DotName.createSimple(parameters[i].type))) { + if (!parameterTypes.get(i).name().equals(DotName.createSimple(parameters[i].declaredType))) { return false; } } From d5c9cbe5a6b854e5cacf88c3a68b4c046a136919 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 31 Aug 2021 18:05:23 +0300 Subject: [PATCH 029/138] Add reflection registration for REST links module --- .../rest/data/panache/deployment/RestDataProcessor.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java index 5b9ae46ff75d09..32031f065ab66e 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java @@ -12,6 +12,7 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.gizmo.ClassOutput; import io.quarkus.jackson.spi.JacksonModuleBuildItem; @@ -31,6 +32,11 @@ public class RestDataProcessor { + @BuildStep + ReflectiveClassBuildItem registerReflection() { + return new ReflectiveClassBuildItem(true, true, HalLink.class); + } + @BuildStep void implementResources(CombinedIndexBuildItem index, List resourceBuildItems, List resourcePropertiesBuildItems, Capabilities capabilities, From 67912b455bb1308c5b34e269d43f52190b3dd2e8 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 31 Aug 2021 16:39:16 +0300 Subject: [PATCH 030/138] Fix Jackson serialization issue in Panache REST Data Before this change the custom serializer would not take the registered Jackson modules into account --- .../hal/HalEntityWrapperJacksonSerializer.java | 17 +++++++---------- .../runtime/hal/AbstractSerializersTest.java | 14 +++++++++++--- .../runtime/hal/JacksonSerializersTest.java | 7 +++++++ .../runtime/hal/JsonbSerializersTest.java | 7 ++++++- .../data/panache/runtime/hal/PublishedBook.java | 13 +++++++++++++ 5 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/PublishedBook.java diff --git a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/hal/HalEntityWrapperJacksonSerializer.java b/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/hal/HalEntityWrapperJacksonSerializer.java index b0c31d39d09c01..8efc74b8b5d833 100644 --- a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/hal/HalEntityWrapperJacksonSerializer.java +++ b/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/hal/HalEntityWrapperJacksonSerializer.java @@ -32,22 +32,19 @@ public void serialize(HalEntityWrapper wrapper, JsonGenerator generator, Seriali for (BeanPropertyDefinition property : getPropertyDefinitions(serializers, wrapper.getEntity().getClass())) { AnnotatedMember accessor = property.getAccessor(); if (accessor != null) { - writeValue(property.getName(), accessor.getValue(wrapper.getEntity()), generator); + Object value = accessor.getValue(wrapper.getEntity()); + generator.writeFieldName(property.getName()); + if (value == null) { + generator.writeNull(); + } else { + serializers.findValueSerializer(value.getClass()).serialize(value, generator, serializers); + } } } writeLinks(wrapper.getEntity(), generator); generator.writeEndObject(); } - private void writeValue(String name, Object value, JsonGenerator generator) throws IOException { - generator.writeFieldName(name); - if (value == null) { - generator.writeNull(); - } else { - generator.writeObject(value); - } - } - private void writeLinks(Object entity, JsonGenerator generator) throws IOException { Map links = linksExtractor.getLinks(entity); generator.writeFieldName("_links"); diff --git a/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/AbstractSerializersTest.java b/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/AbstractSerializersTest.java index 59ab3f35e74cde..88ae8efea84d26 100644 --- a/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/AbstractSerializersTest.java +++ b/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/AbstractSerializersTest.java @@ -17,7 +17,9 @@ abstract class AbstractSerializersTest { @Test void shouldSerializeOneBook() { - Book book = new Book(1, "Black Swan"); + int id = 1; + String title = "Black Swan"; + Book book = usePublishedBook() ? new PublishedBook(id, title) : new Book(id, title); JsonReader jsonReader = Json.createReader(new StringReader(toJson(new HalEntityWrapper(book)))); assertBook(book, jsonReader.readObject()); @@ -25,7 +27,9 @@ void shouldSerializeOneBook() { @Test void shouldSerializeOneBookWithNullName() { - Book book = new Book(1, null); + int id = 1; + String title = null; + Book book = usePublishedBook() ? new PublishedBook(id, title) : new Book(id, title); JsonReader jsonReader = Json.createReader(new StringReader(toJson(new HalEntityWrapper(book)))); assertBook(book, jsonReader.readObject()); @@ -33,7 +37,9 @@ void shouldSerializeOneBookWithNullName() { @Test void shouldSerializeCollectionOfBooks() { - Book book = new Book(1, "Black Swan"); + int id = 1; + String title = "Black Swan"; + Book book = usePublishedBook() ? new PublishedBook(id, title) : new Book(id, title); HalCollectionWrapper wrapper = new HalCollectionWrapper(Collections.singleton(book), Book.class, "books"); JsonReader jsonReader = Json.createReader(new StringReader(toJson(wrapper))); JsonObject collectionJson = jsonReader.readObject(); @@ -60,4 +66,6 @@ private void assertBook(Book book, JsonObject bookJson) { assertThat(bookLinksJson.getJsonObject("list").getString("href")).isEqualTo("/books"); assertThat(bookLinksJson.getJsonObject("add").getString("href")).isEqualTo("/books"); } + + protected abstract boolean usePublishedBook(); } diff --git a/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/JacksonSerializersTest.java b/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/JacksonSerializersTest.java index cdab327d344746..2b444463d25a5f 100644 --- a/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/JacksonSerializersTest.java +++ b/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/JacksonSerializersTest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; class JacksonSerializersTest extends AbstractSerializersTest { @@ -18,6 +19,7 @@ void setup() { module.addSerializer(HalCollectionWrapper.class, new HalCollectionWrapperJacksonSerializer(new BookHalLinksProvider())); objectMapper.registerModule(module); + objectMapper.registerModule(new JavaTimeModule()); } @Override @@ -28,4 +30,9 @@ String toJson(Object object) { throw new RuntimeException(e); } } + + @Override + protected boolean usePublishedBook() { + return true; + } } diff --git a/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/JsonbSerializersTest.java b/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/JsonbSerializersTest.java index ae87e78a65e0cc..5884f3b5894c73 100644 --- a/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/JsonbSerializersTest.java +++ b/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/JsonbSerializersTest.java @@ -22,4 +22,9 @@ void setup() { String toJson(Object object) { return jsonb.toJson(object); } -} \ No newline at end of file + + @Override + protected boolean usePublishedBook() { + return false; + } +} diff --git a/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/PublishedBook.java b/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/PublishedBook.java new file mode 100644 index 00000000000000..38ff69b5463d1c --- /dev/null +++ b/extensions/panache/rest-data-panache/runtime/src/test/java/io/quarkus/rest/data/panache/runtime/hal/PublishedBook.java @@ -0,0 +1,13 @@ +package io.quarkus.rest.data.panache.runtime.hal; + +import java.time.LocalDate; +import java.time.Month; + +public class PublishedBook extends Book { + + public LocalDate publicationDate = LocalDate.of(2021, Month.AUGUST, 31); + + public PublishedBook(long id, String name) { + super(id, name); + } +} From 149bf56e1083f48e30d15307eedf4c979bfadd7d Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 31 Aug 2021 09:32:37 +0300 Subject: [PATCH 031/138] Port rest-data-panache to RESTEasy Reactive Resolves: #19771 --- .../io/quarkus/deployment/Capability.java | 3 ++ .../deployment/pom.xml | 2 +- .../HibernateOrmPanacheRestProcessor.java | 10 ++-- .../runtime/pom.xml | 4 ++ .../deployment/MongoPanacheRestProcessor.java | 10 ++-- .../rest-data-panache/deployment/pom.xml | 4 +- .../panache/deployment/RestDataProcessor.java | 20 +++----- .../methods/StandardMethodImplementor.java | 17 +++++-- .../panache/rest-data-panache/runtime/pom.xml | 12 +---- .../resource/ResourceLinksProvider.java | 48 +++++++++++-------- .../runtime/pom.xml | 6 +++ .../runtime/pom.xml | 6 +++ .../links/deployment/LinksProcessor.java | 6 +-- .../links/runtime/RestLinksProviderImpl.java | 11 +++-- .../spring-data-rest/deployment/pom.xml | 2 +- .../deployment/SpringDataRestProcessor.java | 10 ++-- extensions/spring-data-rest/runtime/pom.xml | 4 ++ .../hibernate-orm-rest-data-panache/pom.xml | 4 +- .../HibernateOrmRestDataPanacheTest.java | 8 ++-- .../mongodb-rest-data-panache/pom.xml | 4 +- integration-tests/spring-data-rest/pom.xml | 4 +- .../spring/data/rest/SpringDataRestTest.java | 8 ++-- 22 files changed, 120 insertions(+), 83 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java index 88e2c041f9afcd..ad466141e02ac1 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java @@ -50,6 +50,9 @@ public interface Capability { String RESTEASY_MUTINY = RESTEASY + ".mutiny"; String RESTEASY_REACTIVE = RESTEASY + ".reactive"; + String RESTEASY_REACTIVE_JSON = RESTEASY_REACTIVE + ".json"; + String RESTEASY_REACTIVE_JSON_JACKSON = RESTEASY_REACTIVE_JSON + ".jackson"; + String RESTEASY_REACTIVE_JSON_JSONB = RESTEASY_REACTIVE_JSON + ".jsonb"; String JWT = QUARKUS_PREFIX + "jwt"; diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/pom.xml b/extensions/panache/hibernate-orm-rest-data-panache/deployment/pom.xml index 0a23562bece626..da7f91a34c697e 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/pom.xml +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/pom.xml @@ -37,7 +37,7 @@
    io.quarkus - quarkus-resteasy-jsonb-deployment + quarkus-resteasy-reactive-jsonb-deployment test diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java index 5af71d2b4b5112..72c049a936f071 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/HibernateOrmPanacheRestProcessor.java @@ -5,6 +5,8 @@ import java.lang.reflect.Modifier; import java.util.List; +import javax.ws.rs.Priorities; + import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; @@ -23,9 +25,10 @@ import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; import io.quarkus.hibernate.orm.rest.data.panache.runtime.RestDataPanacheExceptionMapper; import io.quarkus.hibernate.orm.rest.data.panache.runtime.jta.TransactionalUpdateExecutor; +import io.quarkus.rest.data.panache.RestDataPanacheException; import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.RestDataResourceBuildItem; -import io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem; +import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem; class HibernateOrmPanacheRestProcessor { @@ -41,8 +44,9 @@ FeatureBuildItem feature() { } @BuildStep - ResteasyJaxrsProviderBuildItem registerRestDataPanacheExceptionMapper() { - return new ResteasyJaxrsProviderBuildItem(RestDataPanacheExceptionMapper.class.getName()); + ExceptionMapperBuildItem registerRestDataPanacheExceptionMapper() { + return new ExceptionMapperBuildItem(RestDataPanacheExceptionMapper.class.getName(), + RestDataPanacheException.class.getName(), Priorities.USER + 100, false); } @BuildStep diff --git a/extensions/panache/hibernate-orm-rest-data-panache/runtime/pom.xml b/extensions/panache/hibernate-orm-rest-data-panache/runtime/pom.xml index 64b292a742ffe1..b0aa7a2a9e871d 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/runtime/pom.xml +++ b/extensions/panache/hibernate-orm-rest-data-panache/runtime/pom.xml @@ -22,6 +22,10 @@ io.quarkus quarkus-hibernate-orm-panache + + jakarta.validation + jakarta.validation-api + diff --git a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java index 684a70d9ca9a2f..f2764201458b79 100644 --- a/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java +++ b/extensions/panache/mongodb-rest-data-panache/deployment/src/main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java @@ -5,6 +5,8 @@ import java.lang.reflect.Modifier; import java.util.List; +import javax.ws.rs.Priorities; + import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; @@ -27,9 +29,10 @@ import io.quarkus.mongodb.rest.data.panache.PanacheMongoRepositoryResource; import io.quarkus.mongodb.rest.data.panache.runtime.NoopUpdateExecutor; import io.quarkus.mongodb.rest.data.panache.runtime.RestDataPanacheExceptionMapper; +import io.quarkus.rest.data.panache.RestDataPanacheException; import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.RestDataResourceBuildItem; -import io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem; +import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem; class MongoPanacheRestProcessor { @@ -45,8 +48,9 @@ FeatureBuildItem feature() { } @BuildStep - ResteasyJaxrsProviderBuildItem registerRestDataPanacheExceptionMapper() { - return new ResteasyJaxrsProviderBuildItem(RestDataPanacheExceptionMapper.class.getName()); + ExceptionMapperBuildItem registerRestDataPanacheExceptionMapper() { + return new ExceptionMapperBuildItem(RestDataPanacheExceptionMapper.class.getName(), + RestDataPanacheException.class.getName(), Priorities.USER + 100, false); } @BuildStep diff --git a/extensions/panache/rest-data-panache/deployment/pom.xml b/extensions/panache/rest-data-panache/deployment/pom.xml index d3b0d7ec113a8a..56e8cd354bce5e 100644 --- a/extensions/panache/rest-data-panache/deployment/pom.xml +++ b/extensions/panache/rest-data-panache/deployment/pom.xml @@ -23,7 +23,7 @@ io.quarkus - quarkus-resteasy-deployment + quarkus-resteasy-reactive-links-deployment io.quarkus @@ -66,4 +66,4 @@ - \ No newline at end of file + diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java index 32031f065ab66e..4a26a76f87d24f 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java @@ -3,17 +3,12 @@ import java.util.Arrays; import java.util.List; -import org.jboss.resteasy.links.impl.EL; - -import io.quarkus.arc.deployment.GeneratedBeanBuildItem; -import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; -import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.gizmo.ClassOutput; import io.quarkus.jackson.spi.JacksonModuleBuildItem; import io.quarkus.jsonb.spi.JsonbSerializerBuildItem; @@ -29,6 +24,8 @@ import io.quarkus.rest.data.panache.runtime.hal.HalLink; import io.quarkus.rest.data.panache.runtime.hal.HalLinkJacksonSerializer; import io.quarkus.rest.data.panache.runtime.hal.HalLinkJsonbSerializer; +import io.quarkus.resteasy.reactive.server.deployment.GeneratedJaxRsResourceGizmoAdaptor; +import io.quarkus.resteasy.reactive.spi.GeneratedJaxRsResourceBuildItem; public class RestDataProcessor { @@ -40,8 +37,8 @@ ReflectiveClassBuildItem registerReflection() { @BuildStep void implementResources(CombinedIndexBuildItem index, List resourceBuildItems, List resourcePropertiesBuildItems, Capabilities capabilities, - BuildProducer implementationsProducer) { - ClassOutput classOutput = new GeneratedBeanGizmoAdaptor(implementationsProducer); + BuildProducer implementationsProducer) { + ClassOutput classOutput = new GeneratedJaxRsResourceGizmoAdaptor(implementationsProducer); JaxRsResourceImplementor jaxRsResourceImplementor = new JaxRsResourceImplementor(hasValidatorCapability(capabilities)); ResourcePropertiesProvider resourcePropertiesProvider = new ResourcePropertiesProvider(index.getIndex()); @@ -77,11 +74,6 @@ JsonbSerializerBuildItem registerJsonbSerializers() { HalLinkJsonbSerializer.class.getName())); } - @BuildStep - RuntimeInitializedClassBuildItem el() { - return new RuntimeInitializedClassBuildItem(EL.class.getCanonicalName()); - } - private ResourceProperties getResourceProperties(ResourcePropertiesProvider resourcePropertiesProvider, ResourceMetadata resourceMetadata, List resourcePropertiesBuildItems) { for (ResourcePropertiesBuildItem resourcePropertiesBuildItem : resourcePropertiesBuildItems) { @@ -98,7 +90,7 @@ private boolean hasValidatorCapability(Capabilities capabilities) { } private boolean hasHalCapability(Capabilities capabilities) { - return capabilities.isPresent(Capability.RESTEASY_JSON_JSONB) - || capabilities.isPresent(Capability.RESTEASY_JSON_JACKSON); + return capabilities.isPresent(Capability.RESTEASY_REACTIVE_JSON_JSONB) + || capabilities.isPresent(Capability.RESTEASY_REACTIVE_JSON_JACKSON); } } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java index bc2c937f65c195..eef6fd4c1602dc 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java @@ -12,7 +12,7 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; -import org.jboss.resteasy.links.LinkResource; +import org.jboss.logging.Logger; import io.quarkus.gizmo.AnnotatedElement; import io.quarkus.gizmo.AnnotationCreator; @@ -25,12 +25,15 @@ import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.properties.ResourceProperties; import io.quarkus.rest.data.panache.runtime.sort.SortQueryParamValidator; +import io.quarkus.resteasy.reactive.links.RestLink; /** * A standard JAX-RS method implementor. */ public abstract class StandardMethodImplementor implements MethodImplementor { + private static final Logger LOGGER = Logger.getLogger(StandardMethodImplementor.class); + /** * Implement exposed JAX-RS method. */ @@ -78,9 +81,15 @@ protected void addDeleteAnnotation(AnnotatedElement element) { } protected void addLinksAnnotation(AnnotatedElement element, String entityClassName, String rel) { - AnnotationCreator linkResource = element.addAnnotation(LinkResource.class); - linkResource.addValue("entityClassName", entityClassName); - linkResource.addValue("rel", rel); + AnnotationCreator linkResource = element.addAnnotation(RestLink.class); + Class entityClass; + try { + entityClass = Thread.currentThread().getContextClassLoader().loadClass(entityClassName); + linkResource.addValue("entityType", entityClass); + linkResource.addValue("rel", rel); + } catch (ClassNotFoundException e) { + LOGGER.error("Unable to create links for entity: '" + entityClassName + "'", e); + } } protected void addPathAnnotation(AnnotatedElement element, String value) { diff --git a/extensions/panache/rest-data-panache/runtime/pom.xml b/extensions/panache/rest-data-panache/runtime/pom.xml index 9183fc75e140ce..b6666464f792c8 100644 --- a/extensions/panache/rest-data-panache/runtime/pom.xml +++ b/extensions/panache/rest-data-panache/runtime/pom.xml @@ -19,17 +19,7 @@ io.quarkus - quarkus-resteasy - - - org.jboss.resteasy - resteasy-links - - - jakarta.activation - jakarta.activation-api - - + quarkus-resteasy-reactive-links io.quarkus diff --git a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/resource/ResourceLinksProvider.java b/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/resource/ResourceLinksProvider.java index 7712a422385feb..fcc202f939842f 100644 --- a/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/resource/ResourceLinksProvider.java +++ b/extensions/panache/rest-data-panache/runtime/src/main/java/io/quarkus/rest/data/panache/runtime/resource/ResourceLinksProvider.java @@ -1,41 +1,51 @@ package io.quarkus.rest.data.panache.runtime.resource; +import java.util.Collection; import java.util.HashMap; import java.util.Map; -import org.jboss.resteasy.links.LinksProvider; -import org.jboss.resteasy.links.RESTServiceDiscovery; +import javax.ws.rs.core.Link; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.resteasy.reactive.links.RestLinksProvider; public final class ResourceLinksProvider { private static final String SELF_REF = "self"; public Map getClassLinks(Class className) { - RESTServiceDiscovery links = LinksProvider - .getClassLinksProvider() - .getLinks(className, Thread.currentThread().getContextClassLoader()); - return linksToMap(links); + return linksToMap(restLinksProvider().getTypeLinks(className)); } public Map getInstanceLinks(Object instance) { - RESTServiceDiscovery links = LinksProvider - .getObjectLinksProvider() - .getLinks(instance, Thread.currentThread().getContextClassLoader()); - return linksToMap(links); + return linksToMap(restLinksProvider().getInstanceLinks(instance)); } public String getSelfLink(Object instance) { - RESTServiceDiscovery.AtomLink link = LinksProvider.getObjectLinksProvider() - .getLinks(instance, Thread.currentThread().getContextClassLoader()) - .getLinkForRel(SELF_REF); - return link == null ? null : link.getHref(); + Collection links = restLinksProvider().getInstanceLinks(instance); + for (Link link : links) { + if (SELF_REF.equals(link.getRel())) { + return link.getUri().toString(); + } + } + return null; + } + + private RestLinksProvider restLinksProvider() { + InstanceHandle instance = Arc.container().instance(RestLinksProvider.class); + if (instance.isAvailable()) { + return instance.get(); + } + throw new IllegalStateException("Invalid use of '" + this.getClass().getName() + + "'. No request scope bean found for type '" + ResourceLinksProvider.class.getName() + "'"); } - private Map linksToMap(RESTServiceDiscovery serviceDiscovery) { - Map links = new HashMap<>(serviceDiscovery.size()); - for (RESTServiceDiscovery.AtomLink atomLink : serviceDiscovery) { - links.put(atomLink.getRel(), atomLink.getHref()); + private Map linksToMap(Collection links) { + Map result = new HashMap<>(); + for (Link link : links) { + result.put(link.getRel(), link.getUri().toString()); } - return links; + return result; } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/pom.xml index 4810c69ea7cd47..bf22e85e0524c2 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/pom.xml +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/pom.xml @@ -29,6 +29,12 @@ io.quarkus quarkus-bootstrap-maven-plugin + + + io.quarkus.rest.jackson + io.quarkus.resteasy.reactive.json.jackson + + maven-compiler-plugin diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/pom.xml index fe85ce1a97913e..0032cb13da00d9 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/pom.xml +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jsonb/runtime/pom.xml @@ -29,6 +29,12 @@ io.quarkus quarkus-bootstrap-maven-plugin + + + io.quarkus.rest.jsonb + io.quarkus.resteasy.reactive.json.jsonb + + maven-compiler-plugin diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java index 3f8983032c57e2..c3733000d26f11 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/deployment/src/main/java/io/quarkus/resteasy/reactive/links/deployment/LinksProcessor.java @@ -18,10 +18,10 @@ import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.Record; import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; -import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.gizmo.ClassOutput; +import io.quarkus.resteasy.reactive.common.deployment.JaxRsResourceIndexBuildItem; import io.quarkus.resteasy.reactive.links.RestLinksResponseFilter; import io.quarkus.resteasy.reactive.links.runtime.GetterAccessorsContainer; import io.quarkus.resteasy.reactive.links.runtime.GetterAccessorsContainerRecorder; @@ -44,13 +44,13 @@ void feature(BuildProducer feature) { @BuildStep @Record(STATIC_INIT) - void initializeLinksProvider(CombinedIndexBuildItem indexBuildItem, + void initializeLinksProvider(JaxRsResourceIndexBuildItem indexBuildItem, ResteasyReactiveDeploymentInfoBuildItem deploymentInfoBuildItem, BuildProducer bytecodeTransformersProducer, BuildProducer generatedClassesProducer, GetterAccessorsContainerRecorder getterAccessorsContainerRecorder, LinksProviderRecorder linksProviderRecorder) { - IndexView index = indexBuildItem.getIndex(); + IndexView index = indexBuildItem.getIndexView(); ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClassesProducer, true); // Initialize links container diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderImpl.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderImpl.java index 7a0354d4a6d74e..8960e382906d8d 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderImpl.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-links/runtime/src/main/java/io/quarkus/resteasy/reactive/links/runtime/RestLinksProviderImpl.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.List; import javax.ws.rs.core.Link; @@ -34,8 +33,9 @@ static void setGetterAccessorsContainer(GetterAccessorsContainer getterAccessors public Collection getTypeLinks(Class elementType) { verifyInit(); - List links = new LinkedList<>(); - for (LinkInfo linkInfo : linksContainer.getForClass(elementType)) { + List linkInfoList = linksContainer.getForClass(elementType); + List links = new ArrayList<>(linkInfoList.size()); + for (LinkInfo linkInfo : linkInfoList) { if (linkInfo.getPathParameters().size() == 0) { links.add(Link.fromUriBuilder(uriInfo.getBaseUriBuilder().path(linkInfo.getPath())) .rel(linkInfo.getRel()) @@ -49,8 +49,9 @@ public Collection getTypeLinks(Class elementType) { public Collection getInstanceLinks(T instance) { verifyInit(); - List links = new LinkedList<>(); - for (LinkInfo linkInfo : linksContainer.getForClass(instance.getClass())) { + List linkInfoList = linksContainer.getForClass(instance.getClass()); + List links = new ArrayList<>(linkInfoList.size()); + for (LinkInfo linkInfo : linkInfoList) { links.add(Link.fromUriBuilder(uriInfo.getBaseUriBuilder().path(linkInfo.getPath())) .rel(linkInfo.getRel()) .build(getPathParameterValues(linkInfo, instance))); diff --git a/extensions/spring-data-rest/deployment/pom.xml b/extensions/spring-data-rest/deployment/pom.xml index e3abeaec086897..cab1280cca1534 100644 --- a/extensions/spring-data-rest/deployment/pom.xml +++ b/extensions/spring-data-rest/deployment/pom.xml @@ -38,7 +38,7 @@ io.quarkus - quarkus-resteasy-jsonb-deployment + quarkus-resteasy-reactive-jsonb-deployment test diff --git a/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java b/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java index 52c7b52114d217..16037fb3ef9428 100644 --- a/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java +++ b/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/SpringDataRestProcessor.java @@ -7,6 +7,8 @@ import java.util.LinkedList; import java.util.List; +import javax.ws.rs.Priorities; + import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; @@ -24,10 +26,11 @@ import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.gizmo.ClassOutput; +import io.quarkus.rest.data.panache.RestDataPanacheException; import io.quarkus.rest.data.panache.deployment.ResourceMetadata; import io.quarkus.rest.data.panache.deployment.RestDataResourceBuildItem; import io.quarkus.rest.data.panache.deployment.properties.ResourcePropertiesBuildItem; -import io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem; +import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem; import io.quarkus.spring.data.rest.deployment.crud.CrudMethodsImplementor; import io.quarkus.spring.data.rest.deployment.crud.CrudPropertiesProvider; import io.quarkus.spring.data.rest.deployment.paging.PagingAndSortingMethodsImplementor; @@ -55,8 +58,9 @@ FeatureBuildItem feature() { } @BuildStep - ResteasyJaxrsProviderBuildItem registerRestDataPanacheExceptionMapper() { - return new ResteasyJaxrsProviderBuildItem(RestDataPanacheExceptionMapper.class.getName()); + ExceptionMapperBuildItem registerRestDataPanacheExceptionMapper() { + return new ExceptionMapperBuildItem(RestDataPanacheExceptionMapper.class.getName(), + RestDataPanacheException.class.getName(), Priorities.USER + 100, false); } @BuildStep diff --git a/extensions/spring-data-rest/runtime/pom.xml b/extensions/spring-data-rest/runtime/pom.xml index f1c159c2fb303e..e812673dd80d45 100644 --- a/extensions/spring-data-rest/runtime/pom.xml +++ b/extensions/spring-data-rest/runtime/pom.xml @@ -27,6 +27,10 @@ io.quarkus quarkus-spring-data-rest-api + + jakarta.validation + jakarta.validation-api + diff --git a/integration-tests/hibernate-orm-rest-data-panache/pom.xml b/integration-tests/hibernate-orm-rest-data-panache/pom.xml index 6811ac56be68d3..08bb5e9971b5a1 100644 --- a/integration-tests/hibernate-orm-rest-data-panache/pom.xml +++ b/integration-tests/hibernate-orm-rest-data-panache/pom.xml @@ -23,7 +23,7 @@ io.quarkus - quarkus-resteasy-jackson + quarkus-resteasy-reactive-jackson io.quarkus @@ -79,7 +79,7 @@ io.quarkus - quarkus-resteasy-jackson-deployment + quarkus-resteasy-reactive-jackson-deployment ${project.version} pom test diff --git a/integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java b/integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java index 656972dd443dd0..5b84ebf1678168 100644 --- a/integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java +++ b/integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java @@ -237,8 +237,8 @@ void shouldNotCreateBookWithBlankTitle() { .and().body(book.toString()) .when().post("/books") .then().statusCode(400) - .and().body("parameterViolations[0].path", equalTo("add.arg0.title")) - .and().body("parameterViolations[0].message", equalTo("must not be blank")); + .and().body("violations[0].field", equalTo("add.arg0.title")) + .and().body("violations[0].message", equalTo("must not be blank")); } @Test @@ -307,7 +307,7 @@ void shouldNotUpdateBookWithBlankTitle() { .and().body(book.toString()) .when().put("/books/" + CRIME_AND_PUNISHMENT_ID) .then().statusCode(400) - .and().body("parameterViolations[0].path", equalTo("update.arg1.title")) - .and().body("parameterViolations[0].message", equalTo("must not be blank")); + .and().body("violations[0].field", equalTo("update.arg1.title")) + .and().body("violations[0].message", equalTo("must not be blank")); } } diff --git a/integration-tests/mongodb-rest-data-panache/pom.xml b/integration-tests/mongodb-rest-data-panache/pom.xml index 089a5ffe772bd8..b116d479fa6a60 100644 --- a/integration-tests/mongodb-rest-data-panache/pom.xml +++ b/integration-tests/mongodb-rest-data-panache/pom.xml @@ -19,7 +19,7 @@ io.quarkus - quarkus-resteasy-jackson + quarkus-resteasy-reactive-jackson @@ -63,7 +63,7 @@ io.quarkus - quarkus-resteasy-jackson-deployment + quarkus-resteasy-reactive-jackson-deployment ${project.version} pom test diff --git a/integration-tests/spring-data-rest/pom.xml b/integration-tests/spring-data-rest/pom.xml index 918a7937951ba8..92aeab84a27624 100644 --- a/integration-tests/spring-data-rest/pom.xml +++ b/integration-tests/spring-data-rest/pom.xml @@ -23,7 +23,7 @@ io.quarkus - quarkus-resteasy-jackson + quarkus-resteasy-reactive-jackson io.quarkus @@ -79,7 +79,7 @@ io.quarkus - quarkus-resteasy-jackson-deployment + quarkus-resteasy-reactive-jackson-deployment ${project.version} pom test diff --git a/integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java b/integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java index 0a5325c28e8a7c..09820bae7a5f12 100644 --- a/integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java +++ b/integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java @@ -172,8 +172,8 @@ void shouldNotCreateBookWithBlankTitle() { .and().body(book.toString()) .when().post("/books") .then().statusCode(400) - .and().body("parameterViolations[0].path", equalTo("add.arg0.title")) - .and().body("parameterViolations[0].message", equalTo("must not be blank")); + .and().body("violations[0].field", equalTo("add.arg0.title")) + .and().body("violations[0].message", equalTo("must not be blank")); } @Test @@ -242,7 +242,7 @@ void shouldNotUpdateBookWithBlankTitle() { .and().body(book.toString()) .when().put("/books/" + CRIME_AND_PUNISHMENT_ID) .then().statusCode(400) - .and().body("parameterViolations[0].path", equalTo("update.arg1.title")) - .and().body("parameterViolations[0].message", equalTo("must not be blank")); + .and().body("violations[0].field", equalTo("update.arg1.title")) + .and().body("violations[0].message", equalTo("must not be blank")); } } From f36724b75ffcedc4c8c7cdb05a2c449c2cd58080 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 25 Aug 2021 13:17:53 +0300 Subject: [PATCH 032/138] Add the ability for extensions to exclude GraalVM config from jars Resolves: #17482 --- .../nativeimage/ExcludeConfigBuildItem.java | 41 +++++++++++++++++++ .../pkg/steps/NativeImageBuildStep.java | 20 ++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java new file mode 100644 index 00000000000000..fc6e88f8116f88 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java @@ -0,0 +1,41 @@ +package io.quarkus.deployment.builditem.nativeimage; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * A build item that allows extension to configure the native-image compiler to effectively + * ignore certain configuration files in specific jars. + * + * The {@code jarFile} property specifies the name of the jar file or a regular expression that can be used to + * match multiple jar files. + * Matching jar files using regular expressions should be done as a last resort. + * + * The {@code resourceName} property specifies the name of the resource file or a regular expression that can be used to + * match multiple resource files. + * For the match to work, the resources need to be part of the matched jar file(s) (see {@code jarFile}). + * Matching resource files using regular expressions should be done as a last resort. + * + * See https://github.com/oracle/graal/pull/3179 for more details. + */ +public final class ExcludeConfigBuildItem extends MultiBuildItem { + + private final String jarFile; + private final String resourceName; + + public ExcludeConfigBuildItem(String jarFile, String resourceName) { + this.jarFile = jarFile; + this.resourceName = resourceName; + } + + public ExcludeConfigBuildItem(String jarFile) { + this(jarFile, "META-INF/native-image/native-image.properties"); + } + + public String getJarFile() { + return jarFile; + } + + public String getResourceName() { + return resourceName; + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index 2210a067887cbe..e1dba7ed6f7e9b 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -26,6 +26,7 @@ import io.quarkus.bootstrap.model.AppDependency; import io.quarkus.bootstrap.util.IoUtils; import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.nativeimage.ExcludeConfigBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageSecurityProviderBuildItem; import io.quarkus.deployment.builditem.nativeimage.NativeImageSystemPropertyBuildItem; import io.quarkus.deployment.pkg.NativeConfig; @@ -81,7 +82,8 @@ ArtifactResultBuildItem nativeSourcesResult(NativeConfig nativeConfig, NativeImageSourceJarBuildItem nativeImageSourceJarBuildItem, OutputTargetBuildItem outputTargetBuildItem, PackageConfig packageConfig, - List nativeImageProperties) { + List nativeImageProperties, + List excludeConfigs) { Path outputDir; try { @@ -100,6 +102,7 @@ ArtifactResultBuildItem nativeSourcesResult(NativeConfig nativeConfig, .setNativeConfig(nativeConfig) .setOutputTargetBuildItem(outputTargetBuildItem) .setNativeImageProperties(nativeImageProperties) + .setExcludeConfigs(excludeConfigs) .setOutputDir(outputDir) .setRunnerJarName(runnerJar.getFileName().toString()) // the path to native-image is not known now, it is only known at the time the native-sources will be consumed @@ -130,6 +133,7 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa PackageConfig packageConfig, CurateOutcomeBuildItem curateOutcomeBuildItem, List nativeImageProperties, + List excludeConfigs, List nativeImageSecurityProviders, Optional processInheritIODisabled) { if (nativeConfig.debug.enabled) { @@ -187,6 +191,7 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa .setNativeConfig(nativeConfig) .setOutputTargetBuildItem(outputTargetBuildItem) .setNativeImageProperties(nativeImageProperties) + .setExcludeConfigs(excludeConfigs) .setNativeImageSecurityProviders(nativeImageSecurityProviders) .setOutputDir(outputDir) .setRunnerJarName(runnerJarName) @@ -477,6 +482,7 @@ static class Builder { private NativeConfig nativeConfig; private OutputTargetBuildItem outputTargetBuildItem; private List nativeImageProperties; + private List excludeConfigs; private List nativeImageSecurityProviders; private Path outputDir; private String runnerJarName; @@ -500,6 +506,11 @@ public Builder setNativeImageProperties(List return this; } + public Builder setExcludeConfigs(List excludeConfigs) { + this.excludeConfigs = excludeConfigs; + return this; + } + public Builder setNativeImageSecurityProviders( List nativeImageSecurityProviders) { this.nativeImageSecurityProviders = nativeImageSecurityProviders; @@ -700,6 +711,13 @@ public NativeImageInvokerInfo build() { .collect(Collectors.joining(",")); nativeImageArgs.add("-H:AdditionalSecurityProviders=" + additionalSecurityProviders); } + + // --exclude-config options + for (ExcludeConfigBuildItem excludeConfig : excludeConfigs) { + nativeImageArgs.add("--exclude-config"); + nativeImageArgs.add(excludeConfig.getJarFile()); + nativeImageArgs.add(excludeConfig.getResourceName()); + } } nativeImageArgs.add(nativeImageName); From 58e48e0594f43992a89209826da98348f41ac84e Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 31 Aug 2021 17:23:08 +0100 Subject: [PATCH 033/138] Make sure the --exclude-config arguments precede the -jar arguments --- .../builditem/nativeimage/ExcludeConfigBuildItem.java | 2 +- .../quarkus/deployment/pkg/steps/NativeImageBuildStep.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java index fc6e88f8116f88..8c2cf955c26392 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ExcludeConfigBuildItem.java @@ -28,7 +28,7 @@ public ExcludeConfigBuildItem(String jarFile, String resourceName) { } public ExcludeConfigBuildItem(String jarFile) { - this(jarFile, "META-INF/native-image/native-image.properties"); + this(jarFile, "/META-INF/native-image/native-image\\.properties"); } public String getJarFile() { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index e1dba7ed6f7e9b..2236dd642d26b0 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -595,8 +595,6 @@ public NativeImageInvokerInfo build() { "-H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime"); //the default collection policy results in full GC's 50% of the time nativeImageArgs.add("-H:+JNI"); nativeImageArgs.add("-H:+AllowFoldMethods"); - nativeImageArgs.add("-jar"); - nativeImageArgs.add(runnerJarName); if (nativeConfig.enableFallbackImages) { nativeImageArgs.add("-H:FallbackThreshold=5"); @@ -722,6 +720,10 @@ public NativeImageInvokerInfo build() { nativeImageArgs.add(nativeImageName); + //Make sure to have the -jar as last one, as it otherwise breaks "--exclude-config" + nativeImageArgs.add("-jar"); + nativeImageArgs.add(runnerJarName); + return new NativeImageInvokerInfo(nativeImageArgs); } From e4d2bcfe681928e87e7309bb3a5c7c9c6d35ef7a Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Mon, 30 Aug 2021 19:07:03 +0100 Subject: [PATCH 034/138] Update OIDC TokenStateManager to return Uni --- .../security-openid-connect-multitenancy.adoc | 34 +- ...ity-openid-connect-web-authentication.adoc | 53 +++ .../oidc/test/CodeFlowDevModeTestCase.java | 10 + .../oidc/test/CustomTokenStateManager.java | 19 +- .../quarkus/oidc/test/ProtectedResource.java | 6 + .../resources/application-dev-mode.properties | 1 + .../io/quarkus/oidc/TenantConfigResolver.java | 10 +- .../io/quarkus/oidc/TokenStateManager.java | 125 ++++++- .../runtime/CodeAuthenticationMechanism.java | 344 ++++++++++++------ .../runtime/DefaultTokenStateManager.java | 16 +- 10 files changed, 470 insertions(+), 148 deletions(-) diff --git a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc index 0fa38d53ae5df0..614f14aa451afd 100644 --- a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc @@ -318,7 +318,9 @@ This interface allows you to dynamically create tenant configurations at runtime package io.quarkus.it.keycloak; import javax.enterprise.context.ApplicationScoped; +import java.util.function.Supplier; +import io.smallrye.mutiny.Uni; import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.TenantConfigResolver; import io.vertx.ext.web.RoutingContext; @@ -327,7 +329,7 @@ import io.vertx.ext.web.RoutingContext; public class CustomTenantConfigResolver implements TenantConfigResolver { @Override - public OidcTenantConfig resolve(RoutingContext context) { + public Uni resolve(RoutingContext context, TenantConfigResolver.TenantConfigRequestContext requestContext) { String path = context.request().path(); String[] parts = path.split("/"); @@ -337,24 +339,30 @@ public class CustomTenantConfigResolver implements TenantConfigResolver { } if ("tenant-c".equals(parts[1])) { - OidcTenantConfig config = new OidcTenantConfig(); + // Do 'return requestContext.runBlocking(createTenantConfig());' + // if a blocking call is required to create a tenant config + return Uni.createFromItem(createTenantConfig()); + } + + // resolve to default tenant configuration + return null; + } - config.setTenantId("tenant-c"); - config.setAuthServerUrl("http://localhost:8180/auth/realms/tenant-c"); - config.setClientId("multi-tenant-client"); - OidcTenantConfig.Credentials credentials = new OidcTenantConfig.Credentials(); + private Supplier createTenantConfig() { + final OidcTenantConfig config = new OidcTenantConfig(); - credentials.setSecret("my-secret"); + config.setTenantId("tenant-c"); + config.setAuthServerUrl("http://localhost:8180/auth/realms/tenant-c"); + config.setClientId("multi-tenant-client"); + OidcTenantConfig.Credentials credentials = new OidcTenantConfig.Credentials(); - config.setCredentials(credentials); + credentials.setSecret("my-secret"); - // any other setting support by the quarkus-oidc extension + config.setCredentials(credentials); - return config; - } + // any other setting support by the quarkus-oidc extension - // resolve to default tenant configuration - return null; + return () -> config; } } ---- 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 ec0c6ea9578f65..a2f0fe5df9c6e0 100644 --- a/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-web-authentication.adoc @@ -469,6 +469,59 @@ In such cases, you can use `quarkus.oidc.token-state-manager.split-tokens=true` Register your own `io.quarkus.oidc.TokenStateManager' implementation as an `@ApplicationScoped` CDI bean if you need to customize the way the tokens are associated with the session cookie. For example, you may want to keep the tokens in a database and have only a database pointer stored in a session cookie. Note though that it may present some challenges in making the tokens available across multiple microservices nodes. +Here is a simple example: + +[source, java] +---- +package io.quarkus.oidc.test; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +import io.quarkus.arc.AlternativePriority; +import io.quarkus.oidc.AuthorizationCodeTokens; +import io.quarkus.oidc.OidcTenantConfig; +import io.quarkus.oidc.TokenStateManager; +import io.quarkus.oidc.runtime.DefaultTokenStateManager; +import io.smallrye.mutiny.Uni; +import io.vertx.ext.web.RoutingContext; + +@ApplicationScoped +@AlternativePriority(1) +public class CustomTokenStateManager implements TokenStateManager { + + @Inject + DefaultTokenStateManager tokenStateManager; + + @Override + public Uni createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig, + AuthorizationCodeTokens sessionContent, TokenStateManager.CreateTokenStateRequestContext requestContext) { + return tokenStateManager.createTokenState(routingContext, oidcConfig, sessionContent, requestContext) + .map(t -> (t + "|custom")); + } + + @Override + public Uni getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, + String tokenState, TokenStateManager.GetTokensRequestContext requestContext) { + if (!tokenState.endsWith("|custom")) { + throw new IllegalStateException(); + } + String defaultState = tokenState.substring(0, tokenState.length() - 7); + return tokenStateManager.getTokens(routingContext, oidcConfig, defaultState, requestContext); + } + + @Override + public Uni deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState, + TokenStateManager.DeleteTokensRequestContext requestContext) { + if (!tokenState.endsWith("|custom")) { + throw new IllegalStateException(); + } + String defaultState = tokenState.substring(0, tokenState.length() - 7); + return tokenStateManager.deleteTokens(routingContext, oidcConfig, defaultState, requestContext); + } +} +---- + == Listening to important authentication events One can register `@ApplicationScoped` bean which will observe important OIDC authentication events. The listener will be updated when a user has logged in for the first time or re-authenticated, as well as when the session has been refreshed. More events may be reported in the future. For example: diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeTestCase.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeTestCase.java index 1d55d0df736eea..ed0704e02aae7e 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeTestCase.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeTestCase.java @@ -1,10 +1,12 @@ package io.quarkus.oidc.test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; +import java.net.URI; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.JavaArchive; @@ -14,6 +16,8 @@ import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.SilentCssErrorHandler; import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebRequest; +import com.gargoylesoftware.htmlunit.WebResponse; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlPage; @@ -87,6 +91,12 @@ public void testAccessAndRefreshTokenInjectionDevMode() throws IOException, Inte assertEquals("custom", page.getWebClient().getCookieManager().getCookie("q_session").getValue().split("\\|")[3]); + webClient.getOptions().setRedirectEnabled(false); + WebResponse webResponse = webClient + .loadWebResponse(new WebRequest(URI.create("http://localhost:8080/protected/logout").toURL())); + assertEquals(302, webResponse.getStatusCode()); + assertNull(webClient.getCookieManager().getCookie("q_session")); + webClient.getCookieManager().clearCookies(); } } diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CustomTokenStateManager.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CustomTokenStateManager.java index 220d89e1ff0edc..4a57c25770a12b 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CustomTokenStateManager.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CustomTokenStateManager.java @@ -8,6 +8,7 @@ import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.TokenStateManager; import io.quarkus.oidc.runtime.DefaultTokenStateManager; +import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; @ApplicationScoped @@ -18,25 +19,29 @@ public class CustomTokenStateManager implements TokenStateManager { DefaultTokenStateManager tokenStateManager; @Override - public String createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig, - AuthorizationCodeTokens sessionContent) { - return tokenStateManager.createTokenState(routingContext, oidcConfig, sessionContent) + "|custom"; + public Uni createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig, + AuthorizationCodeTokens sessionContent, TokenStateManager.CreateTokenStateRequestContext requestContext) { + return tokenStateManager.createTokenState(routingContext, oidcConfig, sessionContent, requestContext) + .map(t -> (t + "|custom")); } @Override - public AuthorizationCodeTokens getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, - String tokenState) { + public Uni getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, + String tokenState, TokenStateManager.GetTokensRequestContext requestContext) { if (!tokenState.endsWith("|custom")) { throw new IllegalStateException(); } String defaultState = tokenState.substring(0, tokenState.length() - 7); - return tokenStateManager.getTokens(routingContext, oidcConfig, defaultState); + return tokenStateManager.getTokens(routingContext, oidcConfig, defaultState, requestContext); } @Override - public void deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState) { + public Uni deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState, + TokenStateManager.DeleteTokensRequestContext requestContext) { if (!tokenState.endsWith("|custom")) { throw new IllegalStateException(); } + String defaultState = tokenState.substring(0, tokenState.length() - 7); + return tokenStateManager.deleteTokens(routingContext, oidcConfig, defaultState, requestContext); } } diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResource.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResource.java index fa87a328bd5c83..9c1c73e03987bc 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResource.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResource.java @@ -28,4 +28,10 @@ public String getName() { public String getTenantName(@PathParam("id") String tenantId) { return tenantId + ":" + idToken.getName(); } + + @GET + @Path("logout") + public void logout() { + throw new RuntimeException("Logout must be handled by CodeAuthenticationMechanism"); + } } diff --git a/extensions/oidc/deployment/src/test/resources/application-dev-mode.properties b/extensions/oidc/deployment/src/test/resources/application-dev-mode.properties index 15b1c51e72f529..ee7c873dd6e01f 100644 --- a/extensions/oidc/deployment/src/test/resources/application-dev-mode.properties +++ b/extensions/oidc/deployment/src/test/resources/application-dev-mode.properties @@ -5,6 +5,7 @@ quarkus.oidc.client-id=client-dev quarkus.oidc.credentials.client-secret.provider.name=vault-secret-provider quarkus.oidc.credentials.client-secret.provider.key=secret-from-vault quarkus.oidc.application-type=web-app +quarkus.oidc.logout.path=/protected/logout quarkus.log.category."com.gargoylesoftware.htmlunit.javascript.host.css.CSSStyleSheet".level=FATAL diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/TenantConfigResolver.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/TenantConfigResolver.java index c0f9143cef2f97..59fa463895eed0 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/TenantConfigResolver.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/TenantConfigResolver.java @@ -21,10 +21,12 @@ public interface TenantConfigResolver { * * @param context the routing context * @return the tenant configuration. If {@code null}, indicates that the default configuration/tenant should be chosen + * + * @deprecated Use {@link #resolve(RoutingContext, TenantConfigRequestContext))} instead. */ @Deprecated default OidcTenantConfig resolve(RoutingContext context) { - throw new UnsupportedOperationException("resolve not implemented"); + throw new UnsupportedOperationException("resolve is not implemented"); } /** @@ -39,10 +41,10 @@ default Uni resolve(RoutingContext routingContext, TenantConfi } /** - * A context object that can be used to run blocking tasks + * A context object that can be used to run blocking tasks. *

    - * Blocking config providers should used this context object to run blocking tasks, to prevent excessive and - * unnecessary delegation to thread pools + * Blocking {@code TenantConfigResolver} providers should use this context object to run blocking tasks, to prevent + * excessive and unnecessary delegation to thread pools. */ interface TenantConfigRequestContext { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/TokenStateManager.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/TokenStateManager.java index 94cf60539cc73b..ecda06f10e225e 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/TokenStateManager.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/TokenStateManager.java @@ -1,5 +1,8 @@ package io.quarkus.oidc; +import java.util.function.Supplier; + +import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; /** @@ -13,9 +16,125 @@ */ public interface TokenStateManager { - String createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig, AuthorizationCodeTokens tokens); + /** + * Convert the authorization code flow tokens into a token state. + * + * @param routingContext the request context + * @param oidcConfig the tenant configuration + * @param tokens the authorization code flow tokens + * + * @return the token state + * + * @deprecated Use + * {@link #createTokenState(RoutingContext, OidcTenantConfig, AuthorizationCodeTokens, CreateTokenStateRequestContext)} + * + */ + @Deprecated + default String createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig, + AuthorizationCodeTokens tokens) { + throw new UnsupportedOperationException("createTokenState is not implemented"); + } + + /** + * Convert the authorization code flow tokens into a token state. + * + * @param routingContext the request context + * @param oidcConfig the tenant configuration + * @param tokens the authorization code flow tokens + * @param requestContext the request context + * + * @return the token state + */ + default Uni createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig, + AuthorizationCodeTokens tokens, CreateTokenStateRequestContext requestContext) { + return Uni.createFrom().item(createTokenState(routingContext, oidcConfig, tokens)); + } + + /** + * Convert the token state into the authorization code flow tokens. + * + * @param routingContext the request context + * @param oidcConfig the tenant configuration + * @param tokens the token state + * + * @return the authorization code flow tokens + * + * @deprecated Use {@link #getTokens(RoutingContext, OidcTenantConfig, String, GetTokensRequestContext)} instead. + */ + @Deprecated + default AuthorizationCodeTokens getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState) { + throw new UnsupportedOperationException("getTokens is not implemented"); + } + + /** + * Convert the token state into the authorization code flow tokens. + * + * @param routingContext the request context + * @param oidcConfig the tenant configuration + * @param tokens the token state + * @param requestContext the request context + * + * @return the authorization code flow tokens + */ + default Uni getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, + String tokenState, GetTokensRequestContext requestContext) { + return Uni.createFrom().item(getTokens(routingContext, oidcConfig, tokenState)); + } + + /** + * Delete the token state. + * + * @param routingContext the request context + * @param oidcConfig the tenant configuration + * @param tokens the token state + * + * @deprecated Use {@link #deleteTokens(RoutingContext, OidcTenantConfig, String, DeleteTokensRequestContext)} instead + */ + @Deprecated + default void deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState) { + throw new UnsupportedOperationException("deleteTokens is not implemented"); + } + + /** + * Delete the token state. + * + * @param routingContext the request context + * @param oidcConfig the tenant configuration + * @param tokens the token state + */ + default Uni deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState, + DeleteTokensRequestContext requestContext) { + deleteTokens(routingContext, oidcConfig, tokenState); + return Uni.createFrom().voidItem(); + } + + /** + * A context object that can be used to create a token state by running a blocking task. + *

    + * Blocking providers should use this context to prevent excessive and unnecessary delegation to thread pools. + */ + interface CreateTokenStateRequestContext { + + Uni runBlocking(Supplier function); + } + + /** + * A context object that can be used to convert the token state to the tokens by running a blocking task. + *

    + * Blocking providers should use this context to prevent excessive and unnecessary delegation to thread pools. + */ + interface GetTokensRequestContext { + + Uni runBlocking(Supplier function); + } - AuthorizationCodeTokens getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState); + /** + * A context object that can be used to delete the token state by running a blocking task. + *

    + * Blocking providers should use this context to prevent excessive and unnecessary delegation to thread pools. + */ + interface DeleteTokensRequestContext { - void deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState); + Uni runBlocking(Supplier function); + } } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java index cb4bb8ccd77016..8e6e5e656fa491 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java @@ -10,7 +10,9 @@ import java.util.Optional; import java.util.UUID; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; import java.util.regex.Pattern; import org.jboss.logging.Logger; @@ -23,8 +25,11 @@ import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.OidcTenantConfig.Authentication; import io.quarkus.oidc.SecurityEvent; +import io.quarkus.oidc.TokenStateManager; import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcConstants; +import io.quarkus.runtime.BlockingOperationControl; +import io.quarkus.runtime.ExecutorRecorder; import io.quarkus.security.AuthenticationCompletionException; import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.AuthenticationRedirectException; @@ -32,6 +37,7 @@ import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.vertx.http.runtime.security.ChallengeData; import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.subscription.UniEmitter; import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.impl.CookieImpl; @@ -47,12 +53,17 @@ public class CodeAuthenticationMechanism extends AbstractOidcAuthenticationMecha static final Pattern COOKIE_PATTERN = Pattern.compile("\\" + COOKIE_DELIM); static final String SESSION_COOKIE_NAME = "q_session"; static final String SESSION_MAX_AGE_PARAM = "session-max-age"; + static final Uni VOID_UNI = Uni.createFrom().voidItem(); private static final Logger LOG = Logger.getLogger(CodeAuthenticationMechanism.class); private static final String STATE_COOKIE_NAME = "q_auth"; private static final String POST_LOGOUT_COOKIE_NAME = "q_post_logout"; + private final CreateTokenStateRequestContext createTokenStateRequestContext = new CreateTokenStateRequestContext(); + private final GetTokensRequestContext getTokenStateRequestContext = new GetTokensRequestContext(); + private final DeleteTokensRequestContext deleteTokensRequestContext = new DeleteTokensRequestContext(); + public Uni authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) { return resolver.resolveConfig(context).chain(new Function>() { @@ -96,51 +107,60 @@ private Uni reAuthenticate(Cookie sessionCookie, IdentityProviderManager identityProviderManager, TenantConfigContext configContext) { - AuthorizationCodeTokens session = resolver.getTokenStateManager().getTokens(context, configContext.oidcConfig, - sessionCookie.getValue()); - - context.put(OidcConstants.ACCESS_TOKEN_VALUE, session.getAccessToken()); - context.put(AuthorizationCodeTokens.class.getName(), session); - return authenticate(identityProviderManager, context, new IdTokenCredential(session.getIdToken(), context)) - .map(new Function() { - @Override - public SecurityIdentity apply(SecurityIdentity identity) { - if (isLogout(context, configContext)) { - fireEvent(SecurityEvent.Type.OIDC_LOGOUT_RP_INITIATED, identity); - throw redirectToLogoutEndpoint(context, configContext, session.getIdToken()); - } - return identity; - } - }).onFailure().recoverWithUni(new Function>() { + return resolver.getTokenStateManager().getTokens(context, configContext.oidcConfig, + sessionCookie.getValue(), getTokenStateRequestContext) + .chain(new Function>() { @Override - public Uni apply(Throwable t) { - if (t instanceof AuthenticationRedirectException) { - throw (AuthenticationRedirectException) t; - } + public Uni apply(AuthorizationCodeTokens session) { + context.put(OidcConstants.ACCESS_TOKEN_VALUE, session.getAccessToken()); + context.put(AuthorizationCodeTokens.class.getName(), session); + return authenticate(identityProviderManager, context, + new IdTokenCredential(session.getIdToken(), context)) + .call(new Function>() { + @Override + public Uni apply(SecurityIdentity identity) { + if (isLogout(context, configContext)) { + fireEvent(SecurityEvent.Type.OIDC_LOGOUT_RP_INITIATED, identity); + return buildLogoutRedirectUriUni(context, configContext, + session.getIdToken()); + } + return VOID_UNI; + } + }).onFailure() + .recoverWithUni(new Function>() { + @Override + public Uni apply(Throwable t) { + if (t instanceof AuthenticationRedirectException) { + throw (AuthenticationRedirectException) t; + } - if (!(t instanceof TokenAutoRefreshException)) { - boolean expired = (t.getCause() instanceof InvalidJwtException) - && ((InvalidJwtException) t.getCause()).hasErrorCode(ErrorCodes.EXPIRED); + if (!(t instanceof TokenAutoRefreshException)) { + boolean expired = (t.getCause() instanceof InvalidJwtException) + && ((InvalidJwtException) t.getCause()) + .hasErrorCode(ErrorCodes.EXPIRED); - if (!expired) { - LOG.debugf("Authentication failure: %s", t.getCause()); - throw new AuthenticationCompletionException(t.getCause()); - } - if (!configContext.oidcConfig.token.refreshExpired) { - LOG.debug("Token has expired, token refresh is not allowed"); - throw new AuthenticationCompletionException(t.getCause()); - } - LOG.debug("Token has expired, trying to refresh it"); - return refreshSecurityIdentity(configContext, session.getRefreshToken(), context, - identityProviderManager, false, null); - } else { - return refreshSecurityIdentity(configContext, session.getRefreshToken(), context, - identityProviderManager, true, - ((TokenAutoRefreshException) t).getSecurityIdentity()); - } + if (!expired) { + LOG.debugf("Authentication failure: %s", t.getCause()); + throw new AuthenticationCompletionException(t.getCause()); + } + if (!configContext.oidcConfig.token.refreshExpired) { + LOG.debug("Token has expired, token refresh is not allowed"); + throw new AuthenticationCompletionException(t.getCause()); + } + LOG.debug("Token has expired, trying to refresh it"); + return refreshSecurityIdentity(configContext, session.getRefreshToken(), + context, + identityProviderManager, false, null); + } else { + return refreshSecurityIdentity(configContext, session.getRefreshToken(), + context, + identityProviderManager, true, + ((TokenAutoRefreshException) t).getSecurityIdentity()); + } + } + }); } }); - } private boolean isJavaScript(RoutingContext context) { @@ -168,54 +188,63 @@ public Uni apply(TenantConfigContext tenantContext) { } public Uni getChallengeInternal(RoutingContext context, TenantConfigContext configContext) { - removeCookie(context, configContext, getSessionCookieName(configContext.oidcConfig)); - - if (!shouldAutoRedirect(configContext, context)) { - // If the client (usually an SPA) wants to handle the redirect manually, then - // return status code 499 and WWW-Authenticate header with the 'OIDC' value. - return Uni.createFrom().item(new ChallengeData(499, "WWW-Authenticate", "OIDC")); - } - - StringBuilder codeFlowParams = new StringBuilder(); - - // response_type - codeFlowParams.append(OidcConstants.CODE_FLOW_RESPONSE_TYPE).append(EQ).append(OidcConstants.CODE_FLOW_CODE); - - // client_id - codeFlowParams.append(AMP).append(OidcConstants.CLIENT_ID).append(EQ) - .append(OidcCommonUtils.urlEncode(configContext.oidcConfig.clientId.get())); - - // scope - List scopes = new ArrayList<>(); - scopes.add("openid"); - configContext.oidcConfig.getAuthentication().scopes.ifPresent(scopes::addAll); - codeFlowParams.append(AMP).append(OidcConstants.TOKEN_SCOPE).append(EQ) - .append(OidcCommonUtils.urlEncode(String.join(" ", scopes))); - - // redirect_uri - String redirectPath = getRedirectPath(configContext, context); - String redirectUriParam = buildUri(context, isForceHttps(configContext), redirectPath); - LOG.debugf("Authentication request redirect_uri parameter: %s", redirectUriParam); + return removeSessionCookie(context, configContext, getSessionCookieName(configContext.oidcConfig)) + .chain(new Function>() { - codeFlowParams.append(AMP).append(OidcConstants.CODE_FLOW_REDIRECT_URI).append(EQ) - .append(OidcCommonUtils.urlEncode(redirectUriParam)); + @Override + public Uni apply(Void t) { + if (!shouldAutoRedirect(configContext, context)) { + // If the client (usually an SPA) wants to handle the redirect manually, then + // return status code 499 and WWW-Authenticate header with the 'OIDC' value. + return Uni.createFrom().item(new ChallengeData(499, "WWW-Authenticate", "OIDC")); + } - // state - codeFlowParams.append(AMP).append(OidcConstants.CODE_FLOW_STATE).append(EQ) - .append(generateCodeFlowState(context, configContext, redirectPath)); + StringBuilder codeFlowParams = new StringBuilder(); + + // response_type + codeFlowParams.append(OidcConstants.CODE_FLOW_RESPONSE_TYPE).append(EQ) + .append(OidcConstants.CODE_FLOW_CODE); + + // client_id + codeFlowParams.append(AMP).append(OidcConstants.CLIENT_ID).append(EQ) + .append(OidcCommonUtils.urlEncode(configContext.oidcConfig.clientId.get())); + + // scope + List scopes = new ArrayList<>(); + scopes.add("openid"); + configContext.oidcConfig.getAuthentication().scopes.ifPresent(scopes::addAll); + codeFlowParams.append(AMP).append(OidcConstants.TOKEN_SCOPE).append(EQ) + .append(OidcCommonUtils.urlEncode(String.join(" ", scopes))); + + // redirect_uri + String redirectPath = getRedirectPath(configContext, context); + String redirectUriParam = buildUri(context, isForceHttps(configContext), redirectPath); + LOG.debugf("Authentication request redirect_uri parameter: %s", redirectUriParam); + + codeFlowParams.append(AMP).append(OidcConstants.CODE_FLOW_REDIRECT_URI).append(EQ) + .append(OidcCommonUtils.urlEncode(redirectUriParam)); + + // state + codeFlowParams.append(AMP).append(OidcConstants.CODE_FLOW_STATE).append(EQ) + .append(generateCodeFlowState(context, configContext, redirectPath)); + + // extra redirect parameters, see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequests + if (configContext.oidcConfig.authentication.getExtraParams() != null) { + for (Map.Entry entry : configContext.oidcConfig.authentication.getExtraParams() + .entrySet()) { + codeFlowParams.append(AMP).append(entry.getKey()).append(EQ) + .append(OidcCommonUtils.urlEncode(entry.getValue())); + } + } - // extra redirect parameters, see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequests - if (configContext.oidcConfig.authentication.getExtraParams() != null) { - for (Map.Entry entry : configContext.oidcConfig.authentication.getExtraParams().entrySet()) { - codeFlowParams.append(AMP).append(entry.getKey()).append(EQ) - .append(OidcCommonUtils.urlEncode(entry.getValue())); - } - } + String authorizationURL = configContext.provider.getMetadata().getAuthorizationUri() + "?" + + codeFlowParams.toString(); - String authorizationURL = configContext.provider.getMetadata().getAuthorizationUri() + "?" + codeFlowParams.toString(); + return Uni.createFrom().item(new ChallengeData(HttpResponseStatus.FOUND.code(), HttpHeaders.LOCATION, + authorizationURL)); + } - return Uni.createFrom().item(new ChallengeData(HttpResponseStatus.FOUND.code(), HttpHeaders.LOCATION, - authorizationURL)); + }); } private Uni performCodeFlow(IdentityProviderManager identityProviderManager, @@ -278,12 +307,16 @@ public Uni apply(final AuthorizationCodeTokens tokens, final T return authenticate(identityProviderManager, context, new IdTokenCredential(tokens.getIdToken(), context)) + .call(new Function>() { + @Override + public Uni apply(SecurityIdentity identity) { + return processSuccessfulAuthentication(context, configContext, + tokens, identity); + } + }) .map(new Function() { @Override public SecurityIdentity apply(SecurityIdentity identity) { - processSuccessfulAuthentication(context, configContext, - tokens, identity); - boolean removeRedirectParams = configContext.oidcConfig.authentication .isRemoveRedirectParameters(); if (removeRedirectParams || finalUserPath != null @@ -325,31 +358,48 @@ public Throwable apply(Throwable tInner) { } - private void processSuccessfulAuthentication(RoutingContext context, + private Uni processSuccessfulAuthentication(RoutingContext context, TenantConfigContext configContext, AuthorizationCodeTokens tokens, SecurityIdentity securityIdentity) { - removeCookie(context, configContext, getSessionCookieName(configContext.oidcConfig)); + return removeSessionCookie(context, configContext, getSessionCookieName(configContext.oidcConfig)) + .chain(new Function>() { - JsonObject idToken = OidcUtils.decodeJwtContent(tokens.getIdToken()); + @Override + public Uni apply(Void t) { + JsonObject idToken = OidcUtils.decodeJwtContent(tokens.getIdToken()); - if (!idToken.containsKey("exp") || !idToken.containsKey("iat")) { - LOG.debug("ID Token is required to contain 'exp' and 'iat' claims"); - throw new AuthenticationCompletionException(); - } - long maxAge = idToken.getLong("exp") - idToken.getLong("iat"); - if (configContext.oidcConfig.token.lifespanGrace.isPresent()) { - maxAge += configContext.oidcConfig.token.lifespanGrace.getAsInt(); - } - if (configContext.oidcConfig.token.refreshExpired) { - maxAge += configContext.oidcConfig.authentication.sessionAgeExtension.getSeconds(); - } - context.put(SESSION_MAX_AGE_PARAM, maxAge); - String cookieValue = resolver.getTokenStateManager() - .createTokenState(context, configContext.oidcConfig, tokens); - createCookie(context, configContext.oidcConfig, getSessionCookieName(configContext.oidcConfig), cookieValue, maxAge); + if (!idToken.containsKey("exp") || !idToken.containsKey("iat")) { + LOG.debug("ID Token is required to contain 'exp' and 'iat' claims"); + throw new AuthenticationCompletionException(); + } + long maxAge = idToken.getLong("exp") - idToken.getLong("iat"); + if (configContext.oidcConfig.token.lifespanGrace.isPresent()) { + maxAge += configContext.oidcConfig.token.lifespanGrace.getAsInt(); + } + if (configContext.oidcConfig.token.refreshExpired) { + maxAge += configContext.oidcConfig.authentication.sessionAgeExtension.getSeconds(); + } + final long sessionMaxAge = maxAge; + context.put(SESSION_MAX_AGE_PARAM, maxAge); + return resolver.getTokenStateManager() + .createTokenState(context, configContext.oidcConfig, tokens, createTokenStateRequestContext) + .map(new Function() { + + @Override + public Void apply(String cookieValue) { + createCookie(context, configContext.oidcConfig, + getSessionCookieName(configContext.oidcConfig), + cookieValue, sessionMaxAge); + fireEvent(SecurityEvent.Type.OIDC_LOGIN, securityIdentity); + return null; + } + + }); + } + + }); - fireEvent(SecurityEvent.Type.OIDC_LOGIN, securityIdentity); } private void fireEvent(SecurityEvent.Type eventType, SecurityIdentity securityIdentity) { @@ -441,14 +491,24 @@ private String buildUri(RoutingContext context, boolean forceHttps, String autho .toString(); } - private void removeCookie(RoutingContext context, TenantConfigContext configContext, String cookieName) { + private Uni removeSessionCookie(RoutingContext context, TenantConfigContext configContext, String cookieName) { + String cookieValue = removeCookie(context, configContext, cookieName); + if (cookieValue != null) { + return resolver.getTokenStateManager().deleteTokens(context, configContext.oidcConfig, cookieValue, + deleteTokensRequestContext); + } else { + return VOID_UNI; + } + } + + private String removeCookie(RoutingContext context, TenantConfigContext configContext, String cookieName) { ServerCookie cookie = (ServerCookie) context.cookieMap().get(cookieName); + String cookieValue = null; if (cookie != null) { - if (SESSION_COOKIE_NAME.equals(cookieName)) { - resolver.getTokenStateManager().deleteTokens(context, configContext.oidcConfig, cookie.getValue()); - } + cookieValue = cookie.getValue(); removeCookie(context, cookie, configContext.oidcConfig); } + return cookieValue; } static void removeCookie(RoutingContext context, ServerCookie cookie, OidcTenantConfig oidcConfig) { @@ -500,12 +560,17 @@ public Uni apply(final AuthorizationCodeTokens tokens, final T return authenticate(identityProviderManager, context, new IdTokenCredential(tokens.getIdToken(), context)) - .map(new Function() { + .call(new Function>() { @Override - public SecurityIdentity apply(SecurityIdentity identity) { + public Uni apply(SecurityIdentity identity) { // after a successful refresh, rebuild the identity and update the cookie - processSuccessfulAuthentication(context, configContext, + return processSuccessfulAuthentication(context, configContext, tokens, identity); + } + }) + .map(new Function() { + @Override + public SecurityIdentity apply(SecurityIdentity identity) { fireEvent(autoRefresh ? SecurityEvent.Type.OIDC_SESSION_REFRESHED : SecurityEvent.Type.OIDC_SESSION_EXPIRED_AND_REFRESHED, identity); @@ -555,10 +620,15 @@ private boolean isForceHttps(TenantConfigContext configContext) { return configContext.oidcConfig.authentication.forceRedirectHttpsScheme; } - private AuthenticationRedirectException redirectToLogoutEndpoint(RoutingContext context, TenantConfigContext configContext, + private Uni buildLogoutRedirectUriUni(RoutingContext context, TenantConfigContext configContext, String idToken) { - removeCookie(context, configContext, getSessionCookieName(configContext.oidcConfig)); - return new AuthenticationRedirectException(buildLogoutRedirectUri(configContext, idToken, context)); + return removeSessionCookie(context, configContext, getSessionCookieName(configContext.oidcConfig)) + .map(new Function() { + @Override + public Void apply(Void t) { + throw new AuthenticationRedirectException(buildLogoutRedirectUri(configContext, idToken, context)); + } + }); } private static String getStateCookieName(TenantConfigContext configContext) { @@ -580,4 +650,48 @@ static String getCookieSuffix(String tenantId) { return !"Default".equals(tenantId) ? "_" + tenantId : ""; } + private static class CreateTokenStateRequestContext extends BlockingTaskRunner + implements TokenStateManager.CreateTokenStateRequestContext { + } + + private static class GetTokensRequestContext extends BlockingTaskRunner + implements TokenStateManager.GetTokensRequestContext { + } + + private static class DeleteTokensRequestContext extends BlockingTaskRunner + implements TokenStateManager.DeleteTokensRequestContext { + } + + private static class BlockingTaskRunner { + public Uni runBlocking(Supplier function) { + return Uni.createFrom().deferred(new Supplier>() { + @Override + public Uni get() { + if (BlockingOperationControl.isBlockingAllowed()) { + try { + return Uni.createFrom().item(function.get()); + } catch (Throwable t) { + return Uni.createFrom().failure(t); + } + } else { + return Uni.createFrom().emitter(new Consumer>() { + @Override + public void accept(UniEmitter uniEmitter) { + ExecutorRecorder.getCurrent().execute(new Runnable() { + @Override + public void run() { + try { + uniEmitter.complete(function.get()); + } catch (Throwable t) { + uniEmitter.fail(t); + } + } + }); + } + }); + } + } + }); + } + } } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java index 2b0a951258c24a..b5f05b044882dd 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTokenStateManager.java @@ -5,6 +5,7 @@ import io.quarkus.oidc.AuthorizationCodeTokens; import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.TokenStateManager; +import io.smallrye.mutiny.Uni; import io.vertx.core.http.Cookie; import io.vertx.core.http.impl.ServerCookie; import io.vertx.ext.web.RoutingContext; @@ -16,8 +17,8 @@ public class DefaultTokenStateManager implements TokenStateManager { private static final String SESSION_RT_COOKIE_NAME = CodeAuthenticationMechanism.SESSION_COOKIE_NAME + "_rt"; @Override - public String createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig, - AuthorizationCodeTokens tokens) { + public Uni createTokenState(RoutingContext routingContext, OidcTenantConfig oidcConfig, + AuthorizationCodeTokens tokens, TokenStateManager.CreateTokenStateRequestContext requestContext) { StringBuilder sb = new StringBuilder(); sb.append(tokens.getIdToken()); if (oidcConfig.tokenStateManager.strategy == OidcTenantConfig.TokenStateManager.Strategy.KEEP_ALL_TOKENS) { @@ -56,11 +57,12 @@ public String createTokenState(RoutingContext routingContext, OidcTenantConfig o } } } - return sb.toString(); + return Uni.createFrom().item(sb.toString()); } @Override - public AuthorizationCodeTokens getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState) { + public Uni getTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState, + TokenStateManager.GetTokensRequestContext requestContext) { String[] tokens = CodeAuthenticationMechanism.COOKIE_PATTERN.split(tokenState); String idToken = tokens[0]; @@ -91,17 +93,19 @@ public AuthorizationCodeTokens getTokens(RoutingContext routingContext, OidcTena } } - return new AuthorizationCodeTokens(idToken, accessToken, refreshToken); + return Uni.createFrom().item(new AuthorizationCodeTokens(idToken, accessToken, refreshToken)); } @Override - public void deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState) { + public Uni deleteTokens(RoutingContext routingContext, OidcTenantConfig oidcConfig, String tokenState, + TokenStateManager.DeleteTokensRequestContext requestContext) { if (oidcConfig.tokenStateManager.splitTokens) { CodeAuthenticationMechanism.removeCookie(routingContext, getAccessTokenCookie(routingContext, oidcConfig), oidcConfig); CodeAuthenticationMechanism.removeCookie(routingContext, getRefreshTokenCookie(routingContext, oidcConfig), oidcConfig); } + return CodeAuthenticationMechanism.VOID_UNI; } private static ServerCookie getAccessTokenCookie(RoutingContext routingContext, OidcTenantConfig oidcConfig) { From 2d9e9dc35b0074035df6093a8e7c05b3fb861f03 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Tue, 31 Aug 2021 19:53:29 +0200 Subject: [PATCH 035/138] Update Vert.x version to 4.1.3 and Mutiny bindings to 2.13.0 --- bom/application/pom.xml | 6 +++--- independent-projects/resteasy-reactive/pom.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 5a28a7cab7d310..907bf88c6cf231 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -53,7 +53,7 @@ 1.2.0 1.0.13 2.6.0 - 2.12.0 + 2.13.0 3.9.1 1.2.1 1.3.5 @@ -109,7 +109,7 @@ 1.16.1.Final 1.8.7.Final 3.4.2.Final - 4.1.2 + 4.1.3 4.5.13 4.4.14 4.1.4 @@ -131,7 +131,7 @@ 12.1.7.Final 4.4.1.Final 2.9.2 - 4.1.65.Final + 4.1.67.Final 1.0.3 3.4.2.Final 1.0.0 diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 2aed06eaa2350e..f43d16df34780d 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -52,7 +52,7 @@ 1.1.6 1.0.0 1.6.0 - 4.1.2 + 4.1.3 4.4.0 1.0.0.Final 2.0.0.Final From 6ac8ad3884bfee5915c42f14b0bb7bfff8d30baf Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Tue, 31 Aug 2021 21:11:43 +0200 Subject: [PATCH 036/138] Fix NoSuchElementException on `@Before/AfterAll` with TestInfo Fixes #19800 --- .../it/main/QuarkusTestCallbacksTestCase.java | 21 +++++++++++++++++++ .../test/junit/QuarkusTestExtension.java | 6 ++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java index af121205b21c2a..80983c0f53bc08 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java @@ -3,6 +3,7 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -11,7 +12,9 @@ import java.lang.annotation.Target; import java.lang.reflect.Method; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -27,6 +30,22 @@ @QuarkusTest public class QuarkusTestCallbacksTestCase { + @BeforeAll + static void beforeAllWithTestInfo(TestInfo testInfo) { + checkBeforeOrAfterAllTestInfo(testInfo); + } + + @AfterAll + static void afterAllWithTestInfo(TestInfo testInfo) { + checkBeforeOrAfterAllTestInfo(testInfo); + } + + private static void checkBeforeOrAfterAllTestInfo(TestInfo testInfo) { + assertNotNull(testInfo); + assertEquals(QuarkusTestCallbacksTestCase.class, testInfo.getTestClass().get()); + assertFalse(testInfo.getTestMethod().isPresent()); + } + @BeforeEach void beforeEachWithTestInfo(TestInfo testInfo) throws NoSuchMethodException { checkBeforeOrAfterEachTestInfo(testInfo, "beforeEachWithTestInfo"); @@ -43,6 +62,7 @@ private void checkBeforeOrAfterEachTestInfo(TestInfo testInfo, String unexpected assertNotEquals(testMethodName, QuarkusTestCallbacksTestCase.class.getDeclaredMethod(unexpectedMethodName, TestInfo.class)); assertTrue(testMethodName.startsWith("test")); + assertEquals(QuarkusTestCallbacksTestCase.class, testInfo.getTestClass().get()); } @Test @@ -66,6 +86,7 @@ public void testInfoTestCase(TestInfo testInfo) throws NoSuchMethodException { Method testMethod = testInfo.getTestMethod().get(); assertEquals(testMethod, QuarkusTestCallbacksTestCase.class.getDeclaredMethod("testInfoTestCase", TestInfo.class)); assertEquals(1, testMethod.getAnnotationsByType(TestAnnotation.class).length); + assertEquals(QuarkusTestCallbacksTestCase.class, testInfo.getTestClass().get()); } @Target({ METHOD }) diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java index deb9fc5f555409..edc69c08052312 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java @@ -1039,9 +1039,11 @@ private Object runExtensionMethod(ReflectiveInvocationContext invocation cloneRequired = false; } else if (TestInfo.class.isAssignableFrom(theclass)) { TestInfo info = (TestInfo) arg; - Method newTestMethod = determineTCCLExtensionMethod(info.getTestMethod().get(), testClassFromTCCL); + Method newTestMethod = info.getTestMethod().isPresent() + ? determineTCCLExtensionMethod(info.getTestMethod().get(), testClassFromTCCL) + : null; replacement = new TestInfoImpl(info.getDisplayName(), info.getTags(), Optional.of(testClassFromTCCL), - Optional.of(newTestMethod)); + Optional.ofNullable(newTestMethod)); } else if (clonePattern.matcher(className).matches()) { cloneRequired = true; } else { From f78e00ace9d9696cdebd499817a7cb70023ccd55 Mon Sep 17 00:00:00 2001 From: Erin Schnabel Date: Tue, 31 Aug 2021 15:16:11 -0400 Subject: [PATCH 037/138] CLI: docs and help for -P --- .../quarkus/cli/common/TargetQuarkusVersionGroup.java | 11 +++++++++-- docs/src/main/asciidoc/cli-tooling.adoc | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java index 5e3b2a1254a620..7444fd2418a984 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java @@ -8,6 +8,8 @@ import picocli.CommandLine.Model.CommandSpec; public class TargetQuarkusVersionGroup { + final static String FULL_EXAMPLE = ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + ":" + + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + ":2.2.0.Final"; StreamCoords streamCoords = null; String validStream = null; @@ -18,7 +20,7 @@ public class TargetQuarkusVersionGroup { CommandSpec spec; @CommandLine.Option(paramLabel = "platformKey:streamId", names = { "-S", - "--stream" }, description = "A target stream, for example:%n io.quarkus.platform:999-SNAPSHOT%n io.quarkus.platform:2.0") + "--stream" }, description = "A target stream, for example:%n io.quarkus.platform:2.0") void setStream(String stream) { stream = stream.trim(); if (!stream.isEmpty()) { @@ -34,7 +36,12 @@ void setStream(String stream) { } @CommandLine.Option(paramLabel = "groupId:artifactId:version", names = { "-P", - "--platform-bom" }, description = "A specific Quarkus platform BOM, for example:%n io.quarkus:quarkus-bom:2.0.0.Final") + "--platform-bom" }, description = "A specific Quarkus platform BOM, for example:%n" + + " " + FULL_EXAMPLE + "%n" + + " io.quarkus::999-SNAPSHOT" + + " 2.2.0.Final%n" + + "Default groupId: " + ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + "%n" + + "Default artifactId: " + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + "%n") void setPlatformBom(String bom) { bom = bom.replaceFirst("^::", "").trim(); if (!bom.isEmpty()) { diff --git a/docs/src/main/asciidoc/cli-tooling.adoc b/docs/src/main/asciidoc/cli-tooling.adoc index e2a39ca3599599..c6ef10ea64b003 100644 --- a/docs/src/main/asciidoc/cli-tooling.adoc +++ b/docs/src/main/asciidoc/cli-tooling.adoc @@ -173,12 +173,12 @@ Both `quarkus create` and `quarkus extension list` allow you to explicitly speci 1. Specify a specific Platform Release BOM + -A https://quarkus.io/guides/platform#quarkus-platform-bom[Quarkus Platform release BOM] is identified by `groupId:artifactId:version` (GAV) coordinates. When specifying a platform release BOM, you may use empty segments to fallback to default values (groupId: `io.quarkus`, artifactId: `quarkus-bom`, version: cli version). If you specify only one segment (no `:`), it is assumed to be a version. +A https://quarkus.io/guides/platform#quarkus-platform-bom[Quarkus Platform release BOM] is identified by `groupId:artifactId:version` (GAV) coordinates. When specifying a platform release BOM, you may use empty segments to fallback to default values (shown with `quarkus create app --help`). If you specify only one segment (no `:`), it is assumed to be a version. + For example: + -- Given the `2.0.0.Final` version of the CLI, specifying `-P :quarkus-bom:` is equivalent to `-P io.quarkus.platform:quarkus-bom:2.0.0.Final`. -- Specifying `-P 999-SNAPSHOT` is equivalent to `-P io.quarkus:quarkus-bom:999-SNAPSHOT` +- With the `2.0.0.Final` version of the CLI, specifying `-P :quarkus-bom:` is equivalent to `-P io.quarkus:quarkus-bom:2.0.0.Final`. Specifying `-P 999-SNAPSHOT` is equivalent to `-P io.quarkus:quarkus-bom:999-SNAPSHOT`. +- With the `2.1.0.Final` version of the CLI, `io.quarkus.platform` is the default group id. Specifying `-P :quarkus-bom:` is equivalent to `-P io.quarkus.platform:quarkus-bom:2.1.0.Final`. Note that you need to specify the group id to work with a snapshot, e.g `-P io.quarkus::999-SNAPSHOT` is equivalent to `-P io.quarkus:quarkus-bom:999-SNAPSHOT`. + Note: default values are subject to change. Using the `--dry-run` option will show you the computed value. From d0d2d57c686ea156c03eb7ea865ebb6be70ed6d2 Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Tue, 31 Aug 2021 21:49:53 +0200 Subject: [PATCH 038/138] Do not propagate java compiler argument in kotlin compilation provider --- .../java/io/quarkus/gradle/QuarkusPlugin.java | 3 ++- .../io/quarkus/gradle/tasks/QuarkusDev.java | 8 +++++- .../build.gradle | 26 +++++++++++++++++++ .../gradle.properties | 4 +++ .../settings.gradle | 18 +++++++++++++ .../main/kotlin/org/acme/GreetingResource.kt | 16 ++++++++++++ ...sicKotlinApplicationModuleDevModeTest.java | 26 +++++++++++++++++++ 7 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 integration-tests/gradle/src/main/resources/basic-kotlin-application-project/build.gradle create mode 100644 integration-tests/gradle/src/main/resources/basic-kotlin-application-project/gradle.properties create mode 100644 integration-tests/gradle/src/main/resources/basic-kotlin-application-project/settings.gradle create mode 100644 integration-tests/gradle/src/main/resources/basic-kotlin-application-project/src/main/kotlin/org/acme/GreetingResource.kt create mode 100644 integration-tests/gradle/src/test/java/io/quarkus/gradle/devmode/BasicKotlinApplicationModuleDevModeTest.java diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java index 0130082fdd6756..fb1b6b8618a331 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java @@ -121,7 +121,7 @@ private void registerTasks(Project project, QuarkusPluginExtension quarkusExt) { Task quarkusBuild = tasks.create(QUARKUS_BUILD_TASK_NAME, QuarkusBuild.class); quarkusBuild.dependsOn(quarkusGenerateCode); - Task quarkusDev = tasks.create(QUARKUS_DEV_TASK_NAME, QuarkusDev.class); + QuarkusDev quarkusDev = tasks.create(QUARKUS_DEV_TASK_NAME, QuarkusDev.class); Task quarkusRemoteDev = tasks.create(QUARKUS_REMOTE_DEV_TASK_NAME, QuarkusRemoteDev.class); Task quarkusTest = tasks.create(QUARKUS_TEST_TASK_NAME, QuarkusTest.class); tasks.create(QUARKUS_TEST_CONFIG_TASK_NAME, QuarkusTestConfig.class); @@ -233,6 +233,7 @@ public void execute(Task test) { }); project.getPlugins().withId("org.jetbrains.kotlin.jvm", plugin -> { + quarkusDev.shouldPropagateJavaCompilerArgs(false); tasks.getByName("compileKotlin").dependsOn(quarkusGenerateCode); tasks.getByName("compileTestKotlin").dependsOn(quarkusGenerateCodeTests); }); diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java index 3c050bb07db45e..f28c8bca01aa56 100644 --- a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java +++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java @@ -74,6 +74,8 @@ public class QuarkusDev extends QuarkusTask { private List compilerArgs = new LinkedList<>(); + private boolean shouldPropagateJavaCompilerArgs = true; + @Inject public QuarkusDev() { super("Development mode: enables hot deployment with background compilation"); @@ -303,7 +305,7 @@ private QuarkusDevModeLauncher newLauncher() throws Exception { builder.targetJavaVersion(javaPluginConvention.getTargetCompatibility().toString()); } - if (getCompilerArgs().isEmpty()) { + if (getCompilerArgs().isEmpty() && shouldPropagateJavaCompilerArgs) { getJavaCompileTask() .map(compileTask -> compileTask.getOptions().getCompilerArgs()) .ifPresent(builder::compilerOptions); @@ -571,4 +573,8 @@ private void addToClassPaths(GradleDevModeLauncher.Builder classPathManifest, Fi classPathManifest.classpathEntry(file); } } + + public void shouldPropagateJavaCompilerArgs(boolean shouldPropagateJavaCompilerArgs) { + this.shouldPropagateJavaCompilerArgs = shouldPropagateJavaCompilerArgs; + } } diff --git a/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/build.gradle b/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/build.gradle new file mode 100644 index 00000000000000..6a3efd26bb298e --- /dev/null +++ b/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'io.quarkus' +} + +repositories { + if (System.properties.containsKey('maven.repo.local')) { + maven { + url System.properties.get('maven.repo.local') + } + } else { + mavenLocal() + } + mavenCentral() +} + +dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") + implementation 'io.quarkus:quarkus-resteasy' + implementation 'io.quarkus:quarkus-kotlin' +} + +compileJava { + options.compilerArgs << '-parameters' +} diff --git a/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/gradle.properties b/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/gradle.properties new file mode 100644 index 00000000000000..15d78eaf882e51 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/gradle.properties @@ -0,0 +1,4 @@ +kotlinVersion=1.5.30 + +quarkusPlatformArtifactId=quarkus-bom +quarkusPlatformGroupId=io.quarkus diff --git a/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/settings.gradle b/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/settings.gradle new file mode 100644 index 00000000000000..fb99dc799e1e2f --- /dev/null +++ b/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/settings.gradle @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + if (System.properties.containsKey('maven.repo.local')) { + maven { + url System.properties.get('maven.repo.local') + } + } else { + mavenLocal() + } + mavenCentral() + gradlePluginPortal() + } + plugins { + id 'io.quarkus' version "${quarkusPluginVersion}" + id 'org.jetbrains.kotlin.jvm' version "${kotlinVersion}" + } +} +rootProject.name='code-with-quarkus' diff --git a/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/src/main/kotlin/org/acme/GreetingResource.kt b/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/src/main/kotlin/org/acme/GreetingResource.kt new file mode 100644 index 00000000000000..7634ba14219048 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/basic-kotlin-application-project/src/main/kotlin/org/acme/GreetingResource.kt @@ -0,0 +1,16 @@ +package org.acme + +import javax.ws.rs.GET +import javax.ws.rs.Path +import javax.ws.rs.Produces +import javax.ws.rs.core.MediaType + +@Path("/hello") +class GreetingResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + fun hello(): String { + return "hello" + } +} \ No newline at end of file diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/devmode/BasicKotlinApplicationModuleDevModeTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/devmode/BasicKotlinApplicationModuleDevModeTest.java new file mode 100644 index 00000000000000..a98c32842b24b9 --- /dev/null +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/devmode/BasicKotlinApplicationModuleDevModeTest.java @@ -0,0 +1,26 @@ +package io.quarkus.gradle.devmode; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.UUID; + +import com.google.common.collect.ImmutableMap; + +public class BasicKotlinApplicationModuleDevModeTest extends QuarkusDevGradleTestBase { + + @Override + protected String projectDirectoryName() { + return "basic-kotlin-application-project"; + } + + @Override + protected void testDevMode() throws Exception { + assertThat(getHttpResponse("/hello")).contains("hello"); + + final String uuid = UUID.randomUUID().toString(); + replace("src/main/kotlin/org/acme/GreetingResource.kt", + ImmutableMap.of("return \"hello\"", "return \"" + uuid + "\"")); + + assertUpdatedResponseContains("/hello", uuid); + } +} From d9963fc7f5739e455b1ab7b336ec43bf01bbdb13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Aug 2021 21:08:59 +0000 Subject: [PATCH 039/138] Bump awssdk.version from 2.17.30 to 2.17.31 Bumps `awssdk.version` from 2.17.30 to 2.17.31. Updates `software.amazon.awssdk:bom` from 2.17.30 to 2.17.31 - [Release notes](https://github.com/aws/aws-sdk-java-v2/releases) - [Changelog](https://github.com/aws/aws-sdk-java-v2/blob/master/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-java-v2/compare/2.17.30...2.17.31) Updates `apache-client` from 2.17.30 to 2.17.31 --- updated-dependencies: - dependency-name: software.amazon.awssdk:bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:apache-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index ebfe0a06a2c6f1..ddc2796f3ad246 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -145,7 +145,7 @@ 3.10.0 1.6 2.9.1 - 2.17.30 + 2.17.31 2.40.0 1.4.2 1.5.30 From fb4c1cbb92ad588bd48cec3d07a223ddfec73a91 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Tue, 17 Aug 2021 09:36:14 +1000 Subject: [PATCH 040/138] Add some substitutions for Brotli and Zstd --- extensions/vertx-http/runtime/pom.xml | 5 + .../HttpContentCompressorSubstitutions.java | 115 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/graal/HttpContentCompressorSubstitutions.java diff --git a/extensions/vertx-http/runtime/pom.xml b/extensions/vertx-http/runtime/pom.xml index a1f7c667077d84..fabcf3c34282df 100644 --- a/extensions/vertx-http/runtime/pom.xml +++ b/extensions/vertx-http/runtime/pom.xml @@ -53,6 +53,11 @@ + + org.graalvm.nativeimage + svm + provided + org.junit.jupiter diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/graal/HttpContentCompressorSubstitutions.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/graal/HttpContentCompressorSubstitutions.java new file mode 100644 index 00000000000000..37dac442fbcf17 --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/graal/HttpContentCompressorSubstitutions.java @@ -0,0 +1,115 @@ +package io.quarkus.vertx.http.runtime.graal; + +import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE; +import static io.netty.handler.codec.http.HttpHeaderValues.GZIP; +import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE; +import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP; + +import java.util.function.BooleanSupplier; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.compression.ZlibWrapper; +import io.netty.handler.codec.http2.CompressorHttp2ConnectionEncoder; +import io.netty.handler.codec.http2.Http2Exception; + +public class HttpContentCompressorSubstitutions { + + @TargetClass(className = "io.netty.handler.codec.compression.ZstdEncoder", onlyWith = IsZstdAbsent.class) + public static final class ZstdEncoderFactorySubstitution { + + @Substitute + protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception { + throw new UnsupportedOperationException(); + } + + @Substitute + protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) { + throw new UnsupportedOperationException(); + } + + @Substitute + public void flush(final ChannelHandlerContext ctx) { + throw new UnsupportedOperationException(); + } + } + + @TargetClass(className = "io.netty.handler.codec.compression.BrotliEncoder", onlyWith = IsBrotliAbsent.class) + public static final class BrEncoderFactorySubstitution { + + @Substitute + protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception { + throw new UnsupportedOperationException(); + } + + @Substitute + protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) { + throw new UnsupportedOperationException(); + } + } + + @TargetClass(CompressorHttp2ConnectionEncoder.class) + public static final class CompressorHttp2ConnectionSubstitute { + + @Substitute + protected EmbeddedChannel newContentCompressor(ChannelHandlerContext ctx, CharSequence contentEncoding) + throws Http2Exception { + if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) { + return newCompressionChannel(ctx, ZlibWrapper.GZIP); + } + if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) { + return newCompressionChannel(ctx, ZlibWrapper.ZLIB); + } + // 'identity' or unsupported + return null; + } + + @Alias + private EmbeddedChannel newCompressionChannel(final ChannelHandlerContext ctx, ZlibWrapper wrapper) { + throw new UnsupportedOperationException(); + } + } + + public static class IsZstdAbsent implements BooleanSupplier { + + private boolean zstdAbsent; + + public IsZstdAbsent() { + try { + Class.forName("com.github.luben.zstd.Zstd"); + zstdAbsent = false; + } catch (ClassNotFoundException e) { + zstdAbsent = true; + } + } + + @Override + public boolean getAsBoolean() { + return zstdAbsent; + } + } + + public static class IsBrotliAbsent implements BooleanSupplier { + + private boolean brotliAbsent; + + public IsBrotliAbsent() { + try { + Class.forName("com.aayushatharva.brotli4j.encoder.Encoder"); + brotliAbsent = false; + } catch (ClassNotFoundException e) { + brotliAbsent = true; + } + } + + @Override + public boolean getAsBoolean() { + return brotliAbsent; + } + } +} From a3d5b90cc5fbe17ae79864c0f3868369715d8d46 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 1 Sep 2021 09:28:47 +1000 Subject: [PATCH 041/138] Fix some tests the broke due to default change The max attribute size changed in vert.x --- ...anDefaultFormAttributeMultipartFormInputTest.java | 8 +++++++- .../TooLargeFormAttributeMultipartFormInputTest.java | 12 ++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/LargerThanDefaultFormAttributeMultipartFormInputTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/LargerThanDefaultFormAttributeMultipartFormInputTest.java index 5489e49172b3f6..225943678fa69e 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/LargerThanDefaultFormAttributeMultipartFormInputTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/LargerThanDefaultFormAttributeMultipartFormInputTest.java @@ -39,7 +39,7 @@ public JavaArchive get() { return ShrinkWrap.create(JavaArchive.class) .addClasses(Resource.class, Data.class) .addAsResource(new StringAsset( - "quarkus.http.limits.max-form-attribute-size=4K"), + "quarkus.http.limits.max-form-attribute-size=120K"), "application.properties"); } }); @@ -49,6 +49,12 @@ public JavaArchive get() { @Test public void test() throws IOException { String fileContents = new String(Files.readAllBytes(FILE.toPath()), StandardCharsets.UTF_8); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; ++i) { + sb.append(fileContents); + } + fileContents = sb.toString(); + Assertions.assertTrue(fileContents.length() > HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE); given() .multiPart("text", fileContents) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/TooLargeFormAttributeMultipartFormInputTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/TooLargeFormAttributeMultipartFormInputTest.java index 6699beab8c705d..ef3d7553191684 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/TooLargeFormAttributeMultipartFormInputTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/TooLargeFormAttributeMultipartFormInputTest.java @@ -66,9 +66,13 @@ public void clearDirectory() { @Test public void test() throws IOException { - String formAttrSourceFileContents = new String(Files.readAllBytes(FORM_ATTR_SOURCE_FILE.toPath()), - StandardCharsets.UTF_8); - Assertions.assertTrue(formAttrSourceFileContents.length() > HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE); + String fileContents = new String(Files.readAllBytes(FORM_ATTR_SOURCE_FILE.toPath()), StandardCharsets.UTF_8); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; ++i) { + sb.append(fileContents); + } + fileContents = sb.toString(); + Assertions.assertTrue(fileContents.length() > HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE); given() .multiPart("active", "true") .multiPart("num", "25") @@ -76,7 +80,7 @@ public void test() throws IOException { .multiPart("htmlFile", HTML_FILE, "text/html") .multiPart("xmlFile", XML_FILE, "text/xml") .multiPart("txtFile", TXT_FILE, "text/plain") - .multiPart("name", formAttrSourceFileContents) + .multiPart("name", fileContents) .accept("text/plain") .when() .post("/test") From 9e43b968fa0ecb2fec53890118c61be838b408d2 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 1 Sep 2021 08:43:24 +1000 Subject: [PATCH 042/138] Don't rely on vert.x for CT tests --- bom/application/pom.xml | 7 - .../ContinuousTestingSharedStateListener.java | 93 +++++++++ .../dev/testing/TestTracingProcessor.java | 5 + .../ContinuousTestingSharedStateManager.java | 150 ++++++++++++++ .../ContinuousTestingWebsocketListener.java | 108 ---------- .../elytron-security-jdbc/deployment/pom.xml | 6 - .../jdbc/CustomRoleDecoderDevModeTest.java | 4 +- extensions/hibernate-orm/deployment/pom.xml | 6 - .../orm/HibernateHotReloadTestCase.java | 4 +- .../opentelemetry/deployment/pom.xml | 6 - .../OpenTelemetryContinuousTestingTest.java | 4 +- .../deployment/OpenTelemetryDevModeTest.java | 2 +- .../deployment/pom.xml | 6 - ...aDevServicesContinuousTestingTestCase.java | 2 +- ...tinuousTestingWorkingAppPropsTestCase.java | 2 +- extensions/vertx-http/deployment/pom.xml | 11 -- ...ontinuousTestingWebSocketTestListener.java | 107 ---------- .../devmode/tests/TestsProcessor.java | 2 - .../ContinuousTestingTestUtils.java | 53 ----- .../testrunner/QuarkusTestTypeTestCase.java | 3 +- ...tChangeTrackingWhenStartFailsTestCase.java | 3 +- .../TestFailingBeforeAllTestCase.java | 3 +- .../testrunner/TestRunnerSmokeTestCase.java | 3 +- .../http/testrunner/UnitTestTypeTestCase.java | 3 +- .../brokenonly/TestBrokenOnlyTestCase.java | 4 +- .../includes/ExcludePatternTestCase.java | 4 +- .../includes/IncludePatternTestCase.java | 4 +- .../params/TestParameterizedTestCase.java | 4 +- .../testrunner/tags/ExcludeTagsTestCase.java | 4 +- .../testrunner/tags/IncludeTagsTestCase.java | 4 +- .../ContinuousTestWebSocketHandler.java | 8 +- .../runtime/devmode/DevConsoleRecorder.java | 7 +- test-framework/junit5-internal/pom.xml | 4 + .../test/ContinuousTestingTestUtils.java | 185 ++++++++++++++++++ 34 files changed, 476 insertions(+), 345 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/dev/testing/ContinuousTestingSharedStateListener.java create mode 100644 core/devmode-spi/src/main/java/io/quarkus/dev/testing/ContinuousTestingSharedStateManager.java delete mode 100644 core/devmode-spi/src/main/java/io/quarkus/dev/testing/ContinuousTestingWebsocketListener.java delete mode 100644 extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketTestListener.java delete mode 100644 extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/ContinuousTestingTestUtils.java create mode 100644 test-framework/junit5-internal/src/main/java/io/quarkus/test/ContinuousTestingTestUtils.java diff --git a/bom/application/pom.xml b/bom/application/pom.xml index f1c39ced2103be..5cf1616cc82f10 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -1540,13 +1540,6 @@ quarkus-vertx-http-deployment ${project.version} - - io.quarkus - quarkus-vertx-http-deployment - ${project.version} - test-jar - test - io.quarkus quarkus-vertx-web diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/ContinuousTestingSharedStateListener.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/ContinuousTestingSharedStateListener.java new file mode 100644 index 00000000000000..6604230f961b21 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/ContinuousTestingSharedStateListener.java @@ -0,0 +1,93 @@ +package io.quarkus.deployment.dev.testing; + +import java.util.function.Consumer; + +import io.quarkus.dev.testing.ContinuousTestingSharedStateManager; + +public class ContinuousTestingSharedStateListener implements TestListener { + + private volatile ContinuousTestingSharedStateManager.State lastState; + + @Override + public void listenerRegistered(TestController testController) { + + } + + @Override + public void testsEnabled() { + if (lastState == null) { + ContinuousTestingSharedStateManager + .setLastState(new ContinuousTestingSharedStateManager.State( + ContinuousTestingSharedStateManager.getLastState().lastRun, true, false, + 0, 0, 0, 0, + 0, 0, 0, ContinuousTestingSharedStateManager.getLastState().isBrokenOnly, + ContinuousTestingSharedStateManager.getLastState().isTestOutput, + ContinuousTestingSharedStateManager.getLastState().isInstrumentationBasedReload, + ContinuousTestingSharedStateManager.getLastState().isLiveReload)); + } else { + ContinuousTestingSharedStateManager + .setLastState(lastState); + } + } + + @Override + public void testsDisabled() { + ContinuousTestingSharedStateManager.setRunning(false); + } + + @Override + public void testRunStarted(Consumer listenerConsumer) { + ContinuousTestingSharedStateManager.setInProgress(true); + listenerConsumer.accept(new TestRunListener() { + + @Override + public void runComplete(TestRunResults testRunResults) { + lastState = new ContinuousTestingSharedStateManager.State(testRunResults.getId(), true, false, + testRunResults.getPassedCount() + + testRunResults.getFailedCount() + + testRunResults.getSkippedCount(), + testRunResults.getPassedCount(), + testRunResults.getFailedCount(), testRunResults.getSkippedCount(), + testRunResults.getCurrentPassedCount(), testRunResults.getCurrentFailedCount(), + testRunResults.getCurrentSkippedCount(), + ContinuousTestingSharedStateManager.getLastState().isBrokenOnly, + ContinuousTestingSharedStateManager.getLastState().isTestOutput, + ContinuousTestingSharedStateManager.getLastState().isInstrumentationBasedReload, + ContinuousTestingSharedStateManager.getLastState().isLiveReload); + ContinuousTestingSharedStateManager.setLastState(lastState); + } + + @Override + public void noTests(TestRunResults results) { + runComplete(results); + } + + @Override + public void runAborted() { + ContinuousTestingSharedStateManager.setInProgress(false); + } + }); + + } + + @Override + public void setBrokenOnly(boolean bo) { + ContinuousTestingSharedStateManager.setBrokenOnly(bo); + } + + @Override + public void setTestOutput(boolean to) { + ContinuousTestingSharedStateManager.setTestOutput(to); + } + + @Override + public void setInstrumentationBasedReload(boolean ibr) { + ContinuousTestingSharedStateManager.setInstrumentationBasedReload(ibr); + } + + @Override + public void setLiveReloadEnabled(boolean lre) { + ContinuousTestingSharedStateManager.setLiveReloadEnabled(lre); + } + +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestTracingProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestTracingProcessor.java index 9da423bb4f1bd5..7e2777caaa5e41 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestTracingProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestTracingProcessor.java @@ -42,6 +42,11 @@ LogCleanupFilterBuildItem handle() { static volatile boolean testingSetup; + @BuildStep + TestListenerBuildItem sharedStateListener() { + return new TestListenerBuildItem(new ContinuousTestingSharedStateListener()); + } + @BuildStep(onlyIf = IsDevelopment.class) @Produce(LogHandlerBuildItem.class) @Produce(TestSetupBuildItem.class) diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/testing/ContinuousTestingSharedStateManager.java b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/ContinuousTestingSharedStateManager.java new file mode 100644 index 00000000000000..cc981b36e9009b --- /dev/null +++ b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/ContinuousTestingSharedStateManager.java @@ -0,0 +1,150 @@ +package io.quarkus.dev.testing; + +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.Consumer; + +public class ContinuousTestingSharedStateManager { + + private static final CopyOnWriteArraySet> stateListeners = new CopyOnWriteArraySet<>(); + public static final State INITIAL_STATE = new State(-1, false, false, 0, 0, 0, 0, 0, 0, 0, false, false, false, true); + private static volatile State lastState = INITIAL_STATE; + + public static void addStateListener(Consumer stateListener) { + stateListeners.add(stateListener); + if (lastState != null) { + stateListener.accept(lastState); + } + } + + public static void removeStateListener(Consumer stateListener) { + stateListeners.remove(stateListener); + } + + public static void reset() { + setLastState(INITIAL_STATE); + } + + public static void setLastState(State state) { + lastState = state; + for (var sl : stateListeners) { + sl.accept(state); + } + } + + public static State getLastState() { + return lastState; + } + + public static void setInProgress(boolean inProgress) { + State state = lastState; + if (state != null) { + setLastState( + new State(state.lastRun, state.running, inProgress, state.run, state.passed, state.failed, state.skipped, + state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, + state.isTestOutput, state.isInstrumentationBasedReload, state.isLiveReload)); + } + } + + public static void setRunning(boolean running) { + State state = lastState; + if (state != null) { + setLastState(new State(state.lastRun, running, running && state.inProgress, state.run, state.passed, state.failed, + state.skipped, + state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, state.isTestOutput, + state.isInstrumentationBasedReload, state.isLiveReload)); + } + } + + public static void setBrokenOnly(boolean brokenOnly) { + State state = lastState; + if (state != null) { + setLastState(new State(state.lastRun, state.running, state.inProgress, state.run, state.passed, state.failed, + state.skipped, + state.currentPassed, state.currentFailed, state.currentSkipped, brokenOnly, state.isTestOutput, + state.isInstrumentationBasedReload, state.isLiveReload)); + } + } + + public static void setTestOutput(boolean testOutput) { + State state = lastState; + if (state != null) { + setLastState(new State(state.lastRun, state.running, state.inProgress, state.run, state.passed, state.failed, + state.skipped, + state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, testOutput, + state.isInstrumentationBasedReload, state.isLiveReload)); + } + } + + public static void setInstrumentationBasedReload(boolean instrumentationBasedReload) { + State state = lastState; + if (state != null) { + setLastState(new State(state.lastRun, state.running, state.inProgress, state.run, state.passed, state.failed, + state.skipped, + state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, state.isTestOutput, + instrumentationBasedReload, state.isLiveReload)); + } + } + + public static void setLiveReloadEnabled(boolean liveReload) { + State state = lastState; + if (state != null) { + setLastState(new State(state.lastRun, state.running, state.inProgress, state.run, state.passed, state.failed, + state.skipped, + state.currentPassed, state.currentFailed, state.currentSkipped, state.isBrokenOnly, state.isTestOutput, + state.isInstrumentationBasedReload, liveReload)); + } + } + + public static class State { + public final long lastRun; + public final boolean running; + public final boolean inProgress; + public final long run; + public final long passed; + public final long failed; + public final long skipped; + public final long currentPassed; + public final long currentFailed; + public final long currentSkipped; + public final boolean isBrokenOnly; + public final boolean isTestOutput; + public final boolean isInstrumentationBasedReload; + public final boolean isLiveReload; + + public State(long lastRun, boolean running, boolean inProgress, long run, long passed, long failed, long skipped, + long currentPassed, long currentFailed, long currentSkipped, boolean isBrokenOnly, boolean isTestOutput, + boolean isInstrumentationBasedReload, boolean isLiveReload) { + this.lastRun = lastRun; + this.running = running; + this.inProgress = inProgress; + this.run = run; + this.passed = passed; + this.failed = failed; + this.skipped = skipped; + this.currentPassed = currentPassed; + this.currentFailed = currentFailed; + this.currentSkipped = currentSkipped; + this.isBrokenOnly = isBrokenOnly; + this.isTestOutput = isTestOutput; + this.isInstrumentationBasedReload = isInstrumentationBasedReload; + this.isLiveReload = isLiveReload; + } + + @Override + public String toString() { + return "State{" + + "lastRun=" + lastRun + + ", running=" + running + + ", inProgress=" + inProgress + + ", run=" + run + + ", passed=" + passed + + ", failed=" + failed + + ", skipped=" + skipped + + ", isBrokenOnly=" + isBrokenOnly + + ", isTestOutput=" + isTestOutput + + ", isInstrumentationBasedReload=" + isInstrumentationBasedReload + + ", isLiveReload=" + isLiveReload + + '}'; + } + } +} diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/testing/ContinuousTestingWebsocketListener.java b/core/devmode-spi/src/main/java/io/quarkus/dev/testing/ContinuousTestingWebsocketListener.java deleted file mode 100644 index bc272a58a1db5d..00000000000000 --- a/core/devmode-spi/src/main/java/io/quarkus/dev/testing/ContinuousTestingWebsocketListener.java +++ /dev/null @@ -1,108 +0,0 @@ -package io.quarkus.dev.testing; - -import java.util.function.Consumer; - -//TODO: this is pretty horrible -public class ContinuousTestingWebsocketListener { - - private static Consumer stateListener; - private static volatile State lastState = new State(false, false, 0, 0, 0, 0, false, false, false, true); - - public static Consumer getStateListener() { - return stateListener; - } - - public static void setStateListener(Consumer stateListener) { - ContinuousTestingWebsocketListener.stateListener = stateListener; - if (lastState != null && stateListener != null) { - stateListener.accept(lastState); - } - } - - public static void setLastState(State state) { - lastState = state; - Consumer sl = stateListener; - if (sl != null) { - sl.accept(state); - } - } - - public static State getLastState() { - return lastState; - } - - public static void setInProgress(boolean inProgress) { - State state = lastState; - if (state != null) { - setLastState(new State(state.running, inProgress, state.run, state.passed, state.failed, state.skipped, - state.isBrokenOnly, state.isTestOutput, state.isInstrumentationBasedReload, state.isLiveReload)); - } - } - - public static void setRunning(boolean running) { - State state = lastState; - if (state != null) { - setLastState(new State(running, running && state.inProgress, state.run, state.passed, state.failed, state.skipped, - state.isBrokenOnly, state.isTestOutput, state.isInstrumentationBasedReload, state.isLiveReload)); - } - } - - public static void setBrokenOnly(boolean brokenOnly) { - State state = lastState; - if (state != null) { - setLastState(new State(state.running, state.inProgress, state.run, state.passed, state.failed, state.skipped, - brokenOnly, state.isTestOutput, state.isInstrumentationBasedReload, state.isLiveReload)); - } - } - - public static void setTestOutput(boolean testOutput) { - State state = lastState; - if (state != null) { - setLastState(new State(state.running, state.inProgress, state.run, state.passed, state.failed, state.skipped, - state.isBrokenOnly, testOutput, state.isInstrumentationBasedReload, state.isLiveReload)); - } - } - - public static void setInstrumentationBasedReload(boolean instrumentationBasedReload) { - State state = lastState; - if (state != null) { - setLastState(new State(state.running, state.inProgress, state.run, state.passed, state.failed, state.skipped, - state.isBrokenOnly, state.isTestOutput, instrumentationBasedReload, state.isLiveReload)); - } - } - - public static void setLiveReloadEnabled(boolean liveReload) { - State state = lastState; - if (state != null) { - setLastState(new State(state.running, state.inProgress, state.run, state.passed, state.failed, state.skipped, - state.isBrokenOnly, state.isTestOutput, state.isInstrumentationBasedReload, liveReload)); - } - } - - public static class State { - public final boolean running; - public final boolean inProgress; - public final long run; - public final long passed; - public final long failed; - public final long skipped; - public final boolean isBrokenOnly; - public final boolean isTestOutput; - public final boolean isInstrumentationBasedReload; - public final boolean isLiveReload; - - public State(boolean running, boolean inProgress, long run, long passed, long failed, long skipped, - boolean isBrokenOnly, boolean isTestOutput, boolean isInstrumentationBasedReload, boolean isLiveReload) { - this.running = running; - this.inProgress = inProgress; - this.run = run; - this.passed = passed; - this.failed = failed; - this.skipped = skipped; - this.isBrokenOnly = isBrokenOnly; - this.isTestOutput = isTestOutput; - this.isInstrumentationBasedReload = isInstrumentationBasedReload; - this.isLiveReload = isLiveReload; - } - } -} diff --git a/extensions/elytron-security-jdbc/deployment/pom.xml b/extensions/elytron-security-jdbc/deployment/pom.xml index 0e71187e66be69..44c3c8f921931b 100644 --- a/extensions/elytron-security-jdbc/deployment/pom.xml +++ b/extensions/elytron-security-jdbc/deployment/pom.xml @@ -46,12 +46,6 @@ quarkus-resteasy-deployment test - - io.quarkus - quarkus-vertx-http-deployment - test-jar - test - io.quarkus quarkus-junit5-internal diff --git a/extensions/elytron-security-jdbc/deployment/src/test/java/io/quarkus/elytron/security/jdbc/CustomRoleDecoderDevModeTest.java b/extensions/elytron-security-jdbc/deployment/src/test/java/io/quarkus/elytron/security/jdbc/CustomRoleDecoderDevModeTest.java index b55a8c75daf900..772ebd49aeecf4 100644 --- a/extensions/elytron-security-jdbc/deployment/src/test/java/io/quarkus/elytron/security/jdbc/CustomRoleDecoderDevModeTest.java +++ b/extensions/elytron-security-jdbc/deployment/src/test/java/io/quarkus/elytron/security/jdbc/CustomRoleDecoderDevModeTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.deployment.util.FileUtil; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; import io.restassured.RestAssured; //see https://github.com/quarkusio/quarkus/issues/9296 diff --git a/extensions/hibernate-orm/deployment/pom.xml b/extensions/hibernate-orm/deployment/pom.xml index 75cea02da2efea..f1cd0a90c9730d 100644 --- a/extensions/hibernate-orm/deployment/pom.xml +++ b/extensions/hibernate-orm/deployment/pom.xml @@ -87,12 +87,6 @@ quarkus-smallrye-metrics-deployment test - - io.quarkus - quarkus-vertx-http-deployment - test - test-jar - org.awaitility awaitility diff --git a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateHotReloadTestCase.java b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateHotReloadTestCase.java index 64a86b95bfe2b1..52c1bb4fb2f018 100644 --- a/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateHotReloadTestCase.java +++ b/extensions/hibernate-orm/deployment/src/test/java/io/quarkus/hibernate/orm/HibernateHotReloadTestCase.java @@ -11,9 +11,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; import io.restassured.RestAssured; public class HibernateHotReloadTestCase { diff --git a/extensions/opentelemetry/opentelemetry/deployment/pom.xml b/extensions/opentelemetry/opentelemetry/deployment/pom.xml index c066a7c1f65030..f124d4a8646183 100644 --- a/extensions/opentelemetry/opentelemetry/deployment/pom.xml +++ b/extensions/opentelemetry/opentelemetry/deployment/pom.xml @@ -69,12 +69,6 @@ quarkus-resteasy-deployment test - - io.quarkus - quarkus-vertx-http-deployment - test - test-jar - io.quarkus quarkus-smallrye-health-deployment diff --git a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryContinuousTestingTest.java b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryContinuousTestingTest.java index 8f213de335f5c3..8345afa9615b9e 100644 --- a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryContinuousTestingTest.java +++ b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryContinuousTestingTest.java @@ -7,9 +7,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; public class OpenTelemetryContinuousTestingTest { @RegisterExtension diff --git a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevModeTest.java b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevModeTest.java index 801673acdb8721..b7dc348682d116 100644 --- a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevModeTest.java +++ b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevModeTest.java @@ -8,8 +8,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; import io.restassured.RestAssured; public class OpenTelemetryDevModeTest { diff --git a/extensions/smallrye-reactive-messaging-kafka/deployment/pom.xml b/extensions/smallrye-reactive-messaging-kafka/deployment/pom.xml index bcde6e700bec72..8d8bff7aceca62 100644 --- a/extensions/smallrye-reactive-messaging-kafka/deployment/pom.xml +++ b/extensions/smallrye-reactive-messaging-kafka/deployment/pom.xml @@ -83,12 +83,6 @@ awaitility test - - io.quarkus - quarkus-vertx-http-deployment - test-jar - test - diff --git a/extensions/smallrye-reactive-messaging-kafka/deployment/src/test/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/testing/KafkaDevServicesContinuousTestingTestCase.java b/extensions/smallrye-reactive-messaging-kafka/deployment/src/test/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/testing/KafkaDevServicesContinuousTestingTestCase.java index e4a232da8146b0..8387a6674986f5 100644 --- a/extensions/smallrye-reactive-messaging-kafka/deployment/src/test/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/testing/KafkaDevServicesContinuousTestingTestCase.java +++ b/extensions/smallrye-reactive-messaging-kafka/deployment/src/test/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/testing/KafkaDevServicesContinuousTestingTestCase.java @@ -9,8 +9,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; public class KafkaDevServicesContinuousTestingTestCase { diff --git a/extensions/smallrye-reactive-messaging-kafka/deployment/src/test/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/testing/KafkaDevServicesContinuousTestingWorkingAppPropsTestCase.java b/extensions/smallrye-reactive-messaging-kafka/deployment/src/test/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/testing/KafkaDevServicesContinuousTestingWorkingAppPropsTestCase.java index e4fdcca5abe1a2..57c98b27fdf751 100644 --- a/extensions/smallrye-reactive-messaging-kafka/deployment/src/test/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/testing/KafkaDevServicesContinuousTestingWorkingAppPropsTestCase.java +++ b/extensions/smallrye-reactive-messaging-kafka/deployment/src/test/java/io/quarkus/smallrye/reactivemessaging/kafka/deployment/testing/KafkaDevServicesContinuousTestingWorkingAppPropsTestCase.java @@ -9,8 +9,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; public class KafkaDevServicesContinuousTestingWorkingAppPropsTestCase { diff --git a/extensions/vertx-http/deployment/pom.xml b/extensions/vertx-http/deployment/pom.xml index daa32430e8a055..ed795df314e552 100644 --- a/extensions/vertx-http/deployment/pom.xml +++ b/extensions/vertx-http/deployment/pom.xml @@ -119,17 +119,6 @@ - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - maven-compiler-plugin diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketTestListener.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketTestListener.java deleted file mode 100644 index 2d83492a8186b3..00000000000000 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ContinuousTestingWebSocketTestListener.java +++ /dev/null @@ -1,107 +0,0 @@ -package io.quarkus.vertx.http.deployment.devmode.console; - -import java.util.function.Consumer; - -import org.junit.platform.launcher.TestIdentifier; - -import io.quarkus.deployment.dev.testing.TestController; -import io.quarkus.deployment.dev.testing.TestListener; -import io.quarkus.deployment.dev.testing.TestResult; -import io.quarkus.deployment.dev.testing.TestRunListener; -import io.quarkus.deployment.dev.testing.TestRunResults; -import io.quarkus.dev.testing.ContinuousTestingWebsocketListener; - -public class ContinuousTestingWebSocketTestListener implements TestListener { - - private volatile ContinuousTestingWebsocketListener.State lastState; - - @Override - public void listenerRegistered(TestController testController) { - - } - - @Override - public void testsEnabled() { - if (lastState == null) { - ContinuousTestingWebsocketListener - .setLastState(new ContinuousTestingWebsocketListener.State(true, false, - 0, 0, 0, 0, - ContinuousTestingWebsocketListener.getLastState().isBrokenOnly, - ContinuousTestingWebsocketListener.getLastState().isTestOutput, - ContinuousTestingWebsocketListener.getLastState().isInstrumentationBasedReload, - ContinuousTestingWebsocketListener.getLastState().isLiveReload)); - } else { - ContinuousTestingWebsocketListener - .setLastState(lastState); - } - } - - @Override - public void testsDisabled() { - ContinuousTestingWebsocketListener.setRunning(false); - } - - @Override - public void testRunStarted(Consumer listenerConsumer) { - ContinuousTestingWebsocketListener.setInProgress(true); - listenerConsumer.accept(new TestRunListener() { - @Override - public void runStarted(long toRun) { - - } - - @Override - public void testComplete(TestResult result) { - - } - - @Override - public void runComplete(TestRunResults testRunResults) { - lastState = new ContinuousTestingWebsocketListener.State(true, false, - testRunResults.getPassedCount() + - testRunResults.getFailedCount() + - testRunResults.getSkippedCount(), - testRunResults.getPassedCount(), - testRunResults.getFailedCount(), testRunResults.getSkippedCount(), - ContinuousTestingWebsocketListener.getLastState().isBrokenOnly, - ContinuousTestingWebsocketListener.getLastState().isTestOutput, - ContinuousTestingWebsocketListener.getLastState().isInstrumentationBasedReload, - ContinuousTestingWebsocketListener.getLastState().isLiveReload); - ContinuousTestingWebsocketListener.setLastState( - lastState); - } - - @Override - public void runAborted() { - ContinuousTestingWebsocketListener.setInProgress(false); - } - - @Override - public void testStarted(TestIdentifier testIdentifier, String className) { - - } - }); - - } - - @Override - public void setBrokenOnly(boolean bo) { - ContinuousTestingWebsocketListener.setBrokenOnly(bo); - } - - @Override - public void setTestOutput(boolean to) { - ContinuousTestingWebsocketListener.setTestOutput(to); - } - - @Override - public void setInstrumentationBasedReload(boolean ibr) { - ContinuousTestingWebsocketListener.setInstrumentationBasedReload(ibr); - } - - @Override - public void setLiveReloadEnabled(boolean lre) { - ContinuousTestingWebsocketListener.setLiveReloadEnabled(lre); - } - -} diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java index 6bc93507d3b807..8b58de97ce3ff5 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/tests/TestsProcessor.java @@ -23,7 +23,6 @@ import io.quarkus.devconsole.spi.DevConsoleTemplateInfoBuildItem; import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; -import io.quarkus.vertx.http.deployment.devmode.console.ContinuousTestingWebSocketTestListener; import io.quarkus.vertx.http.runtime.devmode.DevConsoleRecorder; import io.quarkus.vertx.http.runtime.devmode.Json; import io.vertx.core.Handler; @@ -61,7 +60,6 @@ public void setupTestRoutes( .route("dev/test") .handler(recorder.continousTestHandler(shutdownContextBuildItem)) .build()); - testListenerBuildItemBuildProducer.produce(new TestListenerBuildItem(new ContinuousTestingWebSocketTestListener())); } } diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/ContinuousTestingTestUtils.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/ContinuousTestingTestUtils.java deleted file mode 100644 index 5ae3209c3dfff3..00000000000000 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/ContinuousTestingTestUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.quarkus.vertx.http.testrunner; - -import java.util.Arrays; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import org.awaitility.Awaitility; -import org.awaitility.core.ConditionTimeoutException; - -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.restassured.RestAssured; - -/** - * Utilities for testing the test runner itself - */ -public class ContinuousTestingTestUtils { - - long runtToWaitFor = 1; - - public TestStatus waitForNextCompletion() { - try { - Awaitility.waitAtMost(1, TimeUnit.MINUTES).pollInterval(50, TimeUnit.MILLISECONDS).until(new Callable() { - @Override - public Boolean call() throws Exception { - TestStatus ts = RestAssured.get("q/dev/io.quarkus.quarkus-vertx-http/tests/status").as(TestStatus.class); - if (ts.getLastRun() > runtToWaitFor) { - throw new RuntimeException( - "Waiting for run " + runtToWaitFor + " but run " + ts.getLastRun() + " has already occurred"); - } - boolean runComplete = ts.getLastRun() == runtToWaitFor; - if (runComplete && ts.getRunning() > 0) { - //there is a small chance of a race, where changes are picked up twice, due to how filesystems work - //this works around it by waiting for the next run - runtToWaitFor = ts.getRunning(); - return false; - } else if (runComplete) { - runtToWaitFor++; - } - return runComplete; - } - }); - } catch (Exception e) { - TestStatus ts = RestAssured.get("q/dev/io.quarkus.quarkus-vertx-http/tests/status").as(TestStatus.class); - throw new ConditionTimeoutException("Failed to wait for test run" + runtToWaitFor + " " + ts); - } - return RestAssured.get("q/dev/io.quarkus.quarkus-vertx-http/tests/status").as(TestStatus.class); - } - - public static String appProperties(String... props) { - return "quarkus.test.continuous-testing=enabled\nquarkus.test.display-test-output=true\nquarkus.test.basic-console=true\nquarkus.test.disable-console-input=true\n" - + String.join("\n", Arrays.asList(props)); - } -} diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/QuarkusTestTypeTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/QuarkusTestTypeTestCase.java index 344e0412ba79be..b1fe0916dc5853 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/QuarkusTestTypeTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/QuarkusTestTypeTestCase.java @@ -9,8 +9,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; public class QuarkusTestTypeTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestChangeTrackingWhenStartFailsTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestChangeTrackingWhenStartFailsTestCase.java index fd7735b789b0bc..bb9228f8a6c948 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestChangeTrackingWhenStartFailsTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestChangeTrackingWhenStartFailsTestCase.java @@ -10,8 +10,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; public class TestChangeTrackingWhenStartFailsTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestFailingBeforeAllTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestFailingBeforeAllTestCase.java index f5c74a428b92a4..ce3e1e9f0423b9 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestFailingBeforeAllTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestFailingBeforeAllTestCase.java @@ -9,8 +9,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; public class TestFailingBeforeAllTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestRunnerSmokeTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestRunnerSmokeTestCase.java index 423c9fe8fceb09..f09ec12148800a 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestRunnerSmokeTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/TestRunnerSmokeTestCase.java @@ -10,10 +10,11 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; import io.quarkus.vertx.http.deployment.devmode.tests.ClassResult; import io.quarkus.vertx.http.deployment.devmode.tests.SuiteResult; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; import io.restassured.RestAssured; public class TestRunnerSmokeTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/UnitTestTypeTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/UnitTestTypeTestCase.java index bf2d3946d51f7e..db944cd60ccaae 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/UnitTestTypeTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/UnitTestTypeTestCase.java @@ -9,8 +9,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; public class UnitTestTypeTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/brokenonly/TestBrokenOnlyTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/brokenonly/TestBrokenOnlyTestCase.java index e6e48c30f25440..411a751b0a6e6b 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/brokenonly/TestBrokenOnlyTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/brokenonly/TestBrokenOnlyTestCase.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; import io.restassured.RestAssured; public class TestBrokenOnlyTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/includes/ExcludePatternTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/includes/ExcludePatternTestCase.java index cb9aa737d133e3..aa5b9f6d033286 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/includes/ExcludePatternTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/includes/ExcludePatternTestCase.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; import io.quarkus.vertx.http.testrunner.HelloResource; public class ExcludePatternTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/includes/IncludePatternTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/includes/IncludePatternTestCase.java index 715800e8597a8b..69bcf14d1e857c 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/includes/IncludePatternTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/includes/IncludePatternTestCase.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; import io.quarkus.vertx.http.testrunner.HelloResource; public class IncludePatternTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/params/TestParameterizedTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/params/TestParameterizedTestCase.java index f90ef041fdbd47..57c14bc6694f16 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/params/TestParameterizedTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/params/TestParameterizedTestCase.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; public class TestParameterizedTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/tags/ExcludeTagsTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/tags/ExcludeTagsTestCase.java index b263e5890688f1..f3bdea49bd1ae1 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/tags/ExcludeTagsTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/tags/ExcludeTagsTestCase.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; import io.quarkus.vertx.http.testrunner.HelloResource; public class ExcludeTagsTestCase { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/tags/IncludeTagsTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/tags/IncludeTagsTestCase.java index 15b12b553e4968..8d136c596def60 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/tags/IncludeTagsTestCase.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/testrunner/tags/IncludeTagsTestCase.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.ContinuousTestingTestUtils; +import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; -import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus; -import io.quarkus.vertx.http.testrunner.ContinuousTestingTestUtils; import io.quarkus.vertx.http.testrunner.HelloResource; public class IncludeTagsTestCase { diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ContinuousTestWebSocketHandler.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ContinuousTestWebSocketHandler.java index 683ddf8eafc937..34f37b59bf3f65 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ContinuousTestWebSocketHandler.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ContinuousTestWebSocketHandler.java @@ -8,21 +8,21 @@ import org.jboss.logging.Logger; import io.netty.handler.codec.http.HttpHeaderNames; -import io.quarkus.dev.testing.ContinuousTestingWebsocketListener; +import io.quarkus.dev.testing.ContinuousTestingSharedStateManager; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.http.ServerWebSocket; import io.vertx.ext.web.RoutingContext; public class ContinuousTestWebSocketHandler - implements Handler, Consumer { + implements Handler, Consumer { - private static final Logger log = Logger.getLogger(ContinuousTestingWebsocketListener.class); + private static final Logger log = Logger.getLogger(ContinuousTestingSharedStateManager.class); private static final Set sockets = Collections.newSetFromMap(new ConcurrentHashMap<>()); private static volatile String lastMessage; @Override - public void accept(ContinuousTestingWebsocketListener.State state) { + public void accept(ContinuousTestingSharedStateManager.State state) { Json.JsonObjectBuilder response = Json.object(); response.put("running", state.running); response.put("inProgress", state.inProgress); diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleRecorder.java index ec5126f6b59098..8b2e5cb4ee7558 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/DevConsoleRecorder.java @@ -15,7 +15,7 @@ import org.jboss.logging.Logger; import io.quarkus.dev.console.DevConsoleManager; -import io.quarkus.dev.testing.ContinuousTestingWebsocketListener; +import io.quarkus.dev.testing.ContinuousTestingSharedStateManager; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; import io.vertx.core.Handler; @@ -48,11 +48,12 @@ public Handler devConsoleHandler(String devConsoleFinalDestinati public Handler continousTestHandler(ShutdownContext context) { ContinuousTestWebSocketHandler handler = new ContinuousTestWebSocketHandler(); - ContinuousTestingWebsocketListener.setStateListener(handler); + ContinuousTestingSharedStateManager.addStateListener(handler); context.addShutdownTask(new Runnable() { @Override public void run() { - ContinuousTestingWebsocketListener.setStateListener(null); + ContinuousTestingSharedStateManager.removeStateListener(handler); + ContinuousTestingSharedStateManager.reset(); } }); diff --git a/test-framework/junit5-internal/pom.xml b/test-framework/junit5-internal/pom.xml index 85c157b718a990..5e3194031627f8 100644 --- a/test-framework/junit5-internal/pom.xml +++ b/test-framework/junit5-internal/pom.xml @@ -55,6 +55,10 @@ io.quarkus quarkus-devtools-utilities + + org.awaitility + awaitility + diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/ContinuousTestingTestUtils.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/ContinuousTestingTestUtils.java new file mode 100644 index 00000000000000..3fa5c1c3389e88 --- /dev/null +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/ContinuousTestingTestUtils.java @@ -0,0 +1,185 @@ +package io.quarkus.test; + +import java.util.Arrays; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import org.awaitility.Awaitility; +import org.awaitility.core.ConditionTimeoutException; + +import io.quarkus.dev.testing.ContinuousTestingSharedStateManager; + +/** + * Utilities for testing the test runner itself + */ +public class ContinuousTestingTestUtils { + + long runToWaitFor = 1; + + public TestStatus waitForNextCompletion() { + try { + Awaitility.waitAtMost(1, TimeUnit.MINUTES).pollInterval(50, TimeUnit.MILLISECONDS).until(new Callable() { + @Override + public Boolean call() throws Exception { + ContinuousTestingSharedStateManager.State ts = ContinuousTestingSharedStateManager.getLastState(); + if (ts.lastRun > runToWaitFor) { + throw new RuntimeException( + "Waiting for run " + runToWaitFor + " but run " + ts.lastRun + " has already occurred"); + } + boolean runComplete = ts.lastRun == runToWaitFor; + if (runComplete && ts.inProgress) { + //there is a small chance of a race, where changes are picked up twice, due to how filesystems work + //this works around it by waiting for the next run + runToWaitFor = ts.lastRun; + return false; + } else if (runComplete) { + runToWaitFor++; + } + return runComplete; + } + }); + } catch (Exception e) { + ContinuousTestingSharedStateManager.State ts = ContinuousTestingSharedStateManager.getLastState(); + throw new ConditionTimeoutException("Failed to wait for test run " + runToWaitFor + " " + ts); + } + var s = ContinuousTestingSharedStateManager.getLastState(); + return new TestStatus(s.lastRun, s.running ? s.lastRun + 1 : -1, s.run, s.currentPassed, s.currentFailed, + s.currentSkipped, s.passed, s.failed, s.skipped); + } + + public static String appProperties(String... props) { + return "quarkus.test.continuous-testing=enabled\nquarkus.test.display-test-output=true\nquarkus.test.basic-console=true\nquarkus.test.disable-console-input=true\n" + + String.join("\n", Arrays.asList(props)); + } + + public static class TestStatus { + + private long lastRun; + + private long running; + + private long testsRun = -1; + + private long testsPassed = -1; + + private long testsFailed = -1; + + private long testsSkipped = -1; + + private long totalTestsPassed = -1; + + private long totalTestsFailed = -1; + + private long totalTestsSkipped = -1; + + public TestStatus() { + } + + public TestStatus(long lastRun, long running, long testsRun, long testsPassed, long testsFailed, long testsSkipped, + long totalTestsPassed, long totalTestsFailed, long totalTestsSkipped) { + this.lastRun = lastRun; + this.running = running; + this.testsRun = testsRun; + this.testsPassed = testsPassed; + this.testsFailed = testsFailed; + this.testsSkipped = testsSkipped; + this.totalTestsPassed = totalTestsPassed; + this.totalTestsFailed = totalTestsFailed; + this.totalTestsSkipped = totalTestsSkipped; + + } + + public long getLastRun() { + return lastRun; + } + + public TestStatus setLastRun(long lastRun) { + this.lastRun = lastRun; + return this; + } + + public long getRunning() { + return running; + } + + public TestStatus setRunning(long running) { + this.running = running; + return this; + } + + public long getTestsRun() { + return testsRun; + } + + public TestStatus setTestsRun(long testsRun) { + this.testsRun = testsRun; + return this; + } + + public long getTestsPassed() { + return testsPassed; + } + + public TestStatus setTestsPassed(long testsPassed) { + this.testsPassed = testsPassed; + return this; + } + + public long getTestsFailed() { + return testsFailed; + } + + public TestStatus setTestsFailed(long testsFailed) { + this.testsFailed = testsFailed; + return this; + } + + public long getTestsSkipped() { + return testsSkipped; + } + + public TestStatus setTestsSkipped(long testsSkipped) { + this.testsSkipped = testsSkipped; + return this; + } + + public long getTotalTestsPassed() { + return totalTestsPassed; + } + + public TestStatus setTotalTestsPassed(long totalTestsPassed) { + this.totalTestsPassed = totalTestsPassed; + return this; + } + + public long getTotalTestsFailed() { + return totalTestsFailed; + } + + public TestStatus setTotalTestsFailed(long totalTestsFailed) { + this.totalTestsFailed = totalTestsFailed; + return this; + } + + public long getTotalTestsSkipped() { + return totalTestsSkipped; + } + + public TestStatus setTotalTestsSkipped(long totalTestsSkipped) { + this.totalTestsSkipped = totalTestsSkipped; + return this; + } + + @Override + public String toString() { + return "TestStatus{" + + "lastRun=" + lastRun + + ", running=" + running + + ", testsRun=" + testsRun + + ", testsPassed=" + testsPassed + + ", testsFailed=" + testsFailed + + ", testsSkipped=" + testsSkipped + + '}'; + } + } +} From b1d3c9d961334bd88a61ee91eef59305d5ae3960 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Wed, 1 Sep 2021 08:22:36 +0200 Subject: [PATCH 043/138] Update the status of the mutiny, AMQP connector and gRPC extensions to "stable". --- .../runtime/src/main/resources/META-INF/quarkus-extension.yaml | 2 +- .../runtime/src/main/resources/META-INF/quarkus-extension.yaml | 2 +- .../runtime/src/main/resources/META-INF/quarkus-extension.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/grpc/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/grpc/runtime/src/main/resources/META-INF/quarkus-extension.yaml index ba8a04ffba04bc..2a2f8152e8fe75 100644 --- a/extensions/grpc/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/grpc/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -8,7 +8,7 @@ metadata: - "web" - "serialization" - "reactive" - status: "experimental" + status: "stable" codestart: name: "grpc" languages: diff --git a/extensions/mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 658ebabc08beaf..001d82655819ad 100644 --- a/extensions/mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/mutiny/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -10,4 +10,4 @@ metadata: - "Reactor" categories: - "reactive" - status: "preview" \ No newline at end of file + status: "stable" \ No newline at end of file diff --git a/extensions/smallrye-reactive-messaging-amqp/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/smallrye-reactive-messaging-amqp/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 8558a6b2a48770..977c0d3ae2981b 100644 --- a/extensions/smallrye-reactive-messaging-amqp/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/smallrye-reactive-messaging-amqp/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -9,7 +9,7 @@ metadata: guide: "https://quarkus.io/guides/amqp" categories: - "messaging" - status: "preview" + status: "stable" config: - "mp.messaging." - "quarkus.reactive-messaging." From e9e3d038646fb74395fd276e9d617c224dddbcdc Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Wed, 1 Sep 2021 08:51:49 +0200 Subject: [PATCH 044/138] Dev UI Config screen render config editor based on type (Fix #19636) Signed-off-by:Phillip Kruger --- .../builditem/ConfigDescriptionBuildItem.java | 21 ++- .../steps/ConfigDescriptionBuildStep.java | 141 +++++++++++++++++- .../console/ConfigEditorProcessor.java | 14 +- .../io.quarkus.quarkus-vertx-http/config.html | 67 ++++++++- .../runtime/devmode/ConfigDescription.java | 24 ++- 5 files changed, 253 insertions(+), 14 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java index 4f50e314502380..b71457c4730528 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java @@ -1,5 +1,7 @@ package io.quarkus.deployment.builditem; +import java.util.List; + import io.quarkus.builder.item.MultiBuildItem; /** @@ -12,12 +14,17 @@ public final class ConfigDescriptionBuildItem extends MultiBuildItem implements private final Class type; private final String defaultValue; private final String docs; + private final String valueTypeName; + private final List allowedValues; - public ConfigDescriptionBuildItem(String propertyName, Class type, String defaultValue, String docs) { + public ConfigDescriptionBuildItem(String propertyName, Class type, String defaultValue, String docs, + String valueTypeName, List allowedValues) { this.propertyName = propertyName; this.type = type; this.defaultValue = defaultValue; this.docs = docs; + this.valueTypeName = valueTypeName; + this.allowedValues = allowedValues; } public String getPropertyName() { @@ -36,6 +43,18 @@ public String getDocs() { return docs; } + public String getValueTypeName() { + return valueTypeName; + } + + public List getAllowedValues() { + return allowedValues; + } + + public boolean hasAllowedValues() { + return allowedValues != null && !allowedValues.isEmpty(); + } + @Override public int compareTo(ConfigDescriptionBuildItem o) { return propertyName.compareTo(o.propertyName); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java index 5cd2005e2b343f..2dea9889d707a3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java @@ -3,10 +3,17 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.Field; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.Properties; +import java.util.Set; import java.util.function.Consumer; +import java.util.logging.Level; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -50,6 +57,9 @@ public void accept(Container node) { final ConfigProperty configProperty = field.getAnnotation(ConfigProperty.class); String defaultDefault; final Class valueClass = field.getType(); + + EffectiveConfigTypeAndValues effectiveConfigTypeAndValues = getTypeName(field); + if (valueClass == boolean.class) { defaultDefault = "false"; } else if (valueClass.isPrimitive() && valueClass != char.class) { @@ -72,9 +82,138 @@ public void accept(Container node) { String javadocKey = field.getDeclaringClass().getName().replace('$', '.') + '.' + field.getName(); ret.add(new ConfigDescriptionBuildItem("quarkus." + node.getPropertyName(), node.findEnclosingClass().getConfigurationClass(), - defVal, javadoc.getProperty(javadocKey))); + defVal, + javadoc.getProperty(javadocKey), + effectiveConfigTypeAndValues.getTypeName(), + effectiveConfigTypeAndValues.getAllowedValues())); } }); } + private EffectiveConfigTypeAndValues getTypeName(Field field) { + final Class valueClass = field.getType(); + return getTypeName(field, valueClass); + } + + private EffectiveConfigTypeAndValues getTypeName(Field field, Class valueClass) { + EffectiveConfigTypeAndValues typeAndValues = new EffectiveConfigTypeAndValues(); + String name = valueClass.getName(); + + // Extract Optionals, Lists and Sets + if ((valueClass.equals(Optional.class) || valueClass.equals(List.class) || valueClass.equals(Set.class))) { + + if (field != null) { + Type genericType = field.getGenericType(); + name = genericType.getTypeName(); + } + + if (name.contains("<") && name.contains(">")) { + name = name.substring(name.lastIndexOf("<") + 1, name.indexOf(">")); + } + + try { + Class c = Class.forName(name); + return getTypeName(null, c); + } catch (ClassNotFoundException ex) { + // Then we use the name as is. + } + } + + // Check other optionals + if (valueClass.equals(OptionalInt.class)) { + name = Integer.class.getName(); + } else if (valueClass.equals(OptionalDouble.class)) { + name = Double.class.getName(); + } else if (valueClass.equals(OptionalLong.class)) { + name = Long.class.getName(); + } + + // Check if this is an enum + if (Enum.class.isAssignableFrom(valueClass)) { + name = Enum.class.getName(); + + Object[] values = valueClass.getEnumConstants(); + for (Object v : values) { + Enum casted = (Enum) valueClass.cast(v); + typeAndValues.addAllowedValue(casted.name()); + } + } + + // Special case for Log level + if (valueClass.isAssignableFrom(Level.class)) { + typeAndValues.addAllowedValue(Level.ALL.getName()); + typeAndValues.addAllowedValue(Level.CONFIG.getName()); + typeAndValues.addAllowedValue(Level.FINE.getName()); + typeAndValues.addAllowedValue(Level.FINER.getName()); + typeAndValues.addAllowedValue(Level.FINEST.getName()); + typeAndValues.addAllowedValue(Level.INFO.getName()); + typeAndValues.addAllowedValue(Level.OFF.getName()); + typeAndValues.addAllowedValue(Level.SEVERE.getName()); + typeAndValues.addAllowedValue(Level.WARNING.getName()); + } + + // Map all primatives + if (name.equals("int")) { + name = Integer.class.getName(); + } else if (name.equals("boolean")) { + name = Boolean.class.getName(); + } else if (name.equals("float")) { + name = Float.class.getName(); + } else if (name.equals("double")) { + name = Double.class.getName(); + } else if (name.equals("long")) { + name = Long.class.getName(); + } else if (name.equals("byte")) { + name = Byte.class.getName(); + } else if (name.equals("short")) { + name = Short.class.getName(); + } else if (name.equals("char")) { + name = Character.class.getName(); + } + + typeAndValues.setTypeName(name); + return typeAndValues; + } + + class EffectiveConfigTypeAndValues { + private String typeName; + private List allowedValues; + + public EffectiveConfigTypeAndValues() { + + } + + public EffectiveConfigTypeAndValues(String typeName) { + this.typeName = typeName; + } + + public EffectiveConfigTypeAndValues(String typeName, List allowedValues) { + this.typeName = typeName; + this.allowedValues = allowedValues; + } + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public List getAllowedValues() { + return allowedValues; + } + + public void setAllowedValues(List allowedValues) { + this.allowedValues = allowedValues; + } + + public void addAllowedValue(String v) { + if (allowedValues == null) { + allowedValues = new ArrayList<>(); + } + allowedValues.add(v); + } + } + } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java index 1d963b996fd4d1..5f814b22c2eab8 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java @@ -45,14 +45,18 @@ public void config(BuildProducer devCons Optional devServicesLauncherConfig) { List configDescriptions = new ArrayList<>(); for (ConfigDescriptionBuildItem item : configDescriptionBuildItems) { - configDescriptions.add( - new ConfigDescription(item.getPropertyName(), item.getDocs(), item.getDefaultValue(), - isSetByDevServices(devServicesLauncherConfig, item.getPropertyName()))); + new ConfigDescription(item.getPropertyName(), + item.getDocs(), + item.getDefaultValue(), + isSetByDevServices(devServicesLauncherConfig, item.getPropertyName()), + item.getValueTypeName(), + item.getAllowedValues())); } - devConsoleRuntimeTemplateProducer.produce( - new DevConsoleRuntimeTemplateInfoBuildItem("config", new ConfigDescriptionsSupplier(configDescriptions))); + devConsoleRuntimeTemplateProducer.produce(new DevConsoleRuntimeTemplateInfoBuildItem("config", + new ConfigDescriptionsSupplier(configDescriptions))); + devConsoleRuntimeTemplateProducer.produce(new DevConsoleRuntimeTemplateInfoBuildItem("hasDevServices", new HasDevServicesSupplier(devServicesLauncherConfig.isPresent() && devServicesLauncherConfig.get().getConfig() != null diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html index 8b7601c90d7cdd..ca41f9ad994ad1 100644 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html +++ b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html @@ -69,11 +69,32 @@ hideEmptyTables(); } -function changeInputValue(name){ +function changeSelectValue(element, name){ + var $el = $("select[id='" + name + "']"); + var $tr = $("tr[id='tr-" + name + "']"); + var value = element.options[element.selectedIndex].text; + + postChangeConfigValue(name, value, $el); +} + +function changeCheckboxValue(element, name){ var $el = $("input[id='" + name + "']"); var $tr = $("tr[id='tr-" + name + "']"); + var value = element.checked; + postChangeConfigValue(name, value, $el); +} + +function changeInputValue(name){ + var $el = $("input[id='" + name + "']"); + var $tr = $("tr[id='tr-" + name + "']"); var value = $el.val(); + + postChangeConfigValue(name, value, $el); +} + +function postChangeConfigValue(name, value, $el){ + $el.prop('disabled', true); $.post("", { action: "updateProperty", @@ -90,6 +111,7 @@ hideEmptyTables(); changeBackgroundColor("#ff6366", $el); } + $el.prop('disabled', false); }); } @@ -386,12 +408,45 @@ {#if configsource.key.editable} -

    - -
    - + + {#if item.typeName && item.typeName == "java.lang.Boolean"} +
    +
    + + +
    +
    + {#else if item.typeName && (item.typeName == "java.lang.Integer" || item.typeName == "java.lang.Long")} +
    + +
    + +
    +
    + {#else if item.typeName && (item.typeName == "java.lang.Float" || item.typeName == "java.lang.Double")} +
    + +
    + +
    +
    + {#else if item.typeName && (item.typeName == "java.lang.Enum" || item.typeName == "java.util.logging.Level")} +
    +
    -
    + {#else} +
    + +
    + +
    +
    + {/if} {#else} {item.configValue.value} {/if} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ConfigDescription.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ConfigDescription.java index d1baf58b0d3f0b..c0e76b33079870 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ConfigDescription.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ConfigDescription.java @@ -1,5 +1,7 @@ package io.quarkus.vertx.http.runtime.devmode; +import java.util.List; + import io.smallrye.config.ConfigValue; public class ConfigDescription implements Comparable { @@ -8,16 +10,20 @@ public class ConfigDescription implements Comparable { private String defaultValue; private ConfigValue configValue; private boolean autoFromDevServices = false; + private String typeName; + private List allowedValues; public ConfigDescription() { } public ConfigDescription(final String name, final String description, final String defaultValue, - final boolean autoFromDevServices) { + final boolean autoFromDevServices, String typeName, List allowedValues) { this.name = name; this.description = description; this.defaultValue = defaultValue; this.autoFromDevServices = autoFromDevServices; + this.typeName = typeName; + this.allowedValues = allowedValues; } public ConfigDescription( @@ -71,6 +77,22 @@ public void setAutoFromDevServices(boolean autoFromDevServices) { this.autoFromDevServices = autoFromDevServices; } + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public List getAllowedValues() { + return allowedValues; + } + + public void setAllowedValues(List allowedValues) { + this.allowedValues = allowedValues; + } + @Override public int compareTo(ConfigDescription o) { int ordinal = Integer.compare(o.configValue.getConfigSourceOrdinal(), this.configValue.getConfigSourceOrdinal()); From a77eadb930cb39d3e07a0d009e6a04ba2667246d Mon Sep 17 00:00:00 2001 From: Guillaume Le Floch Date: Tue, 31 Aug 2021 09:18:32 +0200 Subject: [PATCH 045/138] Set default jvm-target in kotlin sample project --- .../multi-module-kotlin-project/web/build.gradle.kts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integration-tests/gradle/src/main/resources/multi-module-kotlin-project/web/build.gradle.kts b/integration-tests/gradle/src/main/resources/multi-module-kotlin-project/web/build.gradle.kts index dd4627b9f7240f..55afb0a79cdbbd 100644 --- a/integration-tests/gradle/src/main/resources/multi-module-kotlin-project/web/build.gradle.kts +++ b/integration-tests/gradle/src/main/resources/multi-module-kotlin-project/web/build.gradle.kts @@ -3,6 +3,12 @@ plugins { kotlin("jvm") } +// This is a fix as kotlin 1.5.30 does not support Java 17 yet +if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) { + tasks.quarkusDev { + compilerArgs = listOf("-jvm-target", "16") + } +} dependencies { implementation(project(":port")) implementation(project(":domain")) From 03647998696e6a7e202b7e3a07c0ecb2883b5f1e Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 1 Sep 2021 18:02:27 +1000 Subject: [PATCH 046/138] Make sure KC Dev UI page persists after reload --- .../devservices/keycloak/KeycloakDevServicesProcessor.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java index 1a0658f65fe06e..030b6bf880e97e 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java @@ -93,6 +93,7 @@ public class KeycloakDevServicesProcessor { private static volatile String capturedKeycloakUrl; private static volatile FileTime capturedRealmFileLastModifiedDate; private final IsDockerWorking isDockerWorking = new IsDockerWorking(true); + private static volatile KeycloakDevServicesConfigBuildItem existingDevServiceConfig; @BuildStep(onlyIfNot = IsNormal.class, onlyIf = { IsEnabled.class, GlobalDevServicesConfig.Enabled.class }) public KeycloakDevServicesConfigBuildItem startKeycloakContainer( @@ -121,7 +122,7 @@ public KeycloakDevServicesConfigBuildItem startKeycloakContainer( } } if (!restartRequired) { - return null; + return existingDevServiceConfig; } for (Closeable closeable : closeables) { try { @@ -133,6 +134,7 @@ public KeycloakDevServicesConfigBuildItem startKeycloakContainer( closeables = null; capturedDevServicesConfiguration = null; capturedKeycloakUrl = null; + existingDevServiceConfig = null; } capturedDevServicesConfiguration = currentDevServicesConfiguration; @@ -212,7 +214,8 @@ private KeycloakDevServicesConfigBuildItem prepareConfiguration(boolean createRe configProperties.put(CLIENT_SECRET_CONFIG_KEY, oidcClientSecret); configProperties.put(OIDC_USERS, users); - return new KeycloakDevServicesConfigBuildItem(configProperties); + existingDevServiceConfig = new KeycloakDevServicesConfigBuildItem(configProperties); + return existingDevServiceConfig; } private StartResult startContainer(boolean useSharedContainer) { From 07db37f530aa9450874f2e96b31505ab3e39e622 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 1 Sep 2021 11:32:03 +0300 Subject: [PATCH 047/138] Fix @NoCache without fields handling in RESTEasy Reactive Fixes: #19822 --- .../test/cache/NoCacheOnMethodsTest.java | 40 +++++++++++++------ .../scanning/CacheControlScanner.java | 10 ++--- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/cache/NoCacheOnMethodsTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/cache/NoCacheOnMethodsTest.java index 00662d7c38287d..1925a3855d8522 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/cache/NoCacheOnMethodsTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/cache/NoCacheOnMethodsTest.java @@ -30,37 +30,53 @@ public JavaArchive get() { }); @Test - public void testWith() { - RestAssured.get("/test/with") + public void testWithFields() { + RestAssured.get("/test/withFields") .then() .statusCode(200) - .body(equalTo("with")) + .body(equalTo("withFields")) .header("Cache-Control", "no-cache=\"f1\", no-cache=\"f2\""); } @Test - public void testWithout() { - RestAssured.get("/test/without") + public void testWithoutFields() { + RestAssured.get("/test/withoutFields") .then() .statusCode(200) - .body(equalTo("without")) + .body(equalTo("withoutFields")) + .header("Cache-Control", "no-cache"); + } + + @Test + public void testWithoutAnnotation() { + RestAssured.get("/test/withoutAnnotation") + .then() + .statusCode(200) + .body(equalTo("withoutAnnotation")) .header("Cache-Control", nullValue()); } @Path("test") public static class ResourceWithNoCache { - @Path("with") + @Path("withFields") @GET @NoCache(fields = { "f1", "f2" }) - public String with() { - return "with"; + public String withFields() { + return "withFields"; + } + + @Path("withoutFields") + @GET + @NoCache + public String withoutFields() { + return "withoutFields"; } - @Path("without") + @Path("withoutAnnotation") @GET - public String without() { - return "without"; + public String withoutAnnotation() { + return "withoutAnnotation"; } } } diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/CacheControlScanner.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/CacheControlScanner.java index 5b5ef467906394..6464523f41e220 100644 --- a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/CacheControlScanner.java +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/CacheControlScanner.java @@ -61,18 +61,18 @@ private ExtendedCacheControl noCacheToCacheControl(AnnotationInstance noCacheIns if (noCacheInstance == null) { return null; } + ExtendedCacheControl cacheControl = new ExtendedCacheControl(); + cacheControl.setNoCache(true); + cacheControl.setNoTransform(false); AnnotationValue fieldsValue = noCacheInstance.value("fields"); if (fieldsValue != null) { String[] fields = fieldsValue.asStringArray(); if ((fields != null) && (fields.length > 0)) { - ExtendedCacheControl cacheControl = new ExtendedCacheControl(); - cacheControl.setNoCache(true); - cacheControl.setNoTransform(false); cacheControl.getNoCacheFields().addAll(Arrays.asList(fields)); - return cacheControl; + } } - return null; + return cacheControl; } private ExtendedCacheControl cacheToCacheControl(AnnotationInstance cacheInstance) { From 12a882fd3a8ebbe136360d20511037336b585a6e Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 1 Sep 2021 11:57:07 +0300 Subject: [PATCH 048/138] Mark quarkus-container-image-openshift as stable --- .../runtime/src/main/resources/META-INF/quarkus-extension.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/container-image/container-image-openshift/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/container-image/container-image-openshift/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 5f6b78609781c9..9c1ee15c7c8bb0 100644 --- a/extensions/container-image/container-image-openshift/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/container-image/container-image-openshift/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -8,4 +8,4 @@ metadata: - "image" categories: - "cloud" - status: "preview" \ No newline at end of file + status: "stable" From dc51118ad3ee36d1e861e6aa283f76bf34c12eb4 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Wed, 1 Sep 2021 12:32:17 +0200 Subject: [PATCH 049/138] Dev UI Config screen show lock icon for Build Time properties (Fix #19755) Signed-off-by:Phillip Kruger --- .../builditem/ConfigDescriptionBuildItem.java | 9 ++++++++- .../steps/ConfigDescriptionBuildStep.java | 15 +++++++++------ .../devmode/console/ConfigEditorProcessor.java | 3 ++- .../io.quarkus.quarkus-vertx-http/config.html | 8 ++++++++ .../http/runtime/devmode/ConfigDescription.java | 12 +++++++++++- 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java index b71457c4730528..ad2059b87c604f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ConfigDescriptionBuildItem.java @@ -3,6 +3,7 @@ import java.util.List; import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.runtime.annotations.ConfigPhase; /** * A build item that is not part of the standard build, but is only used to generate @@ -16,15 +17,17 @@ public final class ConfigDescriptionBuildItem extends MultiBuildItem implements private final String docs; private final String valueTypeName; private final List allowedValues; + private final ConfigPhase configPhase; public ConfigDescriptionBuildItem(String propertyName, Class type, String defaultValue, String docs, - String valueTypeName, List allowedValues) { + String valueTypeName, List allowedValues, ConfigPhase configPhase) { this.propertyName = propertyName; this.type = type; this.defaultValue = defaultValue; this.docs = docs; this.valueTypeName = valueTypeName; this.allowedValues = allowedValues; + this.configPhase = configPhase; } public String getPropertyName() { @@ -51,6 +54,10 @@ public List getAllowedValues() { return allowedValues; } + public ConfigPhase getConfigPhase() { + return configPhase; + } + public boolean hasAllowedValues() { return allowedValues != null && !allowedValues.isEmpty(); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java index 2dea9889d707a3..a576fc64c7ad55 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigDescriptionBuildStep.java @@ -23,6 +23,7 @@ import io.quarkus.deployment.configuration.matching.ConfigPatternMap; import io.quarkus.deployment.configuration.matching.Container; import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.util.ClassPathUtils; public class ConfigDescriptionBuildStep { @@ -39,15 +40,16 @@ List createConfigDescriptions( } }); List ret = new ArrayList<>(); - processConfig(config.getReadResult().getBuildTimePatternMap(), ret, javadoc); - processConfig(config.getReadResult().getBuildTimeRunTimePatternMap(), ret, javadoc); - processConfig(config.getReadResult().getBootstrapPatternMap(), ret, javadoc); - processConfig(config.getReadResult().getRunTimePatternMap(), ret, javadoc); + processConfig(config.getReadResult().getBuildTimePatternMap(), ret, javadoc, ConfigPhase.BUILD_TIME); + processConfig(config.getReadResult().getBuildTimeRunTimePatternMap(), ret, javadoc, + ConfigPhase.BUILD_AND_RUN_TIME_FIXED); + processConfig(config.getReadResult().getBootstrapPatternMap(), ret, javadoc, ConfigPhase.BOOTSTRAP); + processConfig(config.getReadResult().getRunTimePatternMap(), ret, javadoc, ConfigPhase.RUN_TIME); return ret; } private void processConfig(ConfigPatternMap patterns, List ret, - Properties javadoc) { + Properties javadoc, ConfigPhase configPhase) { patterns.forEach(new Consumer() { @Override @@ -85,7 +87,8 @@ public void accept(Container node) { defVal, javadoc.getProperty(javadocKey), effectiveConfigTypeAndValues.getTypeName(), - effectiveConfigTypeAndValues.getAllowedValues())); + effectiveConfigTypeAndValues.getAllowedValues(), + configPhase)); } }); } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java index 5f814b22c2eab8..b1cfade99fa138 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java @@ -51,7 +51,8 @@ public void config(BuildProducer devCons item.getDefaultValue(), isSetByDevServices(devServicesLauncherConfig, item.getPropertyName()), item.getValueTypeName(), - item.getAllowedValues())); + item.getAllowedValues(), + item.getConfigPhase().name())); } devConsoleRuntimeTemplateProducer.produce(new DevConsoleRuntimeTemplateInfoBuildItem("config", diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html index ca41f9ad994ad1..5cba1ded572881 100644 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html +++ b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html @@ -401,10 +401,18 @@ + {#if item.configPhase?? && (item.configPhase == "BUILD_AND_RUN_TIME_FIXED" || item.configPhase == "BUILD_TIME")} + + {#else} + + {/if} + {item.configValue.name} + {#if item.autoFromDevServices} {/if} + {#if configsource.key.editable} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ConfigDescription.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ConfigDescription.java index c0e76b33079870..20f3aea287d10a 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ConfigDescription.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/ConfigDescription.java @@ -12,18 +12,20 @@ public class ConfigDescription implements Comparable { private boolean autoFromDevServices = false; private String typeName; private List allowedValues; + private String configPhase; public ConfigDescription() { } public ConfigDescription(final String name, final String description, final String defaultValue, - final boolean autoFromDevServices, String typeName, List allowedValues) { + final boolean autoFromDevServices, String typeName, List allowedValues, String configPhase) { this.name = name; this.description = description; this.defaultValue = defaultValue; this.autoFromDevServices = autoFromDevServices; this.typeName = typeName; this.allowedValues = allowedValues; + this.configPhase = configPhase; } public ConfigDescription( @@ -93,6 +95,14 @@ public void setAllowedValues(List allowedValues) { this.allowedValues = allowedValues; } + public String getConfigPhase() { + return configPhase; + } + + public void setConfigPhase(String configPhase) { + this.configPhase = configPhase; + } + @Override public int compareTo(ConfigDescription o) { int ordinal = Integer.compare(o.configValue.getConfigSourceOrdinal(), this.configValue.getConfigSourceOrdinal()); From 750495329e30681592a2bdc1739fac7c352d8a4e Mon Sep 17 00:00:00 2001 From: Matej Novotny Date: Fri, 27 Sep 2019 10:41:18 +0200 Subject: [PATCH 050/138] Arc - type-level and method-level inheritance tests --- .../MemberLevelInheritanceTest.java | 121 +++++++++++++++ .../inheritance/TypeLevelInheritanceTest.java | 138 ++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/inheritance/MemberLevelInheritanceTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/inheritance/TypeLevelInheritanceTest.java diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/inheritance/MemberLevelInheritanceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/inheritance/MemberLevelInheritanceTest.java new file mode 100644 index 00000000000000..1560a143aa3858 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/inheritance/MemberLevelInheritanceTest.java @@ -0,0 +1,121 @@ +package io.quarkus.arc.test.inheritance; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.ArcContainer; +import io.quarkus.arc.test.ArcTestContainer; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.annotation.Priority; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.Typed; +import javax.inject.Inject; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InterceptorBinding; +import javax.interceptor.InvocationContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class MemberLevelInheritanceTest { + + @RegisterExtension + ArcTestContainer testContainer = new ArcTestContainer(Foo.class, FooExtended.class, DummyBean.class, + MyBinding.class, MyInterceptor.class); + + @Test + public void testMemberLevelInheritance() { + ArcContainer container = Arc.container(); + // IP inheritance + FooExtended fooExtended = container.instance(FooExtended.class).get(); + assertNotNull(fooExtended); + assertNotNull(fooExtended.getBar()); + + // producer inheritance is tested simply by not getting ambiguous dependency while running the test + + // initializer inheritance + assertNotNull(fooExtended.getBeanFromInitMethod()); + + // interceptor binding on a method inheritance + assertEquals(0, MyInterceptor.timesInvoked); + fooExtended.interceptedMethod(); + assertEquals(1, MyInterceptor.timesInvoked); + } + + @ApplicationScoped + static class Foo { + @Inject + DummyBean bar; + + public DummyBean getBar() { + return bar; + } + + @Produces + String string = "42"; + + @Produces + public Integer answerToLifeUniverseAndAll() { + return 42; + } + + DummyBean beanFromInitMethod = null; + + public DummyBean getBeanFromInitMethod() { + return beanFromInitMethod; + } + + @Inject + public void initializerMethod(DummyBean bean) { + System.err.println("Initializer invoked!"); + this.beanFromInitMethod = bean; + } + + @MyBinding + public void interceptedMethod() { + } + } + + @ApplicationScoped + @Typed(FooExtended.class) + static class FooExtended extends Foo { + // does inherit injected field + // does not inherit producers + // does inherit initializer + // does inherit intercepted interceptedMethod + } + + @ApplicationScoped + static class DummyBean { + + } + + @Target({ TYPE, METHOD, FIELD, PARAMETER }) + @Retention(RUNTIME) + @Inherited + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Interceptor + @Priority(1) + static class MyInterceptor { + public static int timesInvoked = 0; + + @AroundInvoke + Object aroundInvoke(InvocationContext ctx) throws Exception { + timesInvoked++; + return ctx.proceed(); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/inheritance/TypeLevelInheritanceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/inheritance/TypeLevelInheritanceTest.java new file mode 100644 index 00000000000000..20865635ca6a7b --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/inheritance/TypeLevelInheritanceTest.java @@ -0,0 +1,138 @@ +package io.quarkus.arc.test.inheritance; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.ArcContainer; +import io.quarkus.arc.test.ArcTestContainer; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.annotation.Priority; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Stereotype; +import javax.enterprise.inject.Typed; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Qualifier; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InterceptorBinding; +import javax.interceptor.InvocationContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** + * Tests type inheritence, e.g. that scopes, stereotypes, bindings and qualifiers are inherited so long as they + * declare {@code @Inherited} + * + * Scope inheritance is tested separately, see ScopeInheritanceTest + */ +public class TypeLevelInheritanceTest { + + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().removeUnusedBeans(false) + .beanClasses(BasicBean.class, SubBean.class, FirstInterceptor.class, + SecondInterceptor.class, MyBinding.class, MyStereotype.class, SecondaryBinding.class, MyQualifier.class) + .build(); + + @Test + public void testTypeInheritance() { + ArcContainer container = Arc.container(); + + assertEquals(0, FirstInterceptor.timesInvoked); + assertEquals(0, SecondInterceptor.timesInvoked); + + BasicBean basicBean = container.instance(BasicBean.class, new MyQualifier.Literal()).get(); + assertNotNull(basicBean); + basicBean.ping(); + assertEquals(1, FirstInterceptor.timesInvoked); + assertEquals(1, SecondInterceptor.timesInvoked); + + SubBean subBean = container.instance(SubBean.class, new MyQualifier.Literal()).get(); + assertNotNull(subBean); + subBean.ping(); + assertEquals(2, FirstInterceptor.timesInvoked); + assertEquals(2, SecondInterceptor.timesInvoked); + } + + @ApplicationScoped + @MyBinding + @MyQualifier + @MyStereotype + static class BasicBean { + public void ping() { + + } + } + + @Typed(SubBean.class) + @ApplicationScoped + static class SubBean extends BasicBean { + + } + + @MyBinding + @Interceptor + @Priority(1) + static class FirstInterceptor { + public static int timesInvoked = 0; + + @AroundInvoke + Object aroundInvoke(InvocationContext ctx) throws Exception { + timesInvoked++; + return ctx.proceed(); + } + } + + @SecondaryBinding + @Interceptor + @Priority(1) + static class SecondInterceptor { + public static int timesInvoked = 0; + + @AroundInvoke + Object aroundInvoke(InvocationContext ctx) throws Exception { + timesInvoked++; + return ctx.proceed(); + } + } + + @Target({ TYPE, METHOD, FIELD, PARAMETER }) + @Retention(RUNTIME) + @Inherited + @InterceptorBinding + @interface MyBinding { + } + + @Stereotype + @Inherited + @SecondaryBinding + @Target({ TYPE, METHOD, FIELD, PARAMETER }) + @Retention(RUNTIME) + @interface MyStereotype { + } + + @Target({ TYPE, METHOD, FIELD, PARAMETER }) + @Retention(RUNTIME) + @Inherited + @InterceptorBinding + @interface SecondaryBinding { + } + + @Inherited + @Qualifier + @Target({ TYPE, METHOD, FIELD, PARAMETER }) + @Retention(RUNTIME) + @interface MyQualifier { + + static class Literal extends AnnotationLiteral implements MyQualifier { + + } + } +} From cdd280a30047631062fe45dd85ce2dc4f5a03b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Wed, 1 Sep 2021 11:56:12 +0200 Subject: [PATCH 051/138] Add missing @ConfigItem annotations --- .../LiquibaseMongodbBuildTimeConfig.java | 4 ++- .../runtime/LiquibaseMongodbConfig.java | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbBuildTimeConfig.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbBuildTimeConfig.java index 9bd5432a0f75e2..8945d1d07b549c 100644 --- a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbBuildTimeConfig.java +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbBuildTimeConfig.java @@ -1,5 +1,6 @@ package io.quarkus.liquibase.runtime; +import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -12,5 +13,6 @@ public class LiquibaseMongodbBuildTimeConfig { /** * The change log file */ - public String changeLog = "db/changeLog.xml"; + @ConfigItem(defaultValue = "db/changeLog.xml") + public String changeLog; } diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbConfig.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbConfig.java index 6cab1009c19d7d..0917e5204285e1 100644 --- a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbConfig.java +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/runtime/LiquibaseMongodbConfig.java @@ -1,9 +1,11 @@ package io.quarkus.liquibase.runtime; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -16,53 +18,67 @@ public class LiquibaseMongodbConfig { /** * The migrate at start flag */ - public boolean migrateAtStart = false; + @ConfigItem + public boolean migrateAtStart; /** * The validate on update flag */ - public boolean validateOnMigrate = true; + @ConfigItem(defaultValue = "true") + public boolean validateOnMigrate; /** * The clean at start flag */ - public boolean cleanAtStart = false; + @ConfigItem + public boolean cleanAtStart; - public Map changeLogParameters = null; + /** + * The parameters to be passed to the changelog. + * Defined as key value pairs. + */ + @ConfigItem + public Map changeLogParameters = new HashMap<>();; /** * The list of contexts */ - public Optional> contexts = null; + @ConfigItem + public Optional> contexts = Optional.empty(); /** * The list of labels */ - public Optional> labels = null; + @ConfigItem + public Optional> labels = Optional.empty(); /** * The default catalog name */ + @ConfigItem public Optional defaultCatalogName = Optional.empty(); /** * The default schema name */ + @ConfigItem public Optional defaultSchemaName = Optional.empty(); /** * The liquibase tables catalog name */ + @ConfigItem public Optional liquibaseCatalogName = Optional.empty(); /** * The liquibase tables schema name */ + @ConfigItem public Optional liquibaseSchemaName = Optional.empty(); /** * The liquibase tables tablespace name */ + @ConfigItem public Optional liquibaseTablespaceName = Optional.empty(); - } From c5b4dc30a2c017c796d9b0ec424aaa7599f0aa52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Szynkiewicz?= Date: Wed, 1 Sep 2021 14:44:59 +0200 Subject: [PATCH 052/138] Support config properties in @ClientHeaderParam fixes #19805 --- .../main/asciidoc/rest-client-reactive.adoc | 32 +++++-- .../MicroProfileRestClientEnricher.java | 13 ++- .../ClientHeaderParamFromPropertyTest.java | 84 +++++++++++++++++++ .../client/reactive/runtime/ConfigUtils.java | 41 +++++++++ 4 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/ClientHeaderParamFromPropertyTest.java create mode 100644 extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/ConfigUtils.java diff --git a/docs/src/main/asciidoc/rest-client-reactive.adoc b/docs/src/main/asciidoc/rest-client-reactive.adoc index 39b1c338f4e77e..9418f0285aa0e7 100644 --- a/docs/src/main/asciidoc/rest-client-reactive.adoc +++ b/docs/src/main/asciidoc/rest-client-reactive.adoc @@ -541,9 +541,13 @@ This difference comes from the laziness aspect of Mutiny and its subscription pr More details about this can be found in https://smallrye.io/smallrye-mutiny/#_uni_and_multi[the Mutiny documentation]. == Custom headers support -The MicroProfile REST client allows amending request headers by registering a `ClientHeadersFactory` with the `@RegisterClientHeaders` annotation. +There are a few ways in which you can specify custom headers for your REST calls: -Let's see it in action by adding a `@RegisterClientHeaders` annotation pointing to a `RequestUUIDHeaderFactory` class in our `CountriesService` REST interface: +- by registering a `ClientHeadersFactory` with the `@RegisterClientHeaders` annotation +- by specifying the value of the header with `@ClientHeaderParam` +- by specifying the value of the header by `@HeaderParam` + +The code below demonstrates how to use each of this techniques: [source, java] ---- @@ -560,12 +564,15 @@ import java.util.Set; @Path("/v2") @RegisterRestClient -@RegisterClientHeaders(RequestUUIDHeaderFactory.class) +@RegisterClientHeaders(RequestUUIDHeaderFactory.class) // <1> +@ClientHeaderParam(name = "my-header", value = "constant-header-value") // <2> +@ClientHeaderParam(name = "computed-header", value = "{org.acme.rest.client.Util.computeHeader}") // <3> public interface CountriesService { @GET @Path("/name/{name}") - Set getByName(@PathParam("name") String name); + @ClientHeaderParam(name = "header-from-properties", value = "${header.value}") // <4> + Set getByName(@PathParam("name") String name, @HeaderParam("jaxrs-style-header") String headerValue); // <5> @GET @Path("/name/{name}") @@ -573,7 +580,12 @@ public interface CountriesService { } ---- -And the `RequestUUIDHeaderFactory` would look like: +<1> There can be only one `ClientHeadersFactory` per class. With it, you can not only add custom headers, but you can also transform existing ones. See the `RequestUUIDHeaderFactory` class below for an example of the factory. +<2> `@ClientHeaderParam` can be used on the client interface and on methods. It can specify a constant header value... +<3> ... and a name of a method that should compute the value of the header. It can either be a static method or a default method in this interface +<4> ... as well as a value from your application's configuration + +A `ClientHeadersFactory` can look as follows: [source, java] ---- @@ -601,8 +613,16 @@ public class RequestUUIDHeaderFactory implements ClientHeadersFactory { As you see in the example above, you can make your `ClientHeadersFactory` implementation a CDI bean by annotating it with a scope-defining annotation, such as `@Singleton`, `@ApplicationScoped`, etc. +To specify a value for `${header.value}`, simply put the following in your `application.properties`: + +[source,properties] +---- +header.value=value of the header +---- + === Default header factory -You can also use `@RegisterClientHeaders` annotation without any custom factory specified. In that case the `DefaultClientHeadersFactoryImpl` factory will be used and all headers listed in `org.eclipse.microprofile.rest.client.propagateHeaders` configuration property will be amended. Individual header names are comma-separated. +The `@RegisterClientHeaders` annotation can also be used without any custom factory specified. In that case the `DefaultClientHeadersFactoryImpl` factory will be used. +If you make a REST client call from a REST resource, this fatory will propagate all the headers listed in `org.eclipse.microprofile.rest.client.propagateHeaders` configuration property from the resource request to the client request. Individual header names are comma-separated. [source, java] ---- diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java index f5e90dc889c70a..88ef8823de5b07 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/MicroProfileRestClientEnricher.java @@ -53,6 +53,7 @@ import io.quarkus.gizmo.TryBlock; import io.quarkus.jaxrs.client.reactive.deployment.JaxrsClientReactiveEnricher; import io.quarkus.rest.client.reactive.HeaderFiller; +import io.quarkus.rest.client.reactive.runtime.ConfigUtils; import io.quarkus.rest.client.reactive.runtime.MicroProfileRestClientRequestFilter; import io.quarkus.rest.client.reactive.runtime.NoOpHeaderFiller; import io.quarkus.runtime.util.HashUtil; @@ -321,9 +322,19 @@ private void addHeaderParam(MethodInfo declaringMethod, MethodCreator fillHeader .trueBranch(); if (values.length > 1 || !(values[0].startsWith("{") && values[0].endsWith("}"))) { + boolean required = annotation.valueWithDefault(index, "required").asBoolean(); ResultHandle headerList = fillHeaders.newInstance(MethodDescriptor.ofConstructor(ArrayList.class)); for (String value : values) { - fillHeaders.invokeInterfaceMethod(LIST_ADD_METHOD, headerList, fillHeaders.load(value)); + if (value.startsWith("${") && value.endsWith("}")) { + ResultHandle headerValueFromConfig = fillHeaders.invokeStaticMethod( + MethodDescriptor.ofMethod(ConfigUtils.class, "getConfigValue", String.class, String.class, + boolean.class), + fillHeaders.load(value), fillHeaders.load(required)); + fillHeaders.ifNotNull(headerValueFromConfig) + .trueBranch().invokeInterfaceMethod(LIST_ADD_METHOD, headerList, headerValueFromConfig); + } else { + fillHeaders.invokeInterfaceMethod(LIST_ADD_METHOD, headerList, fillHeaders.load(value)); + } } fillHeaders.invokeInterfaceMethod(MAP_PUT_METHOD, headerMap, fillHeaders.load(headerName), headerList); diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/ClientHeaderParamFromPropertyTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/ClientHeaderParamFromPropertyTest.java new file mode 100644 index 00000000000000..6a3035988b1078 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/ClientHeaderParamFromPropertyTest.java @@ -0,0 +1,84 @@ +package io.quarkus.rest.client.reactive.headers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URI; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; + +public class ClientHeaderParamFromPropertyTest { + private static final String HEADER_VALUE = "oifajrofijaeoir5gjaoasfaxcvcz"; + + @TestHTTPResource + URI baseUri; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClasses(Client.class) + .addAsResource( + new StringAsset("my.property-value=" + HEADER_VALUE), + "application.properties")); + + @Test + void shouldSetHeaderFromProperties() { + Client client = RestClientBuilder.newBuilder().baseUri(baseUri) + .build(Client.class); + + assertThat(client.getWithHeader()).isEqualTo(HEADER_VALUE); + } + + @Test + void shouldFailOnMissingRequiredHeaderProperty() { + Client client = RestClientBuilder.newBuilder().baseUri(baseUri) + .build(Client.class); + + assertThatThrownBy(client::missingRequiredProperty) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void shouldSucceedOnMissingNonRequiredHeaderProperty() { + Client client = RestClientBuilder.newBuilder().baseUri(baseUri) + .build(Client.class); + + assertThat(client.missingNonRequiredProperty()).isEqualTo(HEADER_VALUE); + } + + @Path("/") + @ApplicationScoped + public static class Resource { + @GET + public String returnHeaderValue(@HeaderParam("my-header") String header) { + return header; + } + } + + @ClientHeaderParam(name = "my-header", value = "${my.property-value}") + public interface Client { + @GET + String getWithHeader(); + + @GET + @ClientHeaderParam(name = "some-other-header", value = "${non-existent-property}") + String missingRequiredProperty(); + + @GET + @ClientHeaderParam(name = "some-other-header", value = "${non-existent-property}", required = false) + String missingNonRequiredProperty(); + } +} diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/ConfigUtils.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/ConfigUtils.java new file mode 100644 index 00000000000000..f1596ddfb713ff --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/ConfigUtils.java @@ -0,0 +1,41 @@ +package io.quarkus.rest.client.reactive.runtime; + +import java.util.NoSuchElementException; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.logging.Logger; + +public class ConfigUtils { + + private static final Logger log = Logger.getLogger(ConfigUtils.class); + + public static String getConfigValue(String configProperty, boolean required) { + String propertyName = stripPrefixAndSuffix(configProperty); + try { + return ConfigProvider.getConfig().getValue(propertyName, String.class); + } catch (NoSuchElementException e) { + String message = "Failed to find value for config property " + configProperty + + " in application configuration. Please provide the value for the property, e.g. by adding " + + propertyName + "= to your application.properties"; + if (required) { + throw new IllegalArgumentException(message, e); + } else { + log.warn(message); + return null; + } + } catch (IllegalArgumentException e) { + String message = "Failed to convert value for property " + configProperty + " to String"; + if (required) { + throw new IllegalArgumentException(message, e); + } else { + log.warn(message); + return null; + } + } + } + + private static String stripPrefixAndSuffix(String configProperty) { + // by now we know that configProperty is of form ${...} + return configProperty.substring(2, configProperty.length() - 1); + } +} From 6675997ad6e6c12a79ba7fe1080bea8963c86b57 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Wed, 1 Sep 2021 14:58:56 +0200 Subject: [PATCH 053/138] Dev UI Config screen copy-to-profile takes filter into account (Fix #19821) Signed-off-by:Phillip Kruger --- .../console/ConfigEditorProcessor.java | 115 +++++++++++------- .../io.quarkus.quarkus-vertx-http/config.html | 23 +++- 2 files changed, 89 insertions(+), 49 deletions(-) diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java index b1cfade99fa138..a793dc8ab3cd64 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/console/ConfigEditorProcessor.java @@ -7,6 +7,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -80,22 +81,13 @@ protected void handlePost(RoutingContext event, MultiMap form) throws Exception Map values = Collections.singletonMap(name, value); updateConfig(values); - } else if (action.equals("copyTestDevServices") && devServicesLauncherConfig.isPresent()) { + } else if (action.equals("copyDevServices") && devServicesLauncherConfig.isPresent()) { + String environment = event.request().getFormAttribute("environment"); + String filter = event.request().getParam("filterConfigKeys"); + List configFilter = getConfigFilter(filter); Map autoconfig = devServicesLauncherConfig.get().getConfig(); - autoconfig = autoconfig.entrySet().stream() - .collect(Collectors.toMap( - e -> appendProfile("test", e.getKey()), - Map.Entry::getValue)); - - updateConfig(autoconfig); - } else if (action.equals("copyProdDevServices") && devServicesLauncherConfig.isPresent()) { - Map autoconfig = devServicesLauncherConfig.get().getConfig(); - - autoconfig = autoconfig.entrySet().stream() - .collect(Collectors.toMap( - e -> appendProfile("prod", e.getKey()), - Map.Entry::getValue)); + autoconfig = filterAndApplyProfile(autoconfig, configFilter, environment.toLowerCase()); updateConfig(autoconfig); } else if (action.equals("updateProperties")) { @@ -111,6 +103,37 @@ protected void handlePost(RoutingContext event, MultiMap form) throws Exception })); } + private Map filterAndApplyProfile(Map autoconfig, List configFilter, + String profile) { + return autoconfig.entrySet().stream() + .filter((t) -> { + if (configFilter != null && !configFilter.isEmpty()) { + for (String sw : configFilter) { + if (t.getKey().startsWith(sw)) { + return true; + } + } + } else { + return true; + } + return false; + }) + .collect(Collectors.toMap( + e -> appendProfile(profile, e.getKey()), + Map.Entry::getValue)); + } + + private List getConfigFilter(String filter) { + if (filter != null && !filter.isEmpty()) { + if (filter.contains(",")) { + return Arrays.asList(filter.split(",")); + } else { + return List.of(filter); + } + } + return Collections.EMPTY_LIST; + } + private String appendProfile(String profile, String originalKey) { return String.format("%%%s.%s", profile, originalKey); } @@ -137,45 +160,47 @@ static byte[] getConfig() { } static void updateConfig(Map values) { - try { - Path configPath = getConfigPath(); - String profile = ProfileManager.getActiveProfile(); - List lines = Files.readAllLines(configPath); - for (Map.Entry entry : values.entrySet()) { - String name = entry.getKey(); - String value = entry.getValue(); - name = !profile.equals(DEVELOPMENT.getDefaultProfile()) ? "%" + profile + "." + name : name; - int nameLine = -1; - for (int i = 0, linesSize = lines.size(); i < linesSize; i++) { - final String line = lines.get(i); - if (line.startsWith(name + "=")) { - nameLine = i; - break; + if (values != null && !values.isEmpty()) { + try { + Path configPath = getConfigPath(); + String profile = ProfileManager.getActiveProfile(); + List lines = Files.readAllLines(configPath); + for (Map.Entry entry : values.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + name = !profile.equals(DEVELOPMENT.getDefaultProfile()) ? "%" + profile + "." + name : name; + int nameLine = -1; + for (int i = 0, linesSize = lines.size(); i < linesSize; i++) { + final String line = lines.get(i); + if (line.startsWith(name + "=")) { + nameLine = i; + break; + } } - } - if (nameLine != -1) { - if (value.isEmpty()) { - lines.remove(nameLine); + if (nameLine != -1) { + if (value.isEmpty()) { + lines.remove(nameLine); + } else { + lines.set(nameLine, name + "=" + value); + } } else { - lines.set(nameLine, name + "=" + value); - } - } else { - if (!value.isEmpty()) { - lines.add(name + "=" + value); + if (!value.isEmpty()) { + lines.add(name + "=" + value); + } } } - } - try (BufferedWriter writer = Files.newBufferedWriter(configPath)) { - for (String i : lines) { - writer.write(i); - writer.newLine(); + try (BufferedWriter writer = Files.newBufferedWriter(configPath)) { + for (String i : lines) { + writer.write(i); + writer.newLine(); + } } + preventKill(); + } catch (Throwable t) { + throw new RuntimeException(t); } - preventKill(); - } catch (Throwable t) { - throw new RuntimeException(t); } } diff --git a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html index 5cba1ded572881..43419922e74892 100644 --- a/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html +++ b/extensions/vertx-http/deployment/src/main/resources/dev-templates/io.quarkus.quarkus-vertx-http/config.html @@ -190,10 +190,11 @@ } function copyDevServices(environment){ - var action = "copy" + environment + "DevServices"; $.post("", { - action: action + action: "copyDevServices", + environment: environment, + filter: configfilter }, function(data, status){ if(status === "success"){ @@ -302,9 +303,22 @@ }); $('#configFilterModal').modal('hide'); + + showHideDevServicesButton(); + hideEmptyTables(); } +function showHideDevServicesButton(){ + // Check if there is any dev services visible on the page + var numberOfMagicConfig = $('.fa-magic:visible').length; + if(numberOfMagicConfig === 0){ + $('.devservices').hide(); + }else { + $('.devservices').show(); + } +} + function hideEmptyTables(){ $('.filterableTable').filter(function(index){ @@ -328,6 +342,7 @@ $(this).parent().show(); }); clearFilterInput(); + showHideDevServicesButton(); } {/script} @@ -358,7 +373,7 @@
    {#if info:hasDevServices} -
    +
    @@ -491,7 +506,7 @@
  • {#if info:hasDevServices} -