From f20b7163cce1d18c9c43a1ca3b2c34e6456f56c6 Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Wed, 7 Aug 2024 10:50:02 +1000 Subject: [PATCH 01/41] Dev UI: Upgraded Lit Signed-off-by: Phillip Kruger --- bom/dev-ui/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bom/dev-ui/pom.xml b/bom/dev-ui/pom.xml index 343d595627c0b4..c72eb8ec30d22c 100644 --- a/bom/dev-ui/pom.xml +++ b/bom/dev-ui/pom.xml @@ -14,10 +14,10 @@ 24.4.5 - 3.1.4 - 4.0.6 - 3.1.4 - 1.2.0 + 3.2.0 + 4.1.0 + 3.2.0 + 1.2.1 2.0.7 2.0.4 2.1.2 From 8f604033a330e7874a24241cd7e7aee768907873 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:47:04 +0000 Subject: [PATCH 02/41] Bump org.apache.commons:commons-compress from 1.26.2 to 1.27.0 Bumps org.apache.commons:commons-compress from 1.26.2 to 1.27.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- independent-projects/tools/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 02659ee82907e4..aa9b08bab9114d 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -189,7 +189,7 @@ 2.1 4.7.6 1.1.0 - 1.26.2 + 1.27.0 1.12.0 2.11.0 1.1.2.Final diff --git a/independent-projects/tools/pom.xml b/independent-projects/tools/pom.xml index dae422620e5f68..20bb9eaa1bb7a4 100644 --- a/independent-projects/tools/pom.xml +++ b/independent-projects/tools/pom.xml @@ -52,7 +52,7 @@ 2.17.2 4.1.0 5.10.3 - 1.26.2 + 1.27.0 3.6.0.Final 5.12.0 ${project.version} From 8752c9286fd3e1e8f69536cc70b0c338f6dd7be2 Mon Sep 17 00:00:00 2001 From: Marc Nuri Date: Fri, 9 Aug 2024 16:22:19 +0200 Subject: [PATCH 03/41] deps: Bump kubernetes-client-bom from 6.13.1 to 6.13.2 Signed-off-by: Marc Nuri --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f85e9d9cee4d8f..e4926f2b964868 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 0.8.12 - 6.13.1 + 6.13.2 5.5.0 6.5.2.Final 4.13.0 From 70ebfb9dfc5773e082528d3a82881e9c659f7777 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 12 Aug 2024 14:08:07 +0200 Subject: [PATCH 04/41] Allow using Maven 3.8.6 again for Quarkus apps You need a newer version to build Quarkus itself but for now we need to support older Maven versions for building Quarkus apps. Fixes #42478 --- build-parent/pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-parent/pom.xml b/build-parent/pom.xml index b92bf6f9f44a7f..d293daa9166441 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -54,7 +54,8 @@ - [${maven.min.version},) + + [3.8.6,) 3.9.8 From b401c5a3406eaa11b62d02d49c48b8f88a20c2b2 Mon Sep 17 00:00:00 2001 From: Thomas Canava Date: Mon, 12 Aug 2024 15:46:16 +0200 Subject: [PATCH 05/41] Make the boot jars in jib respect current timestamp --- .../image/jib/deployment/JibProcessor.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 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 3b93e633ec9463..9f7a2b4330bb4b 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 @@ -540,15 +540,20 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag FileEntriesLayer.Builder bootLibsLayerBuilder = FileEntriesLayer.builder().setName("fast-jar-boot-libs"); Path bootLibPath = componentsPath.resolve(JarResultBuildStep.LIB).resolve(JarResultBuildStep.BOOT_LIB); - try (Stream boolLibPaths = Files.list(bootLibPath)) { - boolLibPaths.forEach(lib -> { + try (Stream bootLibPaths = Files.list(bootLibPath)) { + bootLibPaths.forEach(lib -> { try { AbsoluteUnixPath libPathInContainer = workDirInContainer.resolve(JarResultBuildStep.LIB) .resolve(JarResultBuildStep.BOOT_LIB) .resolve(lib.getFileName()); - // the boot lib jars need to preserve the modification time because otherwise AppCDS won't work - bootLibsLayerBuilder.addEntry(lib, libPathInContainer, - Files.getLastModifiedTime(lib).toInstant()); + Instant bootLibModificationTime; + if (appCDSResult.isPresent()) { + // the boot lib jars need to preserve the modification time because otherwise AppCDS won't work + bootLibModificationTime = Files.getLastModifiedTime(lib).toInstant(); + } else { + bootLibModificationTime = modificationTime; + } + bootLibsLayerBuilder.addEntry(lib, libPathInContainer, bootLibModificationTime); } catch (IOException e) { throw new UncheckedIOException(e); } From 46c180d7fda493de661b6ab707d7b38e1811415c Mon Sep 17 00:00:00 2001 From: Zed Spencer-Milnes Date: Mon, 12 Aug 2024 17:26:31 +0100 Subject: [PATCH 06/41] Bump to Keycloak 25.0.2 Changelogs: Keycloak 25.0.1 - https://www.keycloak.org/2024/06/keycloak-2501-released Keycloak 25.0.2 - https://www.keycloak.org/2024/07/keycloak-2502-released --- bom/application/pom.xml | 2 +- build-parent/pom.xml | 2 +- docs/src/main/asciidoc/security-keycloak-authorization.adoc | 2 +- .../security-oidc-bearer-token-authentication-tutorial.adoc | 2 +- .../security-oidc-code-flow-authentication-tutorial.adoc | 2 +- docs/src/main/asciidoc/security-openid-connect-client.adoc | 2 +- .../src/main/asciidoc/security-openid-connect-dev-services.adoc | 2 +- .../src/main/asciidoc/security-openid-connect-multitenancy.adoc | 2 +- .../oidc/deployment/devservices/keycloak/DevServicesConfig.java | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 987e8fcfff8251..9895d6cec2c520 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -180,7 +180,7 @@ 5.12.0 5.8.0 2.1.0 - 25.0.0 + 25.0.2 1.15.1 3.46.0 2.29.2 diff --git a/build-parent/pom.xml b/build-parent/pom.xml index b92bf6f9f44a7f..4cc86edcbd1b3a 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -94,7 +94,7 @@ - 25.0.0 + 25.0.2 19.0.3 quay.io/keycloak/keycloak:${keycloak.version} quay.io/keycloak/keycloak:${keycloak.wildfly.version}-legacy diff --git a/docs/src/main/asciidoc/security-keycloak-authorization.adoc b/docs/src/main/asciidoc/security-keycloak-authorization.adoc index 11ef9df611b2e6..4c39f3fc6f18ae 100644 --- a/docs/src/main/asciidoc/security-keycloak-authorization.adoc +++ b/docs/src/main/asciidoc/security-keycloak-authorization.adoc @@ -226,7 +226,7 @@ To start a Keycloak server, use the following Docker command: docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8543:8443 -v "$(pwd)"/config/keycloak-keystore.jks:/etc/keycloak-keystore.jks quay.io/keycloak/keycloak:{keycloak.version} start --hostname-strict=false --https-key-store-file=/etc/keycloak-keystore.jks ---- -where `keycloak.version` must be `25.0.0` or later and the `keycloak-keystore.jks` can be found in https://github.com/quarkusio/quarkus-quickstarts/blob/main/security-keycloak-authorization-quickstart/config/keycloak-keystore.jks[quarkus-quickstarts/security-keycloak-authorization-quickstart/config]. +where `keycloak.version` must be `25.0.2` or later and the `keycloak-keystore.jks` can be found in https://github.com/quarkusio/quarkus-quickstarts/blob/main/security-keycloak-authorization-quickstart/config/keycloak-keystore.jks[quarkus-quickstarts/security-keycloak-authorization-quickstart/config]. Try to access your Keycloak server at https://localhost:8543[localhost:8543]. diff --git a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication-tutorial.adoc b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication-tutorial.adoc index 2cccadbdc69a89..3ab3219bf63f3d 100644 --- a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication-tutorial.adoc +++ b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication-tutorial.adoc @@ -217,7 +217,7 @@ For more information, see the <> sectio docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev ---- ==== -* Where the `keycloak.version` is set to version `25.0.0` or later. +* Where the `keycloak.version` is set to version `25.0.2` or later. . You can access your Keycloak server at http://localhost:8180[localhost:8180]. . To access the Keycloak Administration console, log in as the `admin` user by using the following login credentials: diff --git a/docs/src/main/asciidoc/security-oidc-code-flow-authentication-tutorial.adoc b/docs/src/main/asciidoc/security-oidc-code-flow-authentication-tutorial.adoc index 788913c7dc1979..d4e523cb6025d1 100644 --- a/docs/src/main/asciidoc/security-oidc-code-flow-authentication-tutorial.adoc +++ b/docs/src/main/asciidoc/security-oidc-code-flow-authentication-tutorial.adoc @@ -201,7 +201,7 @@ To start a Keycloak server, use Docker and run the following command: docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev ---- -where `keycloak.version` is set to `25.0.0` or later. +where `keycloak.version` is set to `25.0.2` or later. You can access your Keycloak Server at http://localhost:8180[localhost:8180]. diff --git a/docs/src/main/asciidoc/security-openid-connect-client.adoc b/docs/src/main/asciidoc/security-openid-connect-client.adoc index 0199d7bf1ab4bd..89420725660f80 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client.adoc @@ -507,7 +507,7 @@ To start a Keycloak Server, you can use Docker and just run the following comman docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev ---- -Set `{keycloak.version}` to `25.0.0` or later. +Set `{keycloak.version}` to `25.0.2` or later. You can access your Keycloak Server at http://localhost:8180[localhost:8180]. 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 b43fabd579b2e5..0b335a70fafa1f 100644 --- a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc @@ -258,7 +258,7 @@ For more information, see xref:security-oidc-bearer-token-authentication.adoc#be [[keycloak-initialization]] === Keycloak initialization -The `quay.io/keycloak/keycloak:25.0.0` image which contains a Keycloak distribution powered by Quarkus is used to start a container by default. +The `quay.io/keycloak/keycloak:25.0.2` image which contains a Keycloak distribution powered by Quarkus is used to start a container by default. `quarkus.keycloak.devservices.image-name` can be used to change the Keycloak image name. For example, set it to `quay.io/keycloak/keycloak:19.0.3-legacy` to use a Keycloak distribution powered by WildFly. Be aware that a Quarkus-based Keycloak distribution is only available starting from Keycloak `20.0.0`. diff --git a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc index ff8b0b86ff1614..63c2aa710c9c1a 100644 --- a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc @@ -352,7 +352,7 @@ To start a Keycloak server, you can use Docker and run the following command: docker run --name keycloak -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -p 8180:8080 quay.io/keycloak/keycloak:{keycloak.version} start-dev ---- -where `keycloak.version` is set to `25.0.0` or higher. +where `keycloak.version` is set to `25.0.2` or higher. Access your Keycloak server at http://localhost:8180[localhost:8180]. diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java index 9f71002fb2e7fd..f039fd0b8bdf9c 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java @@ -34,7 +34,7 @@ public class DevServicesConfig { * ends with `-legacy`. * Override with `quarkus.keycloak.devservices.keycloak-x-image`. */ - @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:25.0.0") + @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:25.0.2") public String imageName; /** From 09c015a5ebb45f335d3a596ed32632799f693722 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:50:50 +0000 Subject: [PATCH 07/41] Bump com.google.errorprone:error_prone_annotations from 2.29.2 to 2.30.0 Bumps [com.google.errorprone:error_prone_annotations](https://github.com/google/error-prone) from 2.29.2 to 2.30.0. - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.29.2...v2.30.0) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_annotations dependency-type: direct:production update-type: version-update:semver-minor ... 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 987e8fcfff8251..95f39a2e5865b5 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -183,7 +183,7 @@ 25.0.0 1.15.1 3.46.0 - 2.29.2 + 2.30.0 0.27.0 1.44.2 2.1 From 6394b665f0a42a0dac03c789c1d0a7a774825e0b Mon Sep 17 00:00:00 2001 From: frne Date: Mon, 12 Aug 2024 11:29:07 +0200 Subject: [PATCH 08/41] Fix SmallRye Health OpenAPI definitions The quarkus-smallrye-health extension exposes an openapi definition for the health endpoints it provides. This fixes the exposed schema to actually match the JSON structure returned by the endpoints. Additionally, the filter implementation (io.quarkus.smallrye.health.deployment.HealthOpenAPIFilter) has been refactored to use a more fluid and readable schema definition. --- .../deployment/HealthOpenAPIFilter.java | 291 +++++++----------- .../test/DeprecatedHealthOpenAPITest.java | 12 +- .../health/test/HealthOpenAPITest.java | 11 +- 3 files changed, 136 insertions(+), 178 deletions(-) diff --git a/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/HealthOpenAPIFilter.java b/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/HealthOpenAPIFilter.java index feb37ea27dc5fe..62d7fd52327431 100644 --- a/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/HealthOpenAPIFilter.java +++ b/extensions/smallrye-health/deployment/src/main/java/io/quarkus/smallrye/health/deployment/HealthOpenAPIFilter.java @@ -1,8 +1,6 @@ package io.quarkus.smallrye.health.deployment; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -12,9 +10,7 @@ import org.eclipse.microprofile.openapi.models.PathItem; import org.eclipse.microprofile.openapi.models.Paths; import org.eclipse.microprofile.openapi.models.media.Content; -import org.eclipse.microprofile.openapi.models.media.MediaType; import org.eclipse.microprofile.openapi.models.media.Schema; -import org.eclipse.microprofile.openapi.models.responses.APIResponse; import org.eclipse.microprofile.openapi.models.responses.APIResponses; import io.smallrye.openapi.api.models.ComponentsImpl; @@ -31,9 +27,42 @@ * Create OpenAPI entries (if configured) */ public class HealthOpenAPIFilter implements OASFilter { + private static final List MICROPROFILE_HEALTH_TAG = Collections.singletonList("MicroProfile Health"); - private static final String SCHEMA_HEALTH_RESPONSE = "HealthCheckResponse"; - private static final String SCHEMA_HEALTH_STATUS = "HealthCheckStatus"; + private static final String HEALTH_RESPONSE_SCHEMA_NAME = "HealthResponse"; + private static final String HEALTH_CHECK_SCHEMA_NAME = "HealthCheck"; + + private static final Schema healthResponseSchemaDefinition = new SchemaImpl(HEALTH_RESPONSE_SCHEMA_NAME) + .type(Schema.SchemaType.OBJECT) + .properties(Map.ofEntries( + + Map.entry("status", + new SchemaImpl() + .type(Schema.SchemaType.STRING) + .enumeration(List.of("UP", "DOWN"))), + + Map.entry("checks", + new SchemaImpl() + .type(Schema.SchemaType.ARRAY) + .items(new SchemaImpl().ref("#/components/schemas/" + HEALTH_CHECK_SCHEMA_NAME))))); + + private static final Schema healthCheckSchemaDefinition = new SchemaImpl(HEALTH_CHECK_SCHEMA_NAME) + .type(Schema.SchemaType.OBJECT) + .properties(Map.ofEntries( + + Map.entry("name", + new SchemaImpl() + .type(Schema.SchemaType.STRING)), + + Map.entry("status", + new SchemaImpl() + .type(Schema.SchemaType.STRING) + .enumeration(List.of("UP", "DOWN"))), + + Map.entry("data", + new SchemaImpl() + .type(Schema.SchemaType.OBJECT) + .nullable(Boolean.TRUE)))); private final String rootPath; private final String livenessPath; @@ -52,192 +81,102 @@ public void filterOpenAPI(OpenAPI openAPI) { if (openAPI.getComponents() == null) { openAPI.setComponents(new ComponentsImpl()); } - openAPI.getComponents().addSchema(SCHEMA_HEALTH_RESPONSE, createHealthCheckResponse()); - openAPI.getComponents().addSchema(SCHEMA_HEALTH_STATUS, createHealthCheckStatus()); + openAPI.getComponents().addSchema(HEALTH_RESPONSE_SCHEMA_NAME, healthResponseSchemaDefinition); + openAPI.getComponents().addSchema(HEALTH_CHECK_SCHEMA_NAME, healthCheckSchemaDefinition); if (openAPI.getPaths() == null) { openAPI.setPaths(new PathsImpl()); } - Paths paths = openAPI.getPaths(); + + final Paths paths = openAPI.getPaths(); // Health - paths.addPathItem(rootPath, createHealthPathItem()); + paths.addPathItem( + rootPath, + createHealthEndpoint( + "MicroProfile Health Endpoint", + "MicroProfile Health provides a way for your application to distribute " + + "information about its healthiness state to state whether or not it is able to " + + "function properly", + "Check the health of the application", + "microprofile_health_root", + "An aggregated view of the Liveness, Readiness and Startup of this application")); // Liveness - paths.addPathItem(livenessPath, createLivenessPathItem()); + paths.addPathItem( + livenessPath, + createHealthEndpoint( + "MicroProfile Health - Liveness Endpoint", + "Liveness checks are utilized to tell whether the application should be " + + "restarted", + "Check the liveness of the application", + "microprofile_health_liveness", + "The Liveness check of this application")); // Readiness - paths.addPathItem(readinessPath, createReadinessPathItem()); + paths.addPathItem( + readinessPath, + createHealthEndpoint( + "MicroProfile Health - Readiness Endpoint", + "Readiness checks are used to tell whether the application is able to " + + "process requests", + "Check the readiness of the application", + "microprofile_health_readiness", + "The Readiness check of this application")); // Startup - paths.addPathItem(startupPath, createStartupPathItem()); - } - - private PathItem createHealthPathItem() { - PathItem pathItem = new PathItemImpl(); - pathItem.setDescription("MicroProfile Health Endpoint"); - pathItem.setSummary( - "MicroProfile Health provides a way for your application to distribute information about its healthiness state to state whether or not it is able to function properly"); - pathItem.setGET(createHealthOperation()); - return pathItem; - } - - private PathItem createLivenessPathItem() { - PathItem pathItem = new PathItemImpl(); - pathItem.setDescription("MicroProfile Health - Liveness Endpoint"); - pathItem.setSummary( - "Liveness checks are utilized to tell whether the application should be restarted"); - pathItem.setGET(createLivenessOperation()); - return pathItem; - } - - private PathItem createReadinessPathItem() { - PathItem pathItem = new PathItemImpl(); - pathItem.setDescription("MicroProfile Health - Readiness Endpoint"); - pathItem.setSummary( - "Readiness checks are used to tell whether the application is able to process requests"); - pathItem.setGET(createReadinessOperation()); - return pathItem; - } - - private PathItem createStartupPathItem() { - PathItem pathItem = new PathItemImpl(); - pathItem.setDescription("MicroProfile Health - Startup Endpoint"); - pathItem.setSummary( - "Startup checks are an used to tell when the application has started"); - pathItem.setGET(createStartupOperation()); - return pathItem; - } - - private Operation createHealthOperation() { - Operation operation = new OperationImpl(); - operation.setDescription("Check the health of the application"); - operation.setOperationId("microprofile_health_root"); - operation.setTags(MICROPROFILE_HEALTH_TAG); - operation.setSummary("An aggregated view of the Liveness, Readiness and Startup of this application"); - operation.setResponses(createAPIResponses()); - return operation; - } - - private Operation createLivenessOperation() { - Operation operation = new OperationImpl(); - operation.setDescription("Check the liveness of the application"); - operation.setOperationId("microprofile_health_liveness"); - operation.setTags(MICROPROFILE_HEALTH_TAG); - operation.setSummary("The Liveness check of this application"); - operation.setResponses(createAPIResponses()); - return operation; - } - - private Operation createReadinessOperation() { - Operation operation = new OperationImpl(); - operation.setDescription("Check the readiness of the application"); - operation.setOperationId("microprofile_health_readiness"); - operation.setTags(MICROPROFILE_HEALTH_TAG); - operation.setSummary("The Readiness check of this application"); - operation.setResponses(createAPIResponses()); - return operation; - } - - private Operation createStartupOperation() { - Operation operation = new OperationImpl(); - operation.setDescription("Check the startup of the application"); - operation.setOperationId("microprofile_health_startup"); - operation.setTags(MICROPROFILE_HEALTH_TAG); - operation.setSummary("The Startup check of this application"); - operation.setResponses(createAPIResponses()); - return operation; - } - - private APIResponses createAPIResponses() { - APIResponses responses = new APIResponsesImpl(); - responses.addAPIResponse("200", createAPIResponse("OK")); - responses.addAPIResponse("503", createAPIResponse("Service Unavailable")); - responses.addAPIResponse("500", createAPIResponse("Internal Server Error")); - return responses; - } - - private APIResponse createAPIResponse(String description) { - APIResponse response = new APIResponseImpl(); - response.setDescription(description); - response.setContent(createContent()); - return response; - } - - private Content createContent() { - Content content = new ContentImpl(); - content.addMediaType("application/json", createMediaType()); - return content; - } - - private MediaType createMediaType() { - MediaType mediaType = new MediaTypeImpl(); - mediaType.setSchema(new SchemaImpl().ref("#/components/schemas/" + SCHEMA_HEALTH_RESPONSE)); - return mediaType; + paths.addPathItem( + startupPath, + createHealthEndpoint( + "MicroProfile Health - Startup Endpoint", + "Startup checks are an used to tell when the application has started", + "Check the startup of the application", + "microprofile_health_startup", + "The Startup check of this application")); } /** - * HealthCheckResponse: - * type: object - * properties: - * data: - * type: object - * nullable: true - * name: - * type: string - * status: - * $ref: '#/components/schemas/HealthCheckStatus' + * Creates a {@link PathItem} containing the endpoint definition and GET {@link Operation} for health endpoints. * - * @return Schema representing HealthCheckResponse + * @param endpointDescription The description for the endpoint definition + * @param endpointSummary The summary for the endpoint definition + * @param operationDescription The description for the operation definition + * @param operationId The operation-id for the operation definition + * @param operationSummary The summary for the operation definition */ - private Schema createHealthCheckResponse() { - Schema schema = new SchemaImpl(SCHEMA_HEALTH_RESPONSE); - schema.setType(Schema.SchemaType.OBJECT); - schema.setProperties(createProperties()); - return schema; - } - - private Map createProperties() { - Map map = new HashMap<>(); - map.put("data", createData()); - map.put("name", createName()); - map.put("status", new SchemaImpl().ref("#/components/schemas/" + SCHEMA_HEALTH_STATUS)); - return map; - } - - private Schema createData() { - Schema schema = new SchemaImpl("data"); - schema.setType(Schema.SchemaType.OBJECT); - schema.setNullable(Boolean.TRUE); - return schema; - } - - private Schema createName() { - Schema schema = new SchemaImpl("name"); - schema.setType(Schema.SchemaType.STRING); - return schema; - } - - /** - * HealthCheckStatus: - * enum: - * - DOWN - * - UP - * type: string - * - * @return Schema representing Status - */ - private Schema createHealthCheckStatus() { - Schema schema = new SchemaImpl(SCHEMA_HEALTH_STATUS); - schema.setEnumeration(createStateEnumValues()); - schema.setType(Schema.SchemaType.STRING); - return schema; - } - - private List createStateEnumValues() { - List values = new ArrayList<>(); - values.add("DOWN"); - values.add("UP"); - return values; + private PathItem createHealthEndpoint( + String endpointDescription, + String endpointSummary, + String operationDescription, + String operationId, + String operationSummary) { + final Content content = new ContentImpl() + .addMediaType( + "application/json", + new MediaTypeImpl() + .schema(new SchemaImpl().ref("#/components/schemas/" + HEALTH_RESPONSE_SCHEMA_NAME))); + + final APIResponses responses = new APIResponsesImpl() + .addAPIResponse( + "200", + new APIResponseImpl().description("OK").content(content)) + .addAPIResponse( + "503", + new APIResponseImpl().description("Service Unavailable").content(content)) + .addAPIResponse( + "500", + new APIResponseImpl().description("Internal Server Error").content(content)); + + final Operation getOperation = new OperationImpl() + .operationId(operationId) + .description(operationDescription) + .tags(MICROPROFILE_HEALTH_TAG) + .summary(operationSummary) + .responses(responses); + + return new PathItemImpl() + .description(endpointDescription) + .summary(endpointSummary) + .GET(getOperation); } } diff --git a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/DeprecatedHealthOpenAPITest.java b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/DeprecatedHealthOpenAPITest.java index f7c85bd175e985..e9e26dc46e743d 100644 --- a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/DeprecatedHealthOpenAPITest.java +++ b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/DeprecatedHealthOpenAPITest.java @@ -29,11 +29,21 @@ void testOpenApiPathAccessResource() { .when().get(OPEN_API_PATH) .then() .header("Content-Type", "application/json;charset=UTF-8") + .body("paths", Matchers.hasKey("/q/health/ready")) .body("paths", Matchers.hasKey("/q/health/live")) .body("paths", Matchers.hasKey("/q/health/started")) .body("paths", Matchers.hasKey("/q/health")) - .body("components.schemas.HealthCheckResponse.type", Matchers.equalTo("object")); + + .body("components.schemas.HealthResponse.type", Matchers.equalTo("object")) + .body("components.schemas.HealthResponse.properties.status.type", Matchers.equalTo("string")) + .body("components.schemas.HealthResponse.properties.checks.type", Matchers.equalTo("array")) + + .body("components.schemas.HealthCheck.type", Matchers.equalTo("object")) + .body("components.schemas.HealthCheck.properties.status.type", Matchers.equalTo("string")) + .body("components.schemas.HealthCheck.properties.name.type", Matchers.equalTo("string")) + .body("components.schemas.HealthCheck.properties.data.type", Matchers.equalTo("object")) + .body("components.schemas.HealthCheck.properties.data.nullable", Matchers.is(true)); } diff --git a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java index 96697dc0e9c5ed..fe4e1f28f9ee99 100644 --- a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java +++ b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/HealthOpenAPITest.java @@ -32,7 +32,16 @@ void testOpenApiPathAccessResource() { .body("paths", Matchers.hasKey("/q/health/live")) .body("paths", Matchers.hasKey("/q/health/started")) .body("paths", Matchers.hasKey("/q/health")) - .body("components.schemas.HealthCheckResponse.type", Matchers.equalTo("object")); + + .body("components.schemas.HealthResponse.type", Matchers.equalTo("object")) + .body("components.schemas.HealthResponse.properties.status.type", Matchers.equalTo("string")) + .body("components.schemas.HealthResponse.properties.checks.type", Matchers.equalTo("array")) + + .body("components.schemas.HealthCheck.type", Matchers.equalTo("object")) + .body("components.schemas.HealthCheck.properties.status.type", Matchers.equalTo("string")) + .body("components.schemas.HealthCheck.properties.name.type", Matchers.equalTo("string")) + .body("components.schemas.HealthCheck.properties.data.type", Matchers.equalTo("object")) + .body("components.schemas.HealthCheck.properties.data.nullable", Matchers.is(true)); } From 981dd457d744ddaf962fe6de8871af17ec035cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 13 Aug 2024 11:39:27 +0200 Subject: [PATCH 09/41] Test the default H2 dialect version matches the default H2 Maven dependency version --- .../io/quarkus/it/jpa/h2/DialectEndpoint.java | 18 ++++++++++++++++-- .../java/io/quarkus/it/jpa/h2/DialectTest.java | 13 ++++++++++++- .../io/quarkus/it/jpa/h2/DialectEndpoint.java | 18 ++++++++++++++++-- .../java/io/quarkus/it/jpa/h2/DialectTest.java | 13 ++++++++++++- 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/integration-tests/jpa-h2-embedded/src/main/java/io/quarkus/it/jpa/h2/DialectEndpoint.java b/integration-tests/jpa-h2-embedded/src/main/java/io/quarkus/it/jpa/h2/DialectEndpoint.java index 5c74ab76c647e4..8b8acbc42849fa 100644 --- a/integration-tests/jpa-h2-embedded/src/main/java/io/quarkus/it/jpa/h2/DialectEndpoint.java +++ b/integration-tests/jpa-h2-embedded/src/main/java/io/quarkus/it/jpa/h2/DialectEndpoint.java @@ -1,6 +1,9 @@ package io.quarkus.it.jpa.h2; import java.io.IOException; +import java.sql.SQLException; + +import javax.sql.DataSource; import jakarta.inject.Inject; import jakarta.ws.rs.GET; @@ -13,16 +16,27 @@ import io.quarkus.hibernate.orm.runtime.config.DialectVersions; -@Path("/dialect/version") +@Path("/dialect/") @Produces(MediaType.TEXT_PLAIN) public class DialectEndpoint { @Inject SessionFactory sessionFactory; + @Inject + DataSource dataSource; @GET - public String test() throws IOException { + @Path("version") + public String version() throws IOException { var version = sessionFactory.unwrap(SessionFactoryImplementor.class).getJdbcServices().getDialect().getVersion(); return DialectVersions.toString(version); } + @GET + @Path("actual-db-version") + public String actualDbVersion() throws IOException, SQLException { + try (var conn = dataSource.getConnection()) { + return conn.getMetaData().getDatabaseProductVersion(); + } + } + } diff --git a/integration-tests/jpa-h2-embedded/src/test/java/io/quarkus/it/jpa/h2/DialectTest.java b/integration-tests/jpa-h2-embedded/src/test/java/io/quarkus/it/jpa/h2/DialectTest.java index b0fcc3838bdfe8..c50b9af4f10c5f 100644 --- a/integration-tests/jpa-h2-embedded/src/test/java/io/quarkus/it/jpa/h2/DialectTest.java +++ b/integration-tests/jpa-h2-embedded/src/test/java/io/quarkus/it/jpa/h2/DialectTest.java @@ -15,7 +15,8 @@ public class DialectTest { /** - * This is important to avoid https://github.com/quarkusio/quarkus/issues/1886 + * This is important for backwards compatibility reasons: + * we want to keep using at least the same version as before by default. */ @Test public void version() { @@ -23,4 +24,14 @@ public void version() { assertThat(version).startsWith(DialectVersions.Defaults.H2); } + /** + * This is important to avoid https://github.com/quarkusio/quarkus/issues/1886 + */ + @Test + public void actualDbVersion() { + String version = RestAssured.when().get("/dialect/actual-db-version").then().extract().body().asString(); + // Can't use "equal" as the returned string includes trailing information (build date, ...) + assertThat(version).startsWith(DialectVersions.Defaults.H2); + } + } diff --git a/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/DialectEndpoint.java b/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/DialectEndpoint.java index 5c74ab76c647e4..8b8acbc42849fa 100644 --- a/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/DialectEndpoint.java +++ b/integration-tests/jpa-h2/src/main/java/io/quarkus/it/jpa/h2/DialectEndpoint.java @@ -1,6 +1,9 @@ package io.quarkus.it.jpa.h2; import java.io.IOException; +import java.sql.SQLException; + +import javax.sql.DataSource; import jakarta.inject.Inject; import jakarta.ws.rs.GET; @@ -13,16 +16,27 @@ import io.quarkus.hibernate.orm.runtime.config.DialectVersions; -@Path("/dialect/version") +@Path("/dialect/") @Produces(MediaType.TEXT_PLAIN) public class DialectEndpoint { @Inject SessionFactory sessionFactory; + @Inject + DataSource dataSource; @GET - public String test() throws IOException { + @Path("version") + public String version() throws IOException { var version = sessionFactory.unwrap(SessionFactoryImplementor.class).getJdbcServices().getDialect().getVersion(); return DialectVersions.toString(version); } + @GET + @Path("actual-db-version") + public String actualDbVersion() throws IOException, SQLException { + try (var conn = dataSource.getConnection()) { + return conn.getMetaData().getDatabaseProductVersion(); + } + } + } diff --git a/integration-tests/jpa-h2/src/test/java/io/quarkus/it/jpa/h2/DialectTest.java b/integration-tests/jpa-h2/src/test/java/io/quarkus/it/jpa/h2/DialectTest.java index b0fcc3838bdfe8..c50b9af4f10c5f 100644 --- a/integration-tests/jpa-h2/src/test/java/io/quarkus/it/jpa/h2/DialectTest.java +++ b/integration-tests/jpa-h2/src/test/java/io/quarkus/it/jpa/h2/DialectTest.java @@ -15,7 +15,8 @@ public class DialectTest { /** - * This is important to avoid https://github.com/quarkusio/quarkus/issues/1886 + * This is important for backwards compatibility reasons: + * we want to keep using at least the same version as before by default. */ @Test public void version() { @@ -23,4 +24,14 @@ public void version() { assertThat(version).startsWith(DialectVersions.Defaults.H2); } + /** + * This is important to avoid https://github.com/quarkusio/quarkus/issues/1886 + */ + @Test + public void actualDbVersion() { + String version = RestAssured.when().get("/dialect/actual-db-version").then().extract().body().asString(); + // Can't use "equal" as the returned string includes trailing information (build date, ...) + assertThat(version).startsWith(DialectVersions.Defaults.H2); + } + } From d299d555dae1db186c87cf3ed9e123c000019b72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 22:19:33 +0000 Subject: [PATCH 10/41] Bump com.h2database:h2 from 2.3.230 to 2.3.232 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [com.h2database:h2](https://github.com/h2database/h2database) from 2.3.230 to 2.3.232. - [Release notes](https://github.com/h2database/h2database/releases) - [Commits](https://github.com/h2database/h2database/compare/version-2.3.230...version-2.3.232) --- updated-dependencies: - dependency-name: com.h2database:h2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-Authored-By: Yoann Rodière --- bom/application/pom.xml | 2 +- .../quarkus/hibernate/orm/runtime/config/DialectVersions.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 987e8fcfff8251..8936870805d2fc 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -119,7 +119,7 @@ 4.1.5 9.2.1 2.3.2 - 2.3.230 + 2.3.232 42.7.3 3.4.1 8.3.0 diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/config/DialectVersions.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/config/DialectVersions.java index fd4000f2086e39..67f4e28085192b 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/config/DialectVersions.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/config/DialectVersions.java @@ -23,7 +23,7 @@ public static final class Defaults { // This must be aligned on the H2 version in the Quarkus BOM // This must never be removed - public static final String H2 = "2.3.230"; + public static final String H2 = "2.3.232"; private Defaults() { } From 5affc06c6a35460871235357cfd17257c0567ca2 Mon Sep 17 00:00:00 2001 From: Peter Palaga Date: Tue, 13 Aug 2024 12:29:37 +0200 Subject: [PATCH 11/41] Elements missing in quarkus-config-javadoc.yaml for types nested more deeply than 1 level, fix #42501 --- .../io/quarkus/annotation/processor/util/ElementUtil.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/util/ElementUtil.java b/core/processor/src/main/java/io/quarkus/annotation/processor/util/ElementUtil.java index c16f628798fba2..a62011274c905a 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/util/ElementUtil.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/util/ElementUtil.java @@ -117,13 +117,12 @@ public boolean isAnnotationPresent(Element element, String... annotationNames) { */ public boolean isLocalClass(TypeElement clazz) { try { - TypeElement topLevelClass = clazz; - if (clazz.getNestingKind().isNested()) { - topLevelClass = (TypeElement) clazz.getEnclosingElement(); + while (clazz.getNestingKind().isNested()) { + clazz = (TypeElement) clazz.getEnclosingElement(); } processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", - topLevelClass.getQualifiedName().toString().replace('.', '/') + ".java"); + clazz.getQualifiedName().toString().replace('.', '/') + ".java"); return true; } catch (Exception e) { return false; From cda2640a5a552c3d767fd4878ca53ecf4966fdc6 Mon Sep 17 00:00:00 2001 From: Marc Nuri Date: Tue, 13 Aug 2024 12:41:01 +0200 Subject: [PATCH 12/41] deps: Bump kubernetes-client-bom from 6.13.2 to 6.13.3 Signed-off-by: Marc Nuri --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4926f2b964868..c3717abb750a0a 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ 0.8.12 - 6.13.2 + 6.13.3 5.5.0 6.5.2.Final 4.13.0 From 1fb86f76ad85d05690c6c13ba0fa8c207a04df21 Mon Sep 17 00:00:00 2001 From: Peter Palaga Date: Tue, 13 Aug 2024 14:55:08 +0200 Subject: [PATCH 13/41] Freeform Map configuration properties always marked as required, fix #42505 --- .../documentation/config/discovery/ResolvedType.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/ResolvedType.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/ResolvedType.java index f10b93b94fa687..558e65cd4b42b8 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/ResolvedType.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/ResolvedType.java @@ -4,6 +4,8 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; +import io.quarkus.annotation.processor.documentation.config.util.TypeUtil; + public record ResolvedType( TypeMirror wrapperType, TypeMirror unwrappedType, @@ -73,7 +75,11 @@ public static ResolvedType makeMap(TypeMirror type, ResolvedType unwrappedResolv unwrappedResolvedType.binaryName, unwrappedResolvedType.qualifiedName, unwrappedResolvedType.simplifiedName, unwrappedResolvedType.isPrimitive, true, unwrappedResolvedType.isList, - unwrappedResolvedType.isOptional, + unwrappedResolvedType.isOptional + // backwards compatibility with versions before Quarkus 3.14 + // see https://github.com/quarkusio/quarkus/issues/42505 + || "java.lang.String".equals(unwrappedResolvedType.qualifiedName) + || TypeUtil.isPrimitiveWrapper(unwrappedResolvedType.qualifiedName), unwrappedResolvedType.isDeclared, unwrappedResolvedType.isInterface, unwrappedResolvedType.isClass, unwrappedResolvedType.isEnum, unwrappedResolvedType.isDuration, unwrappedResolvedType.isConfigGroup); } From c2019ba63597b2155165110e4ab0db84b14b612c Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Tue, 13 Aug 2024 11:15:34 -0300 Subject: [PATCH 14/41] Commit all doc include changes --- .../quarkiverse/java/.github/workflows/release.tpl.qute.yml | 4 ++-- .../quarkus-my-quarkiverse-ext_.github_workflows_release.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/quarkiverse/java/.github/workflows/release.tpl.qute.yml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/quarkiverse/java/.github/workflows/release.tpl.qute.yml index 67e3e4270562ab..bb3d54d2768ba4 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/quarkiverse/java/.github/workflows/release.tpl.qute.yml +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/quarkiverse/java/.github/workflows/release.tpl.qute.yml @@ -50,8 +50,8 @@ jobs: - name: Update latest release version in docs run: | mvn -B -ntp -pl docs -am package -DskipTests -DskipITs -Denforcer.skip -Dformatter.skip -Dimpsort.skip - if ! git diff --quiet docs/modules/ROOT/pages/includes/attributes.adoc; then - git add docs/modules/ROOT/pages/includes/attributes.adoc + if ! git diff --quiet docs/modules/ROOT/pages/includes; then + git add docs/modules/ROOT/pages/includes git commit -m "Update the latest release version ${{steps.metadata.outputs.current-version}} in documentation" fi diff --git a/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateQuarkiverseExtension/quarkus-my-quarkiverse-ext_.github_workflows_release.yml b/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateQuarkiverseExtension/quarkus-my-quarkiverse-ext_.github_workflows_release.yml index 4bcb4f7d45ef55..b43c1ee0f931d0 100644 --- a/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateQuarkiverseExtension/quarkus-my-quarkiverse-ext_.github_workflows_release.yml +++ b/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateQuarkiverseExtension/quarkus-my-quarkiverse-ext_.github_workflows_release.yml @@ -50,8 +50,8 @@ jobs: - name: Update latest release version in docs run: | mvn -B -ntp -pl docs -am package -DskipTests -DskipITs -Denforcer.skip -Dformatter.skip -Dimpsort.skip - if ! git diff --quiet docs/modules/ROOT/pages/includes/attributes.adoc; then - git add docs/modules/ROOT/pages/includes/attributes.adoc + if ! git diff --quiet docs/modules/ROOT/pages/includes; then + git add docs/modules/ROOT/pages/includes git commit -m "Update the latest release version ${{steps.metadata.outputs.current-version}} in documentation" fi From c358c254da081d4049004ae12067270980e33f40 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 13 Aug 2024 16:28:55 +0100 Subject: [PATCH 15/41] Accept signed OIDC UserInfo --- .../oidc/runtime/OidcIdentityProvider.java | 2 +- .../io/quarkus/oidc/runtime/OidcProvider.java | 37 ++++++++++- .../oidc/runtime/OidcProviderClient.java | 11 ++-- .../src/main/resources/application.properties | 4 +- .../keycloak/CodeFlowAuthorizationTest.java | 61 +++++++++++++++++++ 5 files changed, 107 insertions(+), 8 deletions(-) diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java index d895031606e4e8..83aa12b01af3da 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java @@ -477,7 +477,7 @@ private Uni verifyTokenUni(Map requestD resolvedContext.oidcConfig.token.isSubjectRequired(), nonce)); } catch (Throwable t) { if (t.getCause() instanceof UnresolvableKeyException) { - LOG.debug("No matching JWK key is found, refreshing and repeating the verification"); + LOG.debug("No matching JWK key is found, refreshing and repeating the token verification"); return refreshJwksAndVerifyTokenUni(resolvedContext, token, enforceAudienceVerification, resolvedContext.oidcConfig.token.isSubjectRequired(), nonce); } else { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java index c49c8a6d87372a..1b42daf190431f 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java @@ -41,6 +41,7 @@ import io.quarkus.oidc.UserInfo; import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcConstants; +import io.quarkus.oidc.runtime.OidcProviderClient.UserInfoResponse; import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.credential.TokenCredential; import io.smallrye.jwt.algorithm.SignatureAlgorithm; @@ -65,6 +66,7 @@ public class OidcProvider implements Closeable { AlgorithmConstraints.ConstraintType.PERMIT, ASYMMETRIC_SUPPORTED_ALGORITHMS); private static final AlgorithmConstraints SYMMETRIC_ALGORITHM_CONSTRAINTS = new AlgorithmConstraints( AlgorithmConstraints.ConstraintType.PERMIT, SignatureAlgorithm.HS256.getAlgorithm()); + private static final String APPLICATION_JWT_CONTENT_TYPE = "application/jwt"; static final String ANY_ISSUER = "any"; private final List customValidators; @@ -407,7 +409,40 @@ private static final long now() { } public Uni getUserInfo(String accessToken) { - return client.getUserInfo(accessToken); + return client.getUserInfo(accessToken).onItem() + .transformToUni(new Function>() { + + @Override + public Uni apply(UserInfoResponse response) { + if (APPLICATION_JWT_CONTENT_TYPE.equals(response.contentType())) { + if (oidcConfig.jwks.resolveEarly) { + try { + LOG.debugf("Verifying the signed UserInfo with the local JWK keys: %s", response.data()); + return Uni.createFrom().item( + new UserInfo( + verifyJwtToken(response.data(), true, false, null).localVerificationResult + .encode())); + } catch (Throwable t) { + if (t.getCause() instanceof UnresolvableKeyException) { + LOG.debug( + "No matching JWK key is found, refreshing and repeating the signed UserInfo verification"); + return refreshJwksAndVerifyJwtToken(response.data(), true, false, null) + .onItem().transform(v -> new UserInfo(v.localVerificationResult.encode())); + } else { + LOG.debugf("Signed UserInfo verification has failed: %s", t.getMessage()); + return Uni.createFrom().failure(t); + } + } + } else { + return getKeyResolverAndVerifyJwtToken(new TokenCredential(response.data(), "userinfo"), true, + false, null, true) + .onItem().transform(v -> new UserInfo(v.localVerificationResult.encode())); + } + } else { + return Uni.createFrom().item(new UserInfo(response.data())); + } + } + }); } public Uni getCodeFlowTokens(String code, String redirectUri, String codeVerifier) { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java index 0a01e4b8adf9bc..bcd9db16df7618 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClient.java @@ -15,7 +15,6 @@ import io.quarkus.oidc.OidcConfigurationMetadata; import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.TokenIntrospection; -import io.quarkus.oidc.UserInfo; import io.quarkus.oidc.common.OidcEndpoint; import io.quarkus.oidc.common.OidcRequestContextProperties; import io.quarkus.oidc.common.OidcRequestFilter; @@ -36,7 +35,6 @@ public class OidcProviderClient implements Closeable { private static final Logger LOG = Logger.getLogger(OidcProviderClient.class); - private static final String TENANT_ID_ATTRIBUTE = "oidc-tenant-id"; private static final String AUTHORIZATION_HEADER = String.valueOf(HttpHeaders.AUTHORIZATION); private static final String CONTENT_TYPE_HEADER = String.valueOf(HttpHeaders.CONTENT_TYPE); private static final String ACCEPT_HEADER = String.valueOf(HttpHeaders.ACCEPT); @@ -93,7 +91,7 @@ public Uni getJsonWebKeySet(OidcRequestContextProperties contextP .transform(resp -> getJsonWebKeySet(resp)); } - public Uni getUserInfo(String token) { + public Uni getUserInfo(String token) { LOG.debugf("Get UserInfo on: %s auth: %s", metadata.getUserInfoUri(), OidcConstants.BEARER_SCHEME + " " + token); return OidcCommonUtils .sendRequest(vertx, @@ -221,8 +219,8 @@ private AuthorizationCodeTokens getAuthorizationCodeTokens(HttpResponse return new AuthorizationCodeTokens(idToken, accessToken, refreshToken, tokenExpiresIn); } - private UserInfo getUserInfo(HttpResponse resp) { - return new UserInfo(getString(metadata.getUserInfoUri(), resp)); + private UserInfoResponse getUserInfo(HttpResponse resp) { + return new UserInfoResponse(resp.getHeader(CONTENT_TYPE_HEADER), getString(metadata.getUserInfoUri(), resp)); } private TokenIntrospection getTokenIntrospection(HttpResponse resp) { @@ -290,4 +288,7 @@ public Vertx getVertx() { public WebClient getWebClient() { return client; } + + static record UserInfoResponse(String contentType, String data) { + }; } diff --git a/integration-tests/oidc-wiremock/src/main/resources/application.properties b/integration-tests/oidc-wiremock/src/main/resources/application.properties index f1ec17c5f19494..5495b0fff618dc 100644 --- a/integration-tests/oidc-wiremock/src/main/resources/application.properties +++ b/integration-tests/oidc-wiremock/src/main/resources/application.properties @@ -123,7 +123,7 @@ quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.application-type=hybri quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.auth-server-url=${keycloak.url}/realms/quarkus/ quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.authorization-path=/ quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.token-path=access_token_refreshed -quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.user-info-path=protocol/openid-connect/userinfo +quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.user-info-path=protocol/openid-connect/signeduserinfo quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.jwks-path=${keycloak.url}/realms/quarkus/protocol/openid-connect/certs quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.code-grant.extra-params.extra-param=extra-param-value quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.code-grant.headers.X-Custom=XCustomHeaderValue @@ -233,6 +233,8 @@ quarkus.log.category."io.quarkus.oidc.runtime.OidcProvider".min-level=TRACE quarkus.log.category."io.quarkus.oidc.runtime.OidcProvider".level=TRACE quarkus.log.category."io.quarkus.oidc.runtime.OidcProviderClient".min-level=TRACE quarkus.log.category."io.quarkus.oidc.runtime.OidcProviderClient".level=TRACE +quarkus.log.file.enable=true +quarkus.log.file.format=%C - %s%n quarkus.http.auth.permission.logout.paths=/code-flow/logout quarkus.http.auth.permission.logout.policy=authenticated diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java index a224ca40d4f7fc..66a3931fc3dc73 100644 --- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java +++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java @@ -7,23 +7,33 @@ import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.matching; import static com.github.tomakehurst.wiremock.client.WireMock.notContaining; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static org.awaitility.Awaitility.given; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.PrivateKey; import java.util.Date; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.TimeUnit; import javax.crypto.SecretKey; +import org.awaitility.core.ThrowingRunnable; import org.hamcrest.Matchers; import org.htmlunit.SilentCssErrorHandler; import org.htmlunit.TextPage; @@ -33,6 +43,7 @@ import org.htmlunit.html.HtmlForm; import org.htmlunit.html.HtmlPage; import org.htmlunit.util.Cookie; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -49,6 +60,7 @@ import io.restassured.http.ContentType; import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm; import io.smallrye.jwt.algorithm.SignatureAlgorithm; +import io.smallrye.jwt.build.Jwt; import io.smallrye.jwt.util.KeyUtils; import io.vertx.core.json.JsonObject; @@ -361,6 +373,43 @@ public void testCodeFlowUserInfoCachedInIdToken() throws Exception { .body(Matchers.equalTo("alice:alice:alice-certificate, cache size: 0, TenantConfigResolver: false")); clearCache(); + checkSignedUserInfoRecordInLog(); + } + + private void checkSignedUserInfoRecordInLog() { + final Path logDirectory = Paths.get(".", "target"); + given().await().pollInterval(100, TimeUnit.MILLISECONDS) + .atMost(10, TimeUnit.SECONDS) + .untilAsserted(new ThrowingRunnable() { + @Override + public void run() throws Throwable { + Path accessLogFilePath = logDirectory.resolve("quarkus.log"); + boolean fileExists = Files.exists(accessLogFilePath); + if (!fileExists) { + accessLogFilePath = logDirectory.resolve("target/quarkus.log"); + fileExists = Files.exists(accessLogFilePath); + } + Assertions.assertTrue(Files.exists(accessLogFilePath), + "quarkus log file " + accessLogFilePath + " is missing"); + + boolean signedUserInfoLogDetected = false; + + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(accessLogFilePath)), + StandardCharsets.UTF_8))) { + String line = null; + while ((line = reader.readLine()) != null) { + if (line.contains("Verifying the signed UserInfo with the local JWK keys: ey")) { + signedUserInfoLogDetected = true; + break; + } + } + } + assertTrue(signedUserInfoLogDetected, + "Log file must contain a record confirming that signed UserInfo is returned"); + + } + }); } @Test @@ -564,6 +613,18 @@ private void defineCodeFlowUserInfoCachedInIdTokenStub() { + "\"expires_in\": 305" + "}"))); + wireMockServer.stubFor( + get(urlEqualTo("/auth/realms/quarkus/protocol/openid-connect/signeduserinfo")) + .withHeader("Authorization", containing("Bearer ey")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/jwt") + .withBody( + Jwt.preferredUserName("alice") + .issuer("https://server.example.com") + .audience("quarkus-web-app") + .jws() + .keyId("1").sign("privateKey.jwk")))); + } private void defineCodeFlowTokenIntrospectionStub() { From 8a0fa3ee1f57a6a73435f78530507b55fd484f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 13 Aug 2024 17:39:31 +0200 Subject: [PATCH 16/41] Retire the area/persistence label and use area/jdbc where possible --- .github/quarkus-github-bot.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/quarkus-github-bot.yml b/.github/quarkus-github-bot.yml index 53556b8e171ddf..024cc71aa864e2 100644 --- a/.github/quarkus-github-bot.yml +++ b/.github/quarkus-github-bot.yml @@ -45,13 +45,9 @@ triage: directories: - extensions/amazon-lambda - integration-tests/amazon-lambda - - id: persistence - labels: [area/persistence] - title: "persistence" - directories: - - extensions/jdbc/ - id: db2 - labels: [area/persistence] + # There's no label for DB2, and JDBC/reactive-clients are handled elsewhere. + labels: [] title: "db2" notify: [mswatosh] directories: @@ -109,7 +105,7 @@ triage: - devtools/platform-descriptor-json/src/main/resources/codestarts/ - devtools/platform-descriptor-json/src/main/resources/templates/ - id: hibernate-reactive - labels: [area/hibernate-reactive, area/persistence] + labels: [area/hibernate-reactive] title: "hibernate.reactive" expression: | matches("hibernate", title) && matches("reactive", title) @@ -119,7 +115,7 @@ triage: directories: - extensions/hibernate-reactive - id: hibernate-orm - labels: [area/hibernate-orm, area/persistence] + labels: [area/hibernate-orm] expression: | matches("hibernate", title) && !matches("reactive", title) && !matches("hibernate.validator", title) @@ -732,6 +728,12 @@ triage: labels: [area/devservices] title: "dev.?services?" notify: [stuartwdouglas, geoand] + - id: jdbc + labels: [area/jdbc] + title: "jdbc" + notify: [barreiro,yrodiere] + directories: + - extensions/jdbc/ - id: reactive-sql-clients labels: [area/reactive-sql-clients] title: "(reactive sql|reactive pool|pgpool|mysqlpool|db2pool)" From 12eed297e2daac70b85987967af1c9d2ac719118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 13 Aug 2024 17:40:29 +0200 Subject: [PATCH 17/41] Have @yrodiere track area/jdbc in the GitHub Lottery Until we find a more suitable candidate. --- .github/quarkus-github-lottery.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/quarkus-github-lottery.yml b/.github/quarkus-github-lottery.yml index 4fb5a9eeb635d1..e54c3bb91faee1 100644 --- a/.github/quarkus-github-lottery.yml +++ b/.github/quarkus-github-lottery.yml @@ -28,7 +28,7 @@ participants: days: ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"] maxIssues: 3 maintenance: - labels: ["area/hibernate-orm", "area/hibernate-search", "area/elasticsearch"] + labels: ["area/hibernate-orm", "area/hibernate-search", "area/elasticsearch", "area/jdbc"] days: ["WEDNESDAY"] feedback: needed: From 251aaa1b229204044f3a755af26614d2efafc8e7 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 13 Aug 2024 18:34:05 +0200 Subject: [PATCH 18/41] Move optional free form map resolution to where it's already handled --- .../documentation/config/discovery/ResolvedType.java | 8 +------- .../documentation/config/resolver/ConfigResolver.java | 6 ++++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/ResolvedType.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/ResolvedType.java index 558e65cd4b42b8..f10b93b94fa687 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/ResolvedType.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/ResolvedType.java @@ -4,8 +4,6 @@ import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; -import io.quarkus.annotation.processor.documentation.config.util.TypeUtil; - public record ResolvedType( TypeMirror wrapperType, TypeMirror unwrappedType, @@ -75,11 +73,7 @@ public static ResolvedType makeMap(TypeMirror type, ResolvedType unwrappedResolv unwrappedResolvedType.binaryName, unwrappedResolvedType.qualifiedName, unwrappedResolvedType.simplifiedName, unwrappedResolvedType.isPrimitive, true, unwrappedResolvedType.isList, - unwrappedResolvedType.isOptional - // backwards compatibility with versions before Quarkus 3.14 - // see https://github.com/quarkusio/quarkus/issues/42505 - || "java.lang.String".equals(unwrappedResolvedType.qualifiedName) - || TypeUtil.isPrimitiveWrapper(unwrappedResolvedType.qualifiedName), + unwrappedResolvedType.isOptional, unwrappedResolvedType.isDeclared, unwrappedResolvedType.isInterface, unwrappedResolvedType.isClass, unwrappedResolvedType.isEnum, unwrappedResolvedType.isDuration, unwrappedResolvedType.isConfigGroup); } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java index 88a4098ca0e50d..377dc0484e5685 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java @@ -172,9 +172,11 @@ private void resolveProperty(ConfigRoot configRoot, Map e } String potentiallyMappedPath = propertyPath; + boolean optional = discoveryConfigProperty.getType().isOptional(); if (discoveryConfigProperty.getType().isMap()) { - // it is a leaf pass through map + // it is a leaf pass through map, it is always optional + optional = true; typeQualifiedName = discoveryConfigProperty.getType().wrapperType().toString(); typeSimplifiedName = utils.element().simplifyGenericType(discoveryConfigProperty.getType().wrapperType()); @@ -192,7 +194,7 @@ private void resolveProperty(ConfigRoot configRoot, Map e discoveryConfigProperty.getSourceName(), potentiallyMappedPath, additionalPaths, ConfigNamingUtil.toEnvVarName(potentiallyMappedPath), typeQualifiedName, typeSimplifiedName, discoveryConfigProperty.getType().isMap(), discoveryConfigProperty.getType().isList(), - discoveryConfigProperty.getType().isOptional(), discoveryConfigProperty.getMapKey(), + optional, discoveryConfigProperty.getMapKey(), discoveryConfigProperty.isUnnamedMapKey(), context.isWithinMap(), discoveryConfigProperty.isConverted(), discoveryConfigProperty.getType().isEnum(), From 96b107bbc4e64e4489a98e3fa07c9fd2570f7ce6 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 13 Aug 2024 22:26:54 +0200 Subject: [PATCH 19/41] Fix missing javadoc --- .../pep/runtime/KeycloakPolicyEnforcerTenantConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerTenantConfig.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerTenantConfig.java index d27daf91bf2790..dbd3517d46dfc8 100644 --- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerTenantConfig.java +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerTenantConfig.java @@ -152,13 +152,13 @@ interface PathCacheConfig { interface ClaimInformationPointConfig { /** - * + * Complex config. */ @WithParentName Map>> complexConfig(); /** - * + * Simple config. */ @WithParentName Map> simpleConfig(); From 284e85a4b03f461ea27c7529686f52528a248023 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 13 Aug 2024 22:48:59 +0200 Subject: [PATCH 20/41] Fix the paths considered for the documentation build The annotation processor was only added in for the push in #42076 and we need to run it also for the PR. In passing, fixed some other stuff. --- .github/workflows/ci-actions-incremental.yml | 4 ++-- .github/workflows/doc-build.yml | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-actions-incremental.yml b/.github/workflows/ci-actions-incremental.yml index 3217d4787efdb1..dc91ffe82d1b24 100644 --- a/.github/workflows/ci-actions-incremental.yml +++ b/.github/workflows/ci-actions-incremental.yml @@ -13,7 +13,7 @@ on: - '*.txt' - 'adr/**' - 'jakarta/**' - - 'docs/src/main/asciidoc/**' + - 'docs/**' - '.github/ISSUE_TEMPLATE/**' - '.github/*.yml' - '.github/*.java' @@ -31,7 +31,7 @@ on: - '*.txt' - 'adr/**' - 'jakarta/**' - - 'docs/src/main/asciidoc/**' + - 'docs/**' - '.github/ISSUE_TEMPLATE/**' - '.github/*.yml' - '.github/*.java' diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index 04e6f172b79f86..9334f120080d86 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -7,12 +7,15 @@ on: - '3.0' paths: - 'core/processor/**' - - 'docs/src/main/asciidoc/**' + - 'devtools/config-doc-maven-plugin/**' + - 'docs/**' - '.github/workflows/doc-build.yml' pull_request: types: [opened, synchronize, reopened] paths: - - 'docs/src/main/asciidoc/**' + - 'core/processor/**' + - 'devtools/config-doc-maven-plugin/**' + - 'docs/**' - '.github/workflows/doc-build.yml' concurrency: From 1b9ac694ecd14b19b6f7488c260f6c17d8198c1b Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 13 Aug 2024 23:14:28 +0200 Subject: [PATCH 21/41] Do not run graphviz when skipping docs --- docs/pom.xml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 5201246f047963..f96bd608be1a5b 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -3477,8 +3477,6 @@ dot - - @@ -3525,6 +3523,16 @@ + + us.bryon + graphviz-maven-plugin + + + dot-files + none + + + From 635c9a00c3c13ad4a8ec554e0b18cc7ff475a69c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 13 Aug 2024 23:24:10 +0200 Subject: [PATCH 22/41] Catch most changes that could affect the doc build In particular any change to a pom or a config class. The pattern is a bit naive but it should work in most cases. --- .github/workflows/doc-build.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index 9334f120080d86..363c5ccf4adea7 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -9,6 +9,14 @@ on: - 'core/processor/**' - 'devtools/config-doc-maven-plugin/**' - 'docs/**' + - 'extensions/**/pom.xml' + - 'extensions/**/*Config*.java' + - 'extensions/core/runtime/pom.xml' + - 'extensions/core/runtime/**/*Config*.java' + - 'extensions/core/deployment/pom.xml' + - 'extensions/core/deployment/**/*Config*.java' + - 'test-framework/jacoco/**/pom.xml' + - 'test-framework/jacoco/**/*Config*.java' - '.github/workflows/doc-build.yml' pull_request: types: [opened, synchronize, reopened] @@ -16,6 +24,14 @@ on: - 'core/processor/**' - 'devtools/config-doc-maven-plugin/**' - 'docs/**' + - 'extensions/**/pom.xml' + - 'extensions/**/*Config*.java' + - 'extensions/core/runtime/pom.xml' + - 'extensions/core/runtime/**/*Config*.java' + - 'extensions/core/deployment/pom.xml' + - 'extensions/core/deployment/**/*Config*.java' + - 'test-framework/jacoco/**/pom.xml' + - 'test-framework/jacoco/**/*Config*.java' - '.github/workflows/doc-build.yml' concurrency: From 9f553398e33fbe8b116596a5e11283eec2b03920 Mon Sep 17 00:00:00 2001 From: Rolfe Dlugy-Hegwer Date: Tue, 13 Aug 2024 17:53:01 -0400 Subject: [PATCH 23/41] Updates to reflect current extension names and configuration roots --- .../asciidoc/security-csrf-prevention.adoc | 18 +++++++++--------- ...curity-openid-connect-client-reference.adoc | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/src/main/asciidoc/security-csrf-prevention.adoc b/docs/src/main/asciidoc/security-csrf-prevention.adoc index 7cc847dfe5dce9..a5a2baa0f6080c 100644 --- a/docs/src/main/asciidoc/security-csrf-prevention.adoc +++ b/docs/src/main/asciidoc/security-csrf-prevention.adoc @@ -126,8 +126,8 @@ At this stage no additional configuration is needed - by default the CSRF form f [source,properties] ---- -quarkus.csrf-reactive.form-field-name=csrftoken -quarkus.csrf-reactive.cookie-name=csrftoken +quarkus.rest-csrf.form-field-name=csrftoken +quarkus.rest-csrf.cookie-name=csrftoken ---- == Sign CSRF token @@ -136,7 +136,7 @@ You can get `HMAC` signatures created for the generated CSRF tokens and have the [source,properties] ---- -quarkus.csrf-reactive.token-signature-key=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow +quarkus.rest-csrf.token-signature-key=AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow ---- [[csrf-request-header]] @@ -151,18 +151,18 @@ If HTML `form` tags are not used and you need to pass CSRF token as a header, th ---- <1> This expression is used to inject a CSRF token header and token. This token will be verified by the CSRF filter against a CSRF cookie. -Default header name is `X-CSRF-TOKEN`, you can customize it with `quarkus.csrf-reactive.token-header-name`, for example: +Default header name is `X-CSRF-TOKEN`, you can customize it with `quarkus.rest-csrf.token-header-name`, for example: [source,properties] ---- -quarkus.csrf-reactive.token-header-name=CUSTOM-X-CSRF-TOKEN +quarkus.rest-csrf.token-header-name=CUSTOM-X-CSRF-TOKEN ---- If you need to access the CSRF cookie from JavaScript in order to pass its value as a header, use `{inject:csrf.cookieName}` and `{inject:csrf.headerName}` to inject the cookie name which has to be read as a CSRF header value and allow accessing this cookie: [source,properties] ---- -quarkus.csrf-reactive.cookie-http-only=false +quarkus.rest-csrf.cookie-http-only=false ---- == Cross-origin resource sharing @@ -255,11 +255,11 @@ As you can see a CSRF token verification will be required at the `/service/user` [source,properties] ---- # Verify CSRF token only for the `/service/user` path, ignore other paths such as `/service/users` -quarkus.csrf-reactive.create-token-path=/service/user +quarkus.rest-csrf.create-token-path=/service/user # If `/service/user` path accepts not only `application/x-www-form-urlencoded` payloads but also other ones such as JSON then allow them # Setting this property is not necessary when the token is submitted as a header value -quarkus.csrf-reactive.require-form-url-encoded=false +quarkus.rest-csrf.require-form-url-encoded=false ---- == Verify CSRF token in the application code @@ -316,7 +316,7 @@ Also disable the token verification in the filter: [source,properties] ---- -quarkus.csrf-reactive.verify-token=false +quarkus.rest-csrf.verify-token=false ---- [[csrf-reactive-configuration-reference]] diff --git a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc index cf106e8bfdaec3..2de1e8a175bf7e 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc @@ -1155,7 +1155,7 @@ quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=exchange quarkus.oidc-client.grant-options.exchange.audience=quarkus-app-exchange -quarkus.oidc-token-propagation.exchange-token=true <1> +quarkus.resteasy-client-oidc-token-propagation.exchange-token=true <1> ---- <1> Please note that the `exchange-token` configuration property is ignored when the OidcClient name is set with the `io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient` annotation attribute. @@ -1173,10 +1173,10 @@ quarkus.oidc-client.grant.type=jwt quarkus.oidc-client.grant-options.jwt.requested_token_use=on_behalf_of quarkus.oidc-client.scopes=https://graph.microsoft.com/user.read,offline_access -quarkus.oidc-token-propagation.exchange-token=true +quarkus.resteasy-client-oidc-token-propagation.exchange-token=true ---- -`AccessTokenRequestReactiveFilter` uses a default `OidcClient` by default. A named `OidcClient` can be selected with a `quarkus.oidc-token-propagation-reactive.client-name` configuration property or with the `io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient` annotation attribute. +`AccessTokenRequestReactiveFilter` uses a default `OidcClient` by default. A named `OidcClient` can be selected with a `quarkus.rest-client-oidc-token-propagation.client-name` configuration property or with the `io.quarkus.oidc.token.propagation.AccessToken#exchangeTokenClient` annotation attribute. [[token-propagation]] == Token Propagation @@ -1231,7 +1231,7 @@ public interface ProtectedResourceService { } ---- -Alternatively, `AccessTokenRequestFilter` can be registered automatically with all MP Rest or Jakarta REST clients if the `quarkus.oidc-token-propagation.register-filter` property is set to `true` and `quarkus.oidc-token-propagation.json-web-token` property is set to `false` (which is a default value). +Alternatively, `AccessTokenRequestFilter` can be registered automatically with all MP Rest or Jakarta REST clients if the `quarkus.resteasy-client-oidc-token-propagation.register-filter` property is set to `true` and `quarkus.resteasy-client-oidc-token-propagation.json-web-token` property is set to `false` (which is a default value). ==== Exchange token before propagation @@ -1245,7 +1245,7 @@ quarkus.oidc-client.credentials.secret=secret quarkus.oidc-client.grant.type=exchange quarkus.oidc-client.grant-options.exchange.audience=quarkus-app-exchange -quarkus.oidc-token-propagation.exchange-token=true +quarkus.resteasy-client-oidc-token-propagation.exchange-token=true ---- If you work with providers such as `Azure` that link:https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow#example[require using] link:https://www.rfc-editor.org/rfc/rfc7523#section-2.1[JWT bearer token grant] to exchange the current token, then you can configure `AccessTokenRequestFilter` to exchange the token like this: @@ -1260,12 +1260,12 @@ quarkus.oidc-client.grant.type=jwt quarkus.oidc-client.grant-options.jwt.requested_token_use=on_behalf_of quarkus.oidc-client.scopes=https://graph.microsoft.com/user.read,offline_access -quarkus.oidc-token-propagation.exchange-token=true +quarkus.resteasy-client-oidc-token-propagation.exchange-token=true ---- Note `AccessTokenRequestFilter` will use `OidcClient` to exchange the current token, and you can use `quarkus.oidc-client.grant-options.exchange` to set the additional exchange properties expected by your OpenID Connect Provider. -`AccessTokenRequestFilter` uses a default `OidcClient` by default. A named `OidcClient` can be selected with a `quarkus.oidc-token-propagation.client-name` configuration property. +`AccessTokenRequestFilter` uses a default `OidcClient` by default. A named `OidcClient` can be selected with a `quarkus.resteasy-client-oidc-token-propagation.client-name` configuration property. === RestClient JsonWebTokenRequestFilter @@ -1307,7 +1307,7 @@ public interface ProtectedResourceService { } ---- -Alternatively, `JsonWebTokenRequestFilter` can be registered automatically with all MicroProfile REST or Jakarta REST clients if both `quarkus.oidc-token-propagation.register-filter` and `quarkus.resteasy-client-oidc-token-propagation.json-web-token` properties are set to `true`. +Alternatively, `JsonWebTokenRequestFilter` can be registered automatically with all MicroProfile REST or Jakarta REST clients if both `quarkus.resteasy-client-oidc-token-propagation.register-filter` and `quarkus.resteasy-client-oidc-token-propagation.json-web-token` properties are set to `true`. ==== Update token before propagation From 83a793b649b4d8d3bccc0a6abfc1bc90f353eef8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 22:34:33 +0000 Subject: [PATCH 24/41] Bump flyway.version from 10.17.0 to 10.17.1 Bumps `flyway.version` from 10.17.0 to 10.17.1. Updates `org.flywaydb:flyway-core` from 10.17.0 to 10.17.1 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-10.17.0...flyway-10.17.1) Updates `org.flywaydb:flyway-sqlserver` from 10.17.0 to 10.17.1 Updates `org.flywaydb:flyway-mysql` from 10.17.0 to 10.17.1 Updates `org.flywaydb:flyway-database-oracle` from 10.17.0 to 10.17.1 Updates `org.flywaydb:flyway-database-postgresql` from 10.17.0 to 10.17.1 Updates `org.flywaydb:flyway-database-db2` from 10.17.0 to 10.17.1 Updates `org.flywaydb:flyway-database-derby` from 10.17.0 to 10.17.1 Updates `org.flywaydb:flyway-database-mongodb` from 10.17.0 to 10.17.1 --- updated-dependencies: - dependency-name: org.flywaydb:flyway-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-sqlserver dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-mysql dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-database-oracle dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-database-postgresql dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-database-db2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-database-derby dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-database-mongodb 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 5cb58d67e448c8..1c212f002bda1a 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -160,7 +160,7 @@ 3.2.0 4.2.2 3.0.6.Final - 10.17.0 + 10.17.1 3.0.4 4.29.1 From c512e3d8e81088351f66cfe1480893e541935237 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 13 Aug 2024 18:56:06 +0200 Subject: [PATCH 25/41] Provide a @ConfigDocEnum annotation to enforce hyphenation of enum Also refactor things a bit to centralize the handling of the common annotations. Fixes #42514 --- .../discovery/DiscoveryConfigProperty.java | 16 ++++++++++-- .../config/resolver/ConfigResolver.java | 3 ++- .../scanner/AbstractConfigListener.java | 26 +++++++++++++++++++ .../config/scanner/ConfigMappingListener.java | 11 +------- .../scanner/LegacyConfigRootListener.java | 11 +------- .../documentation/config/util/Types.java | 1 + .../runtime/annotations/ConfigDocEnum.java | 24 +++++++++++++++++ 7 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocEnum.java diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigProperty.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigProperty.java index 84c5ff19660c78..5f1c49fa11902c 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigProperty.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigProperty.java @@ -15,12 +15,13 @@ public class DiscoveryConfigProperty { private final boolean unnamedMapKey; private final ResolvedType type; private final boolean converted; + private final boolean enforceHyphenateEnumValue; private final boolean section; private final boolean sectionGenerated; public DiscoveryConfigProperty(String path, String sourceClass, String sourceName, String defaultValue, String defaultValueForDoc, boolean deprecated, String mapKey, boolean unnamedMapKey, - ResolvedType type, boolean converted, + ResolvedType type, boolean converted, boolean enforceHyphenateEnumValue, boolean section, boolean sectionGenerated) { this.path = path; this.sourceClass = sourceClass; @@ -32,6 +33,7 @@ public DiscoveryConfigProperty(String path, String sourceClass, String sourceNam this.unnamedMapKey = unnamedMapKey; this.type = type; this.converted = converted; + this.enforceHyphenateEnumValue = enforceHyphenateEnumValue; this.section = section; this.sectionGenerated = sectionGenerated; } @@ -76,6 +78,10 @@ public boolean isConverted() { return converted; } + public boolean isEnforceHyphenateEnumValue() { + return enforceHyphenateEnumValue; + } + public boolean isSection() { return section; } @@ -132,6 +138,7 @@ public static class Builder { private String mapKey; private boolean unnamedMapKey = false; private boolean converted = false; + private boolean enforceHyphenateEnumValue = false; private boolean section = false; private boolean sectionGenerated = false; @@ -176,6 +183,11 @@ public Builder converted() { return this; } + public Builder enforceHyphenateEnumValues() { + this.enforceHyphenateEnumValue = true; + return this; + } + public Builder section(boolean generated) { this.section = true; this.sectionGenerated = generated; @@ -191,7 +203,7 @@ public DiscoveryConfigProperty build() { } return new DiscoveryConfigProperty(name, sourceClass, sourceName, defaultValue, defaultValueForDoc, deprecated, - mapKey, unnamedMapKey, type, converted, section, sectionGenerated); + mapKey, unnamedMapKey, type, converted, enforceHyphenateEnumValue, section, sectionGenerated); } } } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java index 88a4098ca0e50d..0280cedc5254ca 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java @@ -153,7 +153,8 @@ private void resolveProperty(ConfigRoot configRoot, Map e String typeSimplifiedName = discoveryConfigProperty.getType().simplifiedName(); // if the property has a converter, we don't hyphenate the values (per historical rules, not exactly sure of the reason) - boolean hyphenateEnumValues = !discoveryConfigProperty.isConverted(); + boolean hyphenateEnumValues = discoveryConfigProperty.isEnforceHyphenateEnumValue() || + !discoveryConfigProperty.isConverted(); String defaultValue = getDefaultValue(discoveryConfigProperty.getDefaultValue(), discoveryConfigProperty.getDefaultValueForDoc(), discoveryConfigProperty.getType(), hyphenateEnumValues); diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/AbstractConfigListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/AbstractConfigListener.java index 2d4765f40e103a..54ce54500882f3 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/AbstractConfigListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/AbstractConfigListener.java @@ -10,8 +10,10 @@ import javax.lang.model.element.TypeElement; import io.quarkus.annotation.processor.documentation.config.discovery.DiscoveryConfigGroup; +import io.quarkus.annotation.processor.documentation.config.discovery.DiscoveryConfigProperty; import io.quarkus.annotation.processor.documentation.config.discovery.EnumDefinition; import io.quarkus.annotation.processor.documentation.config.discovery.EnumDefinition.EnumConstant; +import io.quarkus.annotation.processor.documentation.config.discovery.ResolvedType; import io.quarkus.annotation.processor.documentation.config.util.Types; import io.quarkus.annotation.processor.util.Config; import io.quarkus.annotation.processor.util.Utils; @@ -63,4 +65,28 @@ public void onResolvedEnum(TypeElement enumTypeElement) { enumConstants); configCollector.addResolvedEnum(enumDefinition); } + + protected void handleCommonPropertyAnnotations(DiscoveryConfigProperty.Builder builder, + Map propertyAnnotations, ResolvedType resolvedType, String sourceName) { + + AnnotationMirror configDocSectionAnnotation = propertyAnnotations.get(Types.ANNOTATION_CONFIG_DOC_SECTION); + if (configDocSectionAnnotation != null) { + Boolean sectionGenerated = (Boolean) utils.element().getAnnotationValues(configDocSectionAnnotation) + .get("generated"); + if (sectionGenerated != null && sectionGenerated) { + builder.section(true); + } else { + builder.section(false); + } + } + + AnnotationMirror configDocEnum = propertyAnnotations.get(Types.ANNOTATION_CONFIG_DOC_ENUM); + if (configDocEnum != null) { + Boolean enforceHyphenateValues = (Boolean) utils.element().getAnnotationValues(configDocEnum) + .get("enforceHyphenateValues"); + if (enforceHyphenateValues != null && enforceHyphenateValues) { + builder.enforceHyphenateEnumValues(); + } + } + } } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java index b790c716a245b2..988805d7f532d4 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java @@ -174,16 +174,7 @@ public void onEnclosedMethod(DiscoveryRootElement discoveryRootElement, TypeElem builder.converted(); } - AnnotationMirror configDocSectionAnnotation = methodAnnotations.get(Types.ANNOTATION_CONFIG_DOC_SECTION); - if (configDocSectionAnnotation != null) { - Boolean sectionGenerated = (Boolean) utils.element().getAnnotationValues(configDocSectionAnnotation) - .get("generated"); - if (sectionGenerated != null && sectionGenerated) { - builder.section(true); - } else { - builder.section(false); - } - } + handleCommonPropertyAnnotations(builder, methodAnnotations, resolvedType, sourceName); discoveryRootElement.addProperty(builder.build()); } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java index e4b3e31a840307..bc7aadb4dc150f 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java @@ -176,16 +176,7 @@ public void onEnclosedField(DiscoveryRootElement discoveryRootElement, TypeEleme builder.converted(); } - AnnotationMirror configDocSectionAnnotation = fieldAnnotations.get(Types.ANNOTATION_CONFIG_DOC_SECTION); - if (configDocSectionAnnotation != null) { - Boolean sectionGenerated = (Boolean) utils.element().getAnnotationValues(configDocSectionAnnotation) - .get("generated"); - if (sectionGenerated != null && sectionGenerated) { - builder.section(true); - } else { - builder.section(false); - } - } + handleCommonPropertyAnnotations(builder, fieldAnnotations, resolvedType, sourceName); discoveryRootElement.addProperty(builder.build()); } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/Types.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/Types.java index f98d74f7011531..bfb1e188f7ded3 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/Types.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/Types.java @@ -31,6 +31,7 @@ private Types() { public static final String ANNOTATION_CONFIG_DOC_DEFAULT = "io.quarkus.runtime.annotations.ConfigDocDefault"; public static final String ANNOTATION_CONFIG_DOC_FILE_NAME = "io.quarkus.runtime.annotations.ConfigDocFilename"; public static final String ANNOTATION_CONFIG_DOC_PREFIX = "io.quarkus.runtime.annotations.ConfigDocPrefix"; + public static final String ANNOTATION_CONFIG_DOC_ENUM = "io.quarkus.runtime.annotations.ConfigDocEnum"; public static final String ANNOTATION_CONFIG_WITH_CONVERTER = "io.smallrye.config.WithConverter"; public static final String ANNOTATION_CONFIG_WITH_NAME = "io.smallrye.config.WithName"; diff --git a/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocEnum.java b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocEnum.java new file mode 100644 index 00000000000000..99d99559e95d1f --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocEnum.java @@ -0,0 +1,24 @@ +package io.quarkus.runtime.annotations; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Provides a way to configure how an enum is handled. + */ +@Retention(RUNTIME) +@Target({ FIELD, METHOD }) +@Documented +public @interface ConfigDocEnum { + + /** + * This can be used to enforce hyphenating the enum values even if a converter is present. + */ + boolean enforceHyphenateValues() default false; + +} From 56c712d15242e8bfda2198d6622c58a911ac7be3 Mon Sep 17 00:00:00 2001 From: Rolfe Dlugy-Hegwer Date: Tue, 13 Aug 2024 18:38:09 -0400 Subject: [PATCH 26/41] Remove quarkus-panache-common from docs --- docs/src/main/asciidoc/mongodb-panache.adoc | 31 --------------------- 1 file changed, 31 deletions(-) diff --git a/docs/src/main/asciidoc/mongodb-panache.adoc b/docs/src/main/asciidoc/mongodb-panache.adoc index 35c583a5d3354b..60ab35867ab0ec 100644 --- a/docs/src/main/asciidoc/mongodb-panache.adoc +++ b/docs/src/main/asciidoc/mongodb-panache.adoc @@ -90,37 +90,6 @@ If you don't want to generate a new project, add the dependency in your build fi implementation("io.quarkus:quarkus-mongodb-panache") ---- -[NOTE] -==== -If your project is already configured to use other annotation processors, you will need to additionally add the Panache annotation processor: - -[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] -.pom.xml ----- - - maven-compiler-plugin - ${compiler-plugin.version} - - ${maven.compiler.parameters} - - - - io.quarkus - quarkus-panache-common - ${quarkus.platform.version} - - - - ----- - -[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] -.build.gradle ----- -annotationProcessor("io.quarkus:quarkus-panache-common") ----- -==== - == Setting up and configuring MongoDB with Panache To get started: From 59bb9a8a789088c87e1e05e9a26dfc549b60bba7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 00:03:05 +0000 Subject: [PATCH 27/41] Bump wildfly-elytron.version from 2.5.0.Final to 2.5.1.Final Bumps `wildfly-elytron.version` from 2.5.0.Final to 2.5.1.Final. Updates `org.wildfly.security:wildfly-elytron` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-ssh-util` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-auth-server` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-password-impl` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-realm` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-realm-token` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-realm-jdbc` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-realm-ldap` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-ssl` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-sasl-plain` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-sasl-digest` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-sasl-external` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-sasl-oauth2` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-sasl-scram` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-x500-cert` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-x500-cert-acme` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-credential` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-sasl-gs2` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-asn1` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-sasl-gssapi` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-security-manager-action` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-auth` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-base` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-http` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-keystore` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-mechanism-digest` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-mechanism-gssapi` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-mechanism-oauth2` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-mechanism-scram` from 2.5.0.Final to 2.5.1.Final Updates `org.wildfly.security:wildfly-elytron-mechanism` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-permission` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-provider-util` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-sasl` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-util` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-x500-cert-util` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) Updates `org.wildfly.security:wildfly-elytron-x500` from 2.5.0.Final to 2.5.1.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.5.0.Final...2.5.1.Final) --- updated-dependencies: - dependency-name: org.wildfly.security:wildfly-elytron dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-ssh-util dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-auth-server dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-password-impl dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-realm dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-realm-token dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-realm-jdbc dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-realm-ldap dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-ssl dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-sasl-plain dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-sasl-digest dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-sasl-external dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-sasl-oauth2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-sasl-scram dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-x500-cert dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-x500-cert-acme dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-credential dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-sasl-gs2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-asn1 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-sasl-gssapi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-security-manager-action dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-auth dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-base dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-http dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-keystore dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-digest dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-gssapi dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-oauth2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-scram dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-mechanism dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-permission dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-provider-util dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-sasl dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-util dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-x500-cert-util dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.wildfly.security:wildfly-elytron-x500 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 5cb58d67e448c8..38f465b65c6fe3 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -110,7 +110,7 @@ 2.0.0.Final 1.7.0.Final 1.0.1.Final - 2.5.0.Final + 2.5.1.Final 2.1.4.SP1 3.6.1.Final 4.5.9 From 52742cb61160c12e34ba315fcd6efd1a8bd08dc8 Mon Sep 17 00:00:00 2001 From: Jonathan Kolberg Date: Tue, 13 Aug 2024 20:27:58 +0200 Subject: [PATCH 28/41] Improve Oidc documentation of dev service --- .../deployment/devservices/keycloak/DevServicesConfig.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java index 9f71002fb2e7fd..57a74047c38134 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java @@ -87,6 +87,9 @@ public class DevServicesConfig { * A comma-separated list of class or file system paths to Keycloak realm files. * This list is used to initialize Keycloak. * The first value in this list is used to initialize default tenant connection properties. + *

+ * To learn more about Keycloak realm files, consult the Importing + * and Exporting Keycloak Realms documentation. */ @ConfigItem public Optional> realmPath; From a1b3fa90fc920a1fd80ad0d1079f29625fa2226a Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 12 Aug 2024 18:07:37 +0200 Subject: [PATCH 29/41] Do not keep the whole Manifest in memory Some manifests are quite large, for instance the one for commons-codec is 21 KB. Given most of the information in the Manifest are of no interest to us, let's trim the Manifest to only the information we need. Also we still have the problem of us keeping the jars open multiple times in test and dev mode so it amplifies this issue. In some cases, I ended up with 1700+ jars opened so the Manifests add up quite quickly. --- .../java/io/quarkus/paths/EmptyPathTree.java | 3 +- .../java/io/quarkus/paths/FilePathTree.java | 3 +- .../io/quarkus/paths/FilteredPathTree.java | 5 +- .../io/quarkus/paths/ManifestAttributes.java | 72 +++++++++++++++++++ .../io/quarkus/paths/MultiRootPathTree.java | 5 +- .../main/java/io/quarkus/paths/PathTree.java | 7 +- .../quarkus/paths/PathTreeWithManifest.java | 22 +++--- .../quarkus/paths/SharedArchivePathTree.java | 5 +- .../bootstrap/app/CuratedApplication.java | 6 +- .../AbstractClassPathElement.java | 21 +++--- .../classloading/ClassPathElement.java | 6 +- .../FilteredClassPathElement.java | 6 +- .../PathTreeClassPathElement.java | 6 +- .../classloading/QuarkusClassLoader.java | 21 +++--- 14 files changed, 127 insertions(+), 61 deletions(-) create mode 100644 independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ManifestAttributes.java diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/EmptyPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/EmptyPathTree.java index d1efedc41dad6a..4f31e89c6189dd 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/EmptyPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/EmptyPathTree.java @@ -6,7 +6,6 @@ import java.util.Collections; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; public class EmptyPathTree implements OpenPathTree { @@ -22,7 +21,7 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { return null; } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilePathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilePathTree.java index 348b887742a272..cb80e315b59761 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilePathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilePathTree.java @@ -7,7 +7,6 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; class FilePathTree implements OpenPathTree { @@ -29,7 +28,7 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { return null; } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java index 47b03c66d80312..25de79a0eba9aa 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java @@ -6,7 +6,6 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; public class FilteredPathTree implements PathTree { @@ -24,8 +23,8 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { - return original.getManifest(); + public ManifestAttributes getManifestAttributes() { + return original.getManifestAttributes(); } @Override diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ManifestAttributes.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ManifestAttributes.java new file mode 100644 index 00000000000000..34716ebb6b2e66 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ManifestAttributes.java @@ -0,0 +1,72 @@ +package io.quarkus.paths; + +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * Manifests for some libraries can be quite large (e.g. for commons-codec, it is 21 KB). + *

+ * Given we keep a lot of ArchivePathTree around, it seems like a good idea to only + * keep around the Manifest entries that we actually use in Quarkus. + *

+ * This can be extended further in the future if we need more attributes. + */ +public class ManifestAttributes { + + private String specificationTitle; + private String specificationVersion; + private String specificationVendor; + + private String implementationTitle; + private String implementationVersion; + private String implementationVendor; + + private boolean multiRelease; + + public static ManifestAttributes of(Manifest manifest) { + if (manifest == null) { + return null; + } + + return new ManifestAttributes(manifest); + } + + private ManifestAttributes(Manifest manifest) { + specificationTitle = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_TITLE); + specificationVersion = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_VERSION); + specificationVendor = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_VENDOR); + implementationTitle = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_TITLE); + implementationVersion = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); + implementationVendor = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VENDOR); + + multiRelease = Boolean.parseBoolean(manifest.getMainAttributes().getValue(Attributes.Name.MULTI_RELEASE)); + } + + public String getSpecificationTitle() { + return specificationTitle; + } + + public String getSpecificationVersion() { + return specificationVersion; + } + + public String getSpecificationVendor() { + return specificationVendor; + } + + public String getImplementationTitle() { + return implementationTitle; + } + + public String getImplementationVersion() { + return implementationVersion; + } + + public String getImplementationVendor() { + return implementationVendor; + } + + public boolean isMultiRelease() { + return multiRelease; + } +} diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java index 99196623632508..f94b0bc9cc4f3f 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java @@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; public class MultiRootPathTree implements OpenPathTree { @@ -32,9 +31,9 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { for (PathTree tree : trees) { - final Manifest m = tree.getManifest(); + final ManifestAttributes m = tree.getManifestAttributes(); if (m != null) { return m; } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java index 92c288e10db04d..2b405e058012fc 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java @@ -7,7 +7,6 @@ import java.util.Collection; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; public interface PathTree { @@ -98,12 +97,12 @@ default boolean isEmpty() { } /** - * If {@code META-INF/MANIFEST.MF} found, reads it and returns an instance of {@link java.util.jar.Manifest}, - * otherwise returns null. + * If {@code META-INF/MANIFEST.MF} found, reads it and returns an instance of {@link ManifestAttributes}, + * a trimmed down version of the Manifest, otherwise returns null. * * @return parsed {@code META-INF/MANIFEST.MF} if it's found, otherwise {@code null} */ - Manifest getManifest(); + ManifestAttributes getManifestAttributes(); /** * Walks the tree. diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTreeWithManifest.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTreeWithManifest.java index 8576f4303a30ca..3b3608416f2d27 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTreeWithManifest.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTreeWithManifest.java @@ -42,7 +42,7 @@ public abstract class PathTreeWithManifest implements PathTree { protected boolean manifestEnabled; private final ReentrantReadWriteLock manifestInfoLock = new ReentrantReadWriteLock(); - private transient Manifest manifest; + private transient ManifestAttributes manifestAttributes; protected transient boolean manifestInitialized; protected volatile Map multiReleaseMapping; @@ -61,7 +61,7 @@ protected PathTreeWithManifest(boolean manifestEnabled) { protected PathTreeWithManifest(PathTreeWithManifest pathTreeWithManifest) { pathTreeWithManifest.manifestReadLock().lock(); try { - this.manifest = pathTreeWithManifest.manifest; + this.manifestAttributes = pathTreeWithManifest.manifestAttributes; this.manifestInitialized = pathTreeWithManifest.manifestInitialized; this.multiReleaseMapping = pathTreeWithManifest.multiReleaseMapping; } finally { @@ -78,12 +78,12 @@ public T apply(String relativePath, Function func) { protected abstract T apply(String relativePath, Function func, boolean manifestEnabled); @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { // Optimistically try with a lock that allows concurrent access first, for performance. manifestReadLock().lock(); try { if (manifestInitialized) { - return manifest; + return manifestAttributes; } } finally { manifestReadLock().unlock(); @@ -94,18 +94,18 @@ public Manifest getManifest() { if (manifestInitialized) { // Someone else got here between our call to manifestReadLock().unlock() // and our call to manifestWriteLock().lock(); it can happen. - return manifest; + return manifestAttributes; } final Manifest m = apply("META-INF/MANIFEST.MF", ManifestReader.INSTANCE, false); initManifest(m); } finally { manifestWriteLock().unlock(); } - return manifest; + return manifestAttributes; } protected void initManifest(Manifest m) { - manifest = m; + manifestAttributes = ManifestAttributes.of(m); manifestInitialized = true; } @@ -118,7 +118,9 @@ protected ReadLock manifestReadLock() { } public boolean isMultiReleaseJar() { - return isMultiReleaseJar(getManifest()); + ManifestAttributes manifestAttributes = getManifestAttributes(); + + return manifestAttributes != null && manifestAttributes.isMultiRelease(); } protected Map getMultiReleaseMapping() { @@ -144,10 +146,6 @@ protected String toMultiReleaseRelativePath(String relativePath) { return getMultiReleaseMapping().getOrDefault(relativePath, relativePath); } - private static boolean isMultiReleaseJar(final Manifest m) { - return m != null && Boolean.parseBoolean(m.getMainAttributes().getValue("Multi-Release")); - } - private static class MultiReleaseMappingReader implements Function> { private static final MultiReleaseMappingReader INSTANCE = new MultiReleaseMappingReader(); diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java index a8cc3f4b419628..25129119012d52 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java @@ -10,7 +10,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; /** * While {@link ArchivePathTree} implementation is thread-safe, this implementation @@ -144,8 +143,8 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { - return delegate.getManifest(); + public ManifestAttributes getManifestAttributes() { + return delegate.getManifestAttributes(); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java index 7b66d6c8564135..89d54ad91fb0c7 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java @@ -15,7 +15,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; import java.util.stream.Collectors; import io.quarkus.bootstrap.classloading.ClassPathElement; @@ -27,6 +26,7 @@ import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.DependencyFlags; import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.ManifestAttributes; import io.quarkus.paths.OpenPathTree; import io.quarkus.paths.PathTree; @@ -499,8 +499,8 @@ public ProtectionDomain getProtectionDomain() { } @Override - public Manifest getManifest() { - return delegate.getManifest(); + public ManifestAttributes getManifestAttributes() { + return delegate.getManifestAttributes(); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/AbstractClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/AbstractClassPathElement.java index d2a2faed0a8359..be85f0786cfd8e 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/AbstractClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/AbstractClassPathElement.java @@ -2,37 +2,40 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.jar.Manifest; import org.jboss.logging.Logger; +import io.quarkus.paths.ManifestAttributes; + public abstract class AbstractClassPathElement implements ClassPathElement { private static final Logger log = Logger.getLogger(AbstractClassPathElement.class); - private volatile Manifest manifest; + private volatile ManifestAttributes manifestAttributes; private volatile boolean manifestInitialized = false; @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { if (manifestInitialized) { - return manifest; + return manifestAttributes; } synchronized (this) { if (manifestInitialized) { - return manifest; + return manifestAttributes; } - manifest = readManifest(); + manifestAttributes = readManifest(); manifestInitialized = true; } - return manifest; + return manifestAttributes; } - protected Manifest readManifest() { + protected ManifestAttributes readManifest() { final ClassPathResource mf = getResource("META-INF/MANIFEST.MF"); if (mf != null) { - try { - return new Manifest(new ByteArrayInputStream(mf.getData())); + try (InputStream manifestIs = new ByteArrayInputStream(mf.getData())) { + return ManifestAttributes.of(new Manifest(manifestIs)); } catch (IOException e) { log.warnf("Failed to parse manifest for %s", toString(), e); } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java index 0e319d437e7b77..96c6689a4816d1 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java @@ -7,11 +7,11 @@ import java.util.List; import java.util.Set; import java.util.function.Function; -import java.util.jar.Manifest; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.ResolvedDependency; import io.quarkus.paths.EmptyPathTree; +import io.quarkus.paths.ManifestAttributes; import io.quarkus.paths.OpenPathTree; import io.quarkus.paths.PathTree; @@ -80,7 +80,7 @@ default ResolvedDependency getResolvedDependency() { */ ProtectionDomain getProtectionDomain(); - Manifest getManifest(); + ManifestAttributes getManifestAttributes(); /** * Checks whether this is a runtime classpath element @@ -133,7 +133,7 @@ public ProtectionDomain getProtectionDomain() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { return null; } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/FilteredClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/FilteredClassPathElement.java index 3e6f9a897d2771..a8dbbd2d06ec7a 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/FilteredClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/FilteredClassPathElement.java @@ -7,9 +7,9 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Function; -import java.util.jar.Manifest; import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.ManifestAttributes; import io.quarkus.paths.OpenPathTree; public class FilteredClassPathElement implements ClassPathElement { @@ -63,9 +63,9 @@ public ProtectionDomain getProtectionDomain() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { //we don't support filtering the manifest - return delegate.getManifest(); + return delegate.getManifestAttributes(); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java index a1da4f0e741149..8126c264dbb430 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java @@ -21,11 +21,11 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; -import java.util.jar.Manifest; import org.jboss.logging.Logger; import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.ManifestAttributes; import io.quarkus.paths.OpenPathTree; import io.quarkus.paths.PathTree; import io.quarkus.paths.PathVisit; @@ -176,8 +176,8 @@ public void visitPath(PathVisit visit) { } @Override - protected Manifest readManifest() { - return apply(OpenPathTree::getManifest); + protected ManifestAttributes readManifest() { + return apply(OpenPathTree::getManifestAttributes); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java index 93b862d27166ba..e48f65016950c2 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java @@ -26,11 +26,11 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.jar.Attributes; -import java.util.jar.Manifest; import org.jboss.logging.Logger; +import io.quarkus.paths.ManifestAttributes; + /** * The ClassLoader used for non production Quarkus applications (i.e. dev and test mode). */ @@ -550,15 +550,14 @@ private void definePackage(String name, ClassPathElement classPathElement) { if ((pkgName != null) && definedPackages.get(pkgName) == null) { synchronized (getClassLoadingLock(pkgName)) { if (definedPackages.get(pkgName) == null) { - Manifest mf = classPathElement.getManifest(); - if (mf != null) { - Attributes ma = mf.getMainAttributes(); - definedPackages.put(pkgName, definePackage(pkgName, ma.getValue(Attributes.Name.SPECIFICATION_TITLE), - ma.getValue(Attributes.Name.SPECIFICATION_VERSION), - ma.getValue(Attributes.Name.SPECIFICATION_VENDOR), - ma.getValue(Attributes.Name.IMPLEMENTATION_TITLE), - ma.getValue(Attributes.Name.IMPLEMENTATION_VERSION), - ma.getValue(Attributes.Name.IMPLEMENTATION_VENDOR), null)); + ManifestAttributes manifest = classPathElement.getManifestAttributes(); + if (manifest != null) { + definedPackages.put(pkgName, definePackage(pkgName, manifest.getSpecificationTitle(), + manifest.getSpecificationVersion(), + manifest.getSpecificationVendor(), + manifest.getImplementationTitle(), + manifest.getImplementationVersion(), + manifest.getImplementationVendor(), null)); return; } From 6780c2c17788d59e30ff50238224416cd62786bb Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 12 Aug 2024 21:46:13 +0200 Subject: [PATCH 30/41] Make sure we don't keep a reference to the ZipPath in ArchivePathTree A Path can actually contain references to the filesystem: this is the case of the ZipPath. We want to make sure we don't keep ZipFileSystem around when a path tree is closed so we also need to nullify the path. Finally, we make sure only DirectoryPathTree is marked as Serializable as we only serialize the workspace directories. --- .../io/quarkus/paths/ArchivePathTree.java | 54 ++--- .../io/quarkus/paths/DirectoryPathTree.java | 155 ++------------ .../quarkus/paths/OpenContainerPathTree.java | 189 ++++++++++++++++++ .../main/java/io/quarkus/paths/PathTree.java | 3 + .../quarkus/paths/SharedArchivePathTree.java | 4 +- 5 files changed, 236 insertions(+), 169 deletions(-) create mode 100644 independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/OpenContainerPathTree.java diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ArchivePathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ArchivePathTree.java index f4c094678c8797..ff9054e17f5c5f 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ArchivePathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ArchivePathTree.java @@ -224,15 +224,30 @@ public boolean equals(Object obj) { && manifestEnabled == other.manifestEnabled; } - protected class OpenArchivePathTree extends DirectoryPathTree { + protected class OpenArchivePathTree extends OpenContainerPathTree { - // we don't make the field final as we want to nullify it on close - private volatile FileSystem fs; private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + // we don't make these fields final as we want to nullify them on close + private FileSystem fs; + private Path rootPath; + + private volatile boolean open = true; + protected OpenArchivePathTree(FileSystem fs) { - super(fs.getPath("/"), pathFilter, ArchivePathTree.this); + super(ArchivePathTree.this.pathFilter, ArchivePathTree.this); this.fs = fs; + this.rootPath = fs.getPath("/"); + } + + @Override + protected Path getContainerPath() { + return ArchivePathTree.this.archive; + } + + @Override + protected Path getRootPath() { + return rootPath; } protected ReentrantReadWriteLock.ReadLock readLock() { @@ -272,7 +287,7 @@ protected void initMultiReleaseMapping(Map mrMapping) { public boolean isOpen() { lock.readLock().lock(); try { - return fs != null && fs.isOpen(); + return open; } finally { lock.readLock().unlock(); } @@ -349,7 +364,7 @@ public Path getPath(String relativePath) { */ private void ensureOpen() { // let's not use isOpen() as ensureOpen() is always used inside a read lock - if (fs != null && fs.isOpen()) { + if (open) { return; } throw new RuntimeException("Failed to access " + ArchivePathTree.this.getRoots() @@ -358,28 +373,19 @@ private void ensureOpen() { @Override public void close() throws IOException { - Throwable t = null; lock.writeLock().lock(); try { - super.close(); - } catch (Throwable e) { - t = e; + open = false; + rootPath = null; + fs.close(); + } catch (IOException e) { throw e; } finally { - try { - fs.close(); - } catch (IOException e) { - if (t != null) { - e.addSuppressed(t); - } - throw e; - } finally { - // even when we close the fs, everything is kept as is in the fs instance - // and typically the cen, which is quite large - // let's make sure the fs is nullified for it to be garbage collected - fs = null; - lock.writeLock().unlock(); - } + // even when we close the fs, everything is kept as is in the fs instance + // and typically the cen, which is quite large + // let's make sure the fs is nullified for it to be garbage collected + fs = null; + lock.writeLock().unlock(); } } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/DirectoryPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/DirectoryPathTree.java index 14ceb795ca6a1e..b73d8acc1613d4 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/DirectoryPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/DirectoryPathTree.java @@ -2,55 +2,14 @@ import java.io.IOException; import java.io.Serializable; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.regex.Pattern; -public class DirectoryPathTree extends PathTreeWithManifest implements OpenPathTree, Serializable { +public class DirectoryPathTree extends OpenContainerPathTree implements Serializable { private static final long serialVersionUID = 2255956884896445059L; - private static final boolean USE_WINDOWS_ABSOLUTE_PATH_PATTERN = !FileSystems.getDefault().getSeparator().equals("/"); - - private static volatile Pattern windowsAbsolutePathPattern; - - private static Pattern windowsAbsolutePathPattern() { - return windowsAbsolutePathPattern == null ? windowsAbsolutePathPattern = Pattern.compile("[a-zA-Z]:\\\\.*") - : windowsAbsolutePathPattern; - } - - static boolean isAbsolutePath(String path) { - return path != null && !path.isEmpty() - && (path.charAt(0) == '/' // we want to check for '/' on every OS - || USE_WINDOWS_ABSOLUTE_PATH_PATTERN - && (windowsAbsolutePathPattern().matcher(path).matches()) - || path.startsWith(FileSystems.getDefault().getSeparator())); - } - - static void ensureResourcePath(FileSystem fs, String path) { - if (isAbsolutePath(path)) { - throw new IllegalArgumentException("Expected a path relative to the root of the path tree but got " + path); - } - // this is to disallow reading outside the path tree root - if (path != null && path.contains("..")) { - for (Path pathElement : fs.getPath(path)) { - if (pathElement.toString().equals("..")) { - throw new IllegalArgumentException("'..' cannot be used in resource paths, but got " + path); - } - } - } - } - private Path dir; - private PathFilter pathFilter; /** * For deserialization @@ -68,90 +27,37 @@ public DirectoryPathTree(Path dir, PathFilter pathFilter) { } public DirectoryPathTree(Path dir, PathFilter pathFilter, boolean manifestEnabled) { - super(manifestEnabled); + super(pathFilter, manifestEnabled); this.dir = dir; - this.pathFilter = pathFilter; } protected DirectoryPathTree(Path dir, PathFilter pathFilter, PathTreeWithManifest pathTreeWithManifest) { - super(pathTreeWithManifest); + super(pathFilter, pathTreeWithManifest); this.dir = dir; - this.pathFilter = pathFilter; } @Override - public Collection getRoots() { - return List.of(dir); + protected Path getRootPath() { + return dir; } @Override - public void walk(PathVisitor visitor) { - PathTreeVisit.walk(dir, dir, dir, pathFilter, getMultiReleaseMapping(), visitor); + protected Path getContainerPath() { + return dir; } @Override - public void walkIfContains(String relativePath, PathVisitor visitor) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - return; - } - final Path walkDir = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - if (!Files.exists(walkDir)) { - return; - } - PathTreeVisit.walk(dir, dir, walkDir, pathFilter, getMultiReleaseMapping(), visitor); - } - - private void ensureResourcePath(String path) { - ensureResourcePath(dir.getFileSystem(), path); - } - - @Override - protected T apply(String relativePath, Function func, boolean manifestEnabled) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - return func.apply(null); - } - final Path path = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - if (!Files.exists(path)) { - return func.apply(null); - } - return PathTreeVisit.process(dir, dir, path, pathFilter, func); - } - - @Override - public void accept(String relativePath, Consumer consumer) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - consumer.accept(null); - return; - } - final Path path = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - if (!Files.exists(path)) { - consumer.accept(null); - return; - } - PathTreeVisit.consume(dir, dir, path, pathFilter, consumer); + public boolean isOpen() { + return true; } @Override - public boolean contains(String relativePath) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - return false; - } - final Path path = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - return Files.exists(path); + public void close() throws IOException { } @Override - public Path getPath(String relativePath) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - return null; - } - final Path path = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - return Files.exists(path) ? path : null; + public PathTree getOriginalTree() { + return this; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { @@ -165,41 +71,4 @@ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassN pathFilter = (PathFilter) in.readObject(); manifestEnabled = in.readBoolean(); } - - @Override - public OpenPathTree open() { - return this; - } - - @Override - public boolean isOpen() { - return true; - } - - @Override - public void close() throws IOException { - } - - @Override - public PathTree getOriginalTree() { - return this; - } - - @Override - public int hashCode() { - return Objects.hash(dir, pathFilter, manifestEnabled); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DirectoryPathTree other = (DirectoryPathTree) obj; - return Objects.equals(dir, other.dir) && Objects.equals(pathFilter, other.pathFilter) - && manifestEnabled == other.manifestEnabled; - } } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/OpenContainerPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/OpenContainerPathTree.java new file mode 100644 index 00000000000000..ecdf1ba10e4833 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/OpenContainerPathTree.java @@ -0,0 +1,189 @@ +package io.quarkus.paths; + +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.regex.Pattern; + +public abstract class OpenContainerPathTree extends PathTreeWithManifest implements OpenPathTree { + + private static final boolean USE_WINDOWS_ABSOLUTE_PATH_PATTERN = !FileSystems.getDefault().getSeparator().equals("/"); + + private static volatile Pattern windowsAbsolutePathPattern; + + private static Pattern windowsAbsolutePathPattern() { + return windowsAbsolutePathPattern == null ? windowsAbsolutePathPattern = Pattern.compile("[a-zA-Z]:\\\\.*") + : windowsAbsolutePathPattern; + } + + static boolean isAbsolutePath(String path) { + return path != null && !path.isEmpty() + && (path.charAt(0) == '/' // we want to check for '/' on every OS + || USE_WINDOWS_ABSOLUTE_PATH_PATTERN + && (windowsAbsolutePathPattern().matcher(path).matches()) + || path.startsWith(FileSystems.getDefault().getSeparator())); + } + + static void ensureResourcePath(FileSystem fs, String path) { + if (isAbsolutePath(path)) { + throw new IllegalArgumentException("Expected a path relative to the root of the path tree but got " + path); + } + // this is to disallow reading outside the path tree root + if (path != null && path.contains("..")) { + for (Path pathElement : fs.getPath(path)) { + if (pathElement.toString().equals("..")) { + throw new IllegalArgumentException("'..' cannot be used in resource paths, but got " + path); + } + } + } + } + + protected PathFilter pathFilter; + + /** + * For deserialization of DirectoryPathTree + */ + public OpenContainerPathTree() { + } + + protected OpenContainerPathTree(PathFilter pathFilter) { + this(pathFilter, false); + } + + protected OpenContainerPathTree(PathFilter pathFilter, boolean manifestEnabled) { + super(manifestEnabled); + this.pathFilter = pathFilter; + } + + protected OpenContainerPathTree(PathFilter pathFilter, PathTreeWithManifest pathTreeWithManifest) { + super(pathTreeWithManifest); + this.pathFilter = pathFilter; + } + + /** + * This is the path to the container. + *

+ * In the case of a zip archive, it's the path of the archive. + * In the case of a directory, it's the directory. + *

+ * Should only be used for equals/hashCode. + */ + protected abstract Path getContainerPath(); + + /** + * This is the path to the container. + *

+ * In the case of a zip archive, it's the path to the root of the archive (i.e. a ZipPath). + * In the case of a directory, it's the directory. + *

+ * Should be used for any read operation on the container. + */ + protected abstract Path getRootPath(); + + @Override + public OpenPathTree open() { + return this; + } + + @Override + public Collection getRoots() { + return List.of(getContainerPath()); + } + + @Override + public void walk(PathVisitor visitor) { + PathTreeVisit.walk(getRootPath(), getRootPath(), getRootPath(), pathFilter, getMultiReleaseMapping(), + visitor); + } + + @Override + public void walkIfContains(String relativePath, PathVisitor visitor) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + return; + } + final Path walkDir = getRootPath() + .resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + if (!Files.exists(walkDir)) { + return; + } + PathTreeVisit.walk(getRootPath(), getRootPath(), walkDir, pathFilter, getMultiReleaseMapping(), visitor); + } + + private void ensureResourcePath(String path) { + ensureResourcePath(getRootPath().getFileSystem(), path); + } + + @Override + protected T apply(String relativePath, Function func, boolean manifestEnabled) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + return func.apply(null); + } + final Path path = getRootPath().resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + if (!Files.exists(path)) { + return func.apply(null); + } + return PathTreeVisit.process(getRootPath(), getRootPath(), path, pathFilter, func); + } + + @Override + public void accept(String relativePath, Consumer consumer) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + consumer.accept(null); + return; + } + final Path path = getRootPath().resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + if (!Files.exists(path)) { + consumer.accept(null); + return; + } + PathTreeVisit.consume(getRootPath(), getRootPath(), path, pathFilter, consumer); + } + + @Override + public boolean contains(String relativePath) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + return false; + } + final Path path = getRootPath().resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + return Files.exists(path); + } + + @Override + public Path getPath(String relativePath) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + return null; + } + final Path path = getRootPath().resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + return Files.exists(path) ? path : null; + } + + @Override + public int hashCode() { + return Objects.hash(getContainerPath(), pathFilter, manifestEnabled); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + OpenContainerPathTree other = (OpenContainerPathTree) obj; + return Objects.equals(getContainerPath(), other.getContainerPath()) + && Objects.equals(pathFilter, other.pathFilter) + && manifestEnabled == other.manifestEnabled; + } +} diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java index 2b405e058012fc..f8e4280b4f4632 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java @@ -82,6 +82,9 @@ static PathTree ofArchive(Path archive, PathFilter filter) { /** * The roots of the path tree. + *

+ * Note that for archives, it will return the path to the archive itself, + * not a path that you can browse. * * @return roots of the path tree */ diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java index 25129119012d52..0ea0048549c8be 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java @@ -57,7 +57,7 @@ public OpenPathTree open() { return new CallerOpenPathTree(lastOpen); } try { - this.lastOpen = new SharedOpenArchivePathTree(openFs()); + this.lastOpen = new SharedOpenArchivePathTree(archive, openFs()); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -68,7 +68,7 @@ private class SharedOpenArchivePathTree extends OpenArchivePathTree { private final AtomicInteger users = new AtomicInteger(1); - protected SharedOpenArchivePathTree(FileSystem fs) { + protected SharedOpenArchivePathTree(Path archivePath, FileSystem fs) { super(fs); openCount.incrementAndGet(); } From d7d04d32b012f175f0ece93ffc7fdd82e591969d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDan=20O=C5=BEbot?= Date: Wed, 14 Aug 2024 10:40:17 +0200 Subject: [PATCH 31/41] Implement docker-compose file strategy --- .../quarkus/project/quarkus/codestart.yml | 1 + .../CodestartFileStrategyHandler.java | 3 +- ...erComposeCodestartFileStrategyHandler.java | 57 +++++++++++++++++++ .../core/CodestartProcessorTest.java | 3 + 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/DockerComposeCodestartFileStrategyHandler.java diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/codestart.yml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/codestart.yml index 2822b07c03a855..4385f60945648a 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/codestart.yml +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/codestart.yml @@ -8,6 +8,7 @@ output-strategy: "README.adoc": forbidden "readme.adoc": forbidden ".gitignore": append + "docker-compose-include-*": docker-compose-includes "src/main/resources/META-INF/resources/index.html": content-merge "src/main/resources/application.yml": smart-config-merge "src/main/resources/application.properties": forbidden diff --git a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategyHandler.java b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategyHandler.java index 56af40804f4e11..1883e850ceadaf 100644 --- a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategyHandler.java +++ b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategyHandler.java @@ -27,7 +27,8 @@ public interface CodestartFileStrategyHandler { new ForbiddenCodestartFileStrategyHandler(), new SmartConfigMergeCodestartFileStrategyHandler(), new SmartPomMergeCodestartFileStrategyHandler(), - new SmartPackageFileStrategyHandler()) + new SmartPackageFileStrategyHandler(), + new DockerComposeCodestartFileStrategyHandler()) .collect(Collectors.toMap(CodestartFileStrategyHandler::name, Function.identity())); String name(); diff --git a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/DockerComposeCodestartFileStrategyHandler.java b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/DockerComposeCodestartFileStrategyHandler.java new file mode 100644 index 00000000000000..f14c2448b76b4e --- /dev/null +++ b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/DockerComposeCodestartFileStrategyHandler.java @@ -0,0 +1,57 @@ +package io.quarkus.devtools.codestarts.core.strategy; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import io.quarkus.devtools.codestarts.CodestartStructureException; +import io.quarkus.devtools.codestarts.core.reader.TargetFile; + +public class DockerComposeCodestartFileStrategyHandler implements CodestartFileStrategyHandler { + + static final String NAME = "docker-compose-includes"; + + final String INCLUDE_LINE_IDENTIFIER = "include:\n"; + + @Override + public String name() { + return NAME; + } + + @Override + @SuppressWarnings("unchecked") + public void process(Path targetDirectory, String relativePath, List codestartFiles, Map data) + throws IOException { + List includes = (List) data.getOrDefault(NAME, new ArrayList()); + + for (TargetFile targetFile : codestartFiles) { + String filename = targetFile.getSourceName(); + writeFile(targetDirectory.resolve(filename), targetFile.getContent()); + + includes.add(filename); + } + data.put(NAME, includes); + + StringBuilder content = new StringBuilder(INCLUDE_LINE_IDENTIFIER); + for (String include : includes) { + content.append(" - ").append(include).append("\n"); + } + + final Path targetPath = targetDirectory.resolve("docker-compose.yml"); + writeFile(targetPath, content.toString()); + } + + @Override + public void writeFile(final Path targetPath, final String content) throws IOException { + if (Files.exists(targetPath) && !Files.readString(targetPath).startsWith(INCLUDE_LINE_IDENTIFIER)) { + throw new CodestartStructureException( + "Target file already exists: " + targetPath.toString()); + } + + Files.writeString(targetPath, content); + } + +} diff --git a/independent-projects/tools/codestarts/src/test/java/io/quarkus/devtools/codestarts/core/CodestartProcessorTest.java b/independent-projects/tools/codestarts/src/test/java/io/quarkus/devtools/codestarts/core/CodestartProcessorTest.java index 3fc71c03dea020..539a2aace8c4fe 100644 --- a/independent-projects/tools/codestarts/src/test/java/io/quarkus/devtools/codestarts/core/CodestartProcessorTest.java +++ b/independent-projects/tools/codestarts/src/test/java/io/quarkus/devtools/codestarts/core/CodestartProcessorTest.java @@ -19,6 +19,7 @@ void checkSelectedDefaultStrategy() { Map spec = new HashMap<>(); spec.put("test/foo.tt", "forbidden"); spec.put("*", "replace"); + spec.put("docker-compose-include-test.yml", "docker-compose-includes"); final CodestartProcessor processor = new CodestartProcessor( MessageWriter.info(), @@ -29,6 +30,8 @@ void checkSelectedDefaultStrategy() { assertThat(processor.getSelectedDefaultStrategy()).isEqualTo(CodestartFileStrategyHandler.BY_NAME.get("replace")); assertThat(processor.getStrategy("test/foo.tt")).hasValue(CodestartFileStrategyHandler.BY_NAME.get("forbidden")); + assertThat(processor.getStrategy("docker-compose-include-test.yml")) + .hasValue(CodestartFileStrategyHandler.BY_NAME.get("docker-compose-includes")); } @Test From 43a3ccbc74f1c89150235dead55d2d031bcbfe40 Mon Sep 17 00:00:00 2001 From: Holly Cummins Date: Wed, 14 Aug 2024 10:18:52 +0100 Subject: [PATCH 32/41] Correct indendation so metadata is in the metadata block --- .../src/main/asciidoc/extension-metadata.adoc | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/src/main/asciidoc/extension-metadata.adoc b/docs/src/main/asciidoc/extension-metadata.adoc index 3c66b792d8ad25..220ff69f480332 100644 --- a/docs/src/main/asciidoc/extension-metadata.adoc +++ b/docs/src/main/asciidoc/extension-metadata.adoc @@ -64,6 +64,9 @@ And here is the final version of the file included in the runtime JAR augmented ---- name: "Quarkus REST (formerly RESTEasy Reactive)" artifact: "io.quarkus:quarkus-rest:999-SNAPSHOT" +description: "A Jakarta REST implementation utilizing build time processing and Vert.x.\ + \ This extension is not compatible with the quarkus-resteasy extension, or any of\ + \ the extensions that depend on it." <1> metadata: short-name: "rest" keywords: @@ -84,13 +87,13 @@ metadata: artifact: "io.quarkus:quarkus-project-core-extension-codestarts::jar:999-SNAPSHOT" config: - "quarkus.rest." - built-with-quarkus-core: "3.8.5" <1> - requires-quarkus-core: "[3.8,)" <2> - capabilities: <3> + built-with-quarkus-core: "3.8.5" <2> + requires-quarkus-core: "[3.8,)" <3> + capabilities: <4> provides: - "io.quarkus.rest" - "io.quarkus.resteasy.reactive" - extension-dependencies: <4> + extension-dependencies: <5> - "io.quarkus:quarkus-rest-common" - "io.quarkus:quarkus-mutiny" - "io.quarkus:quarkus-smallrye-context-propagation" @@ -100,21 +103,18 @@ metadata: - "io.quarkus:quarkus-vertx-http" - "io.quarkus:quarkus-core" - "io.quarkus:quarkus-jsonp" -description: "A Jakarta REST implementation utilizing build time processing and Vert.x.\ - \ This extension is not compatible with the quarkus-resteasy extension, or any of\ - \ the extensions that depend on it." <5> -scm-url: "https://github.com/quarkusio/quarkus" <6> -sponsor: A Sponsoring Organisation <7> + scm-url: "https://github.com/quarkusio/quarkus" <6> + sponsor: A Sponsoring Organisation <7> ---- - -<1> Quarkus version the extension was built with -<2> The Quarkus version range this extension requires. Optional, and will be set automatically by using the `built-with-quarkus-core` as the minimum range. -<3> https://quarkus.io/guides/capabilities[Capabilities] this extension provides -<4> Direct dependencies on other extensions -<5> Description that can be displayed to users. In this case, the description was copied from the `pom.xml` of the extension module but it could also be provided in the template file. +<1> Description that can be displayed to users. In this case, the description was copied from the `pom.xml` of the extension module but it could also be provided in the template file. +<2> Quarkus version the extension was built with +<3> The Quarkus version range this extension requires. Optional, and will be set automatically by using the `built-with-quarkus-core` as the minimum range. +<4> https://quarkus.io/guides/capabilities[Capabilities] this extension provides +<5> Direct dependencies on other extensions <6> The source code repository of this extension. Optional, and will often be set automatically by using the `` information in the pom. In GitHub Actions builds, it will be inferred from the CI environment. For other GitHub repositories, it can be controlled by setting a `GITHUB_REPOSITORY` environment variable. <7> The sponsor(s) of this extension. Optional, and will sometimes be determined automatically from commit history. + [[quarkus-extension-properties]] == META-INF/quarkus-extension.properties From 68dbddbff3d375316d4a3d9e515184daf86f9c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarren=CC=83o?= Date: Wed, 14 Aug 2024 06:34:59 +0200 Subject: [PATCH 33/41] Handle GraalVM EA versions and update GraalVM version mapping * EA versions can result from quick builds running `mx build` on substratevm and using labs JDK EA builds. * Update GraalVM version mapping according to Christian Wimmer's latest information. He indicated that upcoming JDK 24 will be GraalVM 24.2, and then by JDK 25 version numbers will lineup. --- .../quarkus/deployment/pkg/steps/GraalVM.java | 33 ++++++++++++------- .../deployment/pkg/steps/GraalVMTest.java | 13 ++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/GraalVM.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/GraalVM.java index 734575f2f0333b..2aa16d5165c0c7 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/GraalVM.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/GraalVM.java @@ -15,6 +15,7 @@ public final class GraalVM { // Implements version parsing after https://github.com/oracle/graal/pull/6302 static final class VersionParseHelper { + private static final String EA_BUILD_PREFIX = "-ea"; private static final String JVMCI_BUILD_PREFIX = "jvmci-"; private static final String MANDREL_VERS_PREFIX = "Mandrel-"; @@ -103,11 +104,10 @@ private static String libericaVersion(String vendorVersion) { if (vendorVersion == null) { return null; } - int idx = vendorVersion.indexOf(LIBERICA_NIK_VERS_PREFIX); - if (idx < 0) { + final String version = buildVersion(vendorVersion, LIBERICA_NIK_VERS_PREFIX); + if (version == null) { return null; } - String version = vendorVersion.substring(idx + LIBERICA_NIK_VERS_PREFIX.length()); return matchVersion(version); } @@ -122,11 +122,10 @@ private static String mandrelVersion(String vendorVersion) { if (vendorVersion == null) { return null; } - int idx = vendorVersion.indexOf(MANDREL_VERS_PREFIX); - if (idx < 0) { + final String version = buildVersion(vendorVersion, MANDREL_VERS_PREFIX); + if (version == null) { return null; } - String version = vendorVersion.substring(idx + MANDREL_VERS_PREFIX.length()); return matchVersion(version); } @@ -142,11 +141,13 @@ private static String graalVersion(String buildInfo, int jdkFeature) { if (buildInfo == null) { return null; } - int idx = buildInfo.indexOf(JVMCI_BUILD_PREFIX); - if (idx < 0) { - return null; + String version = buildVersion(buildInfo, JVMCI_BUILD_PREFIX); + if (version == null) { + version = buildVersion(buildInfo, EA_BUILD_PREFIX); + if (version == null) { + return null; + } } - String version = buildInfo.substring(idx + JVMCI_BUILD_PREFIX.length()); Matcher versMatcher = VERSION_PATTERN.matcher(version); if (versMatcher.find()) { return matchVersion(version); @@ -154,6 +155,14 @@ private static String graalVersion(String buildInfo, int jdkFeature) { return GRAAL_MAPPING.get(jdkFeature); } } + + private static String buildVersion(String buildInfo, String buildPrefix) { + int idx = buildInfo.indexOf(buildPrefix); + if (idx < 0) { + return null; + } + return buildInfo.substring(idx + buildPrefix.length()); + } } // Temporarily work around https://github.com/quarkusio/quarkus/issues/36246, @@ -161,8 +170,8 @@ private static String graalVersion(String buildInfo, int jdkFeature) { // https://github.com/quarkusio/quarkus/issues/34161 private static final Map GRAAL_MAPPING = Map.of(22, "24.0", 23, "24.1", - 24, "25.0", - 25, "25.1"); + 24, "24.2", + 25, "25.0"); public static final class Version implements Comparable { diff --git a/core/deployment/src/test/java/io/quarkus/deployment/pkg/steps/GraalVMTest.java b/core/deployment/src/test/java/io/quarkus/deployment/pkg/steps/GraalVMTest.java index 9af2755056560e..647bd3a28474ba 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/pkg/steps/GraalVMTest.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/pkg/steps/GraalVMTest.java @@ -165,6 +165,19 @@ public void testGraalVMEE22DevVersionParser() { assertThat(graalVMEE22Dev.javaVersion.update()).isEqualTo(0); } + @Test + public void testGraalVMEA24DevVersionParser() { + final Version graalVMEA24Dev = Version.of(Stream.of(("native-image 24-ea 2025-03-18\n" + + "OpenJDK Runtime Environment Oracle GraalVM 24-dev.ea+10.1 (build 24-ea+10-1076)\n" + + "OpenJDK 64-Bit Server VM Oracle GraalVM 24-dev.ea+10.1 (build 24-ea+10-1076, mixed mode, sharing)") + .split("\\n"))); + assertThat(graalVMEA24Dev.distribution.name()).isEqualTo("GRAALVM"); + assertThat(graalVMEA24Dev.getVersionAsString()).isEqualTo("24.2-dev"); + assertThat(graalVMEA24Dev.javaVersion.toString()).isEqualTo("24-ea+10-1076"); + assertThat(graalVMEA24Dev.javaVersion.feature()).isEqualTo(24); + assertThat(graalVMEA24Dev.javaVersion.update()).isEqualTo(0); + } + @Test public void testGraalVMVersionsOlderThan() { assertOlderThan("GraalVM Version 19.3.6 CE", "GraalVM Version 20.2.0 (Java Version 11.0.9)"); From 1fd2a9a22d7ac265c89766ef81daf603e72d9b3b Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Wed, 14 Aug 2024 11:56:13 +0200 Subject: [PATCH 34/41] Redis cache: make blocking executions unordered When the Redis cache is invoked from an ordered Vert.x blocking execution, which happens for example with SmallRye GraphQL or with chained caching (one blocking `@CacheResult` method invoking other blocking `@CacheResult` method), the Redis cache ends up hanging. This is because the next execution cannot start until the previous execution finishes, but the previous execution waits for the next execution. This commit fixes that by making the Redis cache blocking executions unordered. --- .../deployment/ChainedRedisCacheTest.java | 49 +++++++++++++++++++ .../cache/redis/runtime/RedisCacheImpl.java | 6 +-- 2 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 extensions/redis-cache/deployment/src/test/java/io/quarkus/cache/redis/deployment/ChainedRedisCacheTest.java diff --git a/extensions/redis-cache/deployment/src/test/java/io/quarkus/cache/redis/deployment/ChainedRedisCacheTest.java b/extensions/redis-cache/deployment/src/test/java/io/quarkus/cache/redis/deployment/ChainedRedisCacheTest.java new file mode 100644 index 00000000000000..c7cdd51420da0f --- /dev/null +++ b/extensions/redis-cache/deployment/src/test/java/io/quarkus/cache/redis/deployment/ChainedRedisCacheTest.java @@ -0,0 +1,49 @@ +package io.quarkus.cache.redis.deployment; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.cache.CacheResult; +import io.quarkus.redis.datasource.RedisDataSource; +import io.quarkus.test.QuarkusUnitTest; + +public class ChainedRedisCacheTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClasses(ChainedCachedService.class, TestUtil.class)); + + @Inject + ChainedCachedService chainedCachedService; + + @Test + public void test() { + RedisDataSource redisDataSource = Arc.container().select(RedisDataSource.class).get(); + List allKeysAtStart = TestUtil.allRedisKeys(redisDataSource); + + assertEquals("fubar:42", chainedCachedService.cache1("fubar")); + + List allKeysAtEnd = TestUtil.allRedisKeys(redisDataSource); + assertEquals(allKeysAtStart.size() + 2, allKeysAtEnd.size()); + } + + @ApplicationScoped + public static class ChainedCachedService { + @CacheResult(cacheName = "cache1") + public String cache1(String key) { + return key + ":" + cache2(42); + } + + @CacheResult(cacheName = "cache2") + public int cache2(int value) { + return value; + } + } +} diff --git a/extensions/redis-cache/runtime/src/main/java/io/quarkus/cache/redis/runtime/RedisCacheImpl.java b/extensions/redis-cache/runtime/src/main/java/io/quarkus/cache/redis/runtime/RedisCacheImpl.java index f2a414678229cd..d0be182e72ad0e 100644 --- a/extensions/redis-cache/runtime/src/main/java/io/quarkus/cache/redis/runtime/RedisCacheImpl.java +++ b/extensions/redis-cache/runtime/src/main/java/io/quarkus/cache/redis/runtime/RedisCacheImpl.java @@ -141,7 +141,7 @@ private Uni computeValue(K key, Function valueLoader, boolean is public V get() { return valueLoader.apply(key); } - }).runSubscriptionOn(MutinyHelper.blockingExecutor(vertx.getDelegate())); + }).runSubscriptionOn(MutinyHelper.blockingExecutor(vertx.getDelegate(), false)); } else { return Uni.createFrom().item(valueLoader.apply(key)); } @@ -205,8 +205,8 @@ public Uni apply(V value) { result = set(connection, encodedKey, encodedValue).replaceWith(value); } if (isWorkerThread) { - return result - .runSubscriptionOn(MutinyHelper.blockingExecutor(vertx.getDelegate())); + return result.runSubscriptionOn( + MutinyHelper.blockingExecutor(vertx.getDelegate(), false)); } return result; } From a17b30a4f93f90992ae4822f6f9d3d6a8a8bd10b Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 13 Aug 2024 18:16:27 +0100 Subject: [PATCH 35/41] Add http root to OIDC back channel logout handlers --- .../java/io/quarkus/oidc/OidcTenantConfig.java | 2 ++ .../oidc/runtime/BackChannelLogoutHandler.java | 18 ++++++++++++++++-- .../runtime/DefaultTenantConfigResolver.java | 6 ++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java index 0c20982f42061e..290121ce29559b 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java @@ -424,6 +424,8 @@ public void setFrontchannel(Frontchannel frontchannel) { public static class Backchannel { /** * The relative path of the Back-Channel Logout endpoint at the application. + * It must start with the forward slash '/', for example, '/back-channel-logout'. + * This value is always resolved relative to 'quarkus.http.root-path'. */ @ConfigItem public Optional path = Optional.empty(); diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java index f3047ddb205210..f66c5899e834f9 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BackChannelLogoutHandler.java @@ -13,6 +13,7 @@ import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.SecurityEvent; import io.quarkus.oidc.SecurityEvent.Type; +import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcConstants; import io.quarkus.security.spi.runtime.SecurityEventHelper; import io.vertx.core.Handler; @@ -24,6 +25,7 @@ public class BackChannelLogoutHandler { private static final Logger LOG = Logger.getLogger(BackChannelLogoutHandler.class); + private static final String SLASH = "/"; @Inject DefaultTenantConfigResolver resolver; @@ -44,7 +46,8 @@ public void setup(@Observes Router router) { private void addRoute(Router router, OidcTenantConfig oidcTenantConfig) { if (oidcTenantConfig.isTenantEnabled() && oidcTenantConfig.logout.backchannel.path.isPresent()) { - router.route(oidcTenantConfig.logout.backchannel.path.get()).handler(new RouteHandler(oidcTenantConfig)); + router.route(getRootPath() + oidcTenantConfig.logout.backchannel.path.get()) + .handler(new RouteHandler(oidcTenantConfig)); } } @@ -160,7 +163,18 @@ private TenantConfigContext getTenantConfigContext(RoutingContext context) { private boolean isMatchingTenant(String requestPath, TenantConfigContext tenant) { return tenant.oidcConfig.isTenantEnabled() && tenant.oidcConfig.getTenantId().get().equals(oidcTenantConfig.getTenantId().get()) - && requestPath.equals(tenant.oidcConfig.logout.backchannel.path.orElse(null)); + && requestPath.equals(getRootPath() + tenant.oidcConfig.logout.backchannel.path.orElse(null)); } } + + private String getRootPath() { + // Prepend '/' if it is not present + String rootPath = OidcCommonUtils.prependSlash(resolver.getRootPath()); + // Strip trailing '/' if the length is > 1 + if (rootPath.length() > 1 && rootPath.endsWith("/")) { + rootPath = rootPath.substring(rootPath.length() - 1); + } + // if it is only '/' then return an empty value + return SLASH.equals(rootPath) ? "" : rootPath; + } } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java index d679739d539b8d..28d79053b51324 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java @@ -51,6 +51,7 @@ public class DefaultTenantConfigResolver { private final TenantConfigBean tenantConfigBean; private final TenantResolver[] staticTenantResolvers; private final boolean annotationBasedTenantResolutionEnabled; + private final String rootPath; @Inject Instance tenantConfigResolver; @@ -86,6 +87,7 @@ public class DefaultTenantConfigResolver { this.staticTenantResolvers = prepareStaticTenantResolvers(tenantConfigBean, rootPath, tenantResolverInstance, resolveTenantsWithIssuer, new DefaultStaticTenantResolver()); this.annotationBasedTenantResolutionEnabled = Boolean.getBoolean(OidcUtils.ANNOTATION_BASED_TENANT_RESOLUTION_ENABLED); + this.rootPath = rootPath; } @PostConstruct @@ -414,6 +416,10 @@ public OidcTenantConfig getResolvedConfig(String sessionTenantId) { return null; } + public String getRootPath() { + return rootPath; + } + private static final class IssuerBasedTenantResolver implements TenantResolver { private final TenantConfigContext[] tenantConfigContexts; From cc7bc0b72f430069640b752cd68b921efa76f8f4 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Wed, 14 Aug 2024 15:05:17 +0100 Subject: [PATCH 36/41] Fix #42546 --- .../main/asciidoc/images/oidc-github-1.png | Bin 62726 -> 148346 bytes .../security-openid-connect-providers.adoc | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/images/oidc-github-1.png b/docs/src/main/asciidoc/images/oidc-github-1.png index 41edce4ce6bba2d5c90bde5867550116215ad072..bb183b3a038c24247d0c9a15e13861ec2b61ec78 100644 GIT binary patch literal 148346 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 literal 62726 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 O Date: Tue, 13 Aug 2024 14:50:00 -0400 Subject: [PATCH 37/41] Bump apicurio-registry.version from 2.5.10.Final to 2.6.2.Final --- 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 bf2f52cafc159f..c5abc4d42fe564 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -196,7 +196,7 @@ 2.23.1 1.3.1.Final 1.12.0 - 2.5.10.Final + 2.6.2.Final 0.1.18.Final 1.20.1 3.4.0 From 2c87bb5b20d3ea8f236da35dd7ff5ac6020f336f Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 15 Aug 2024 00:20:33 +0200 Subject: [PATCH 38/41] Properly handle case when quarkus-extension.yaml doesn't exist We need to catch the NoSuchFileException and ignore it. Per gripe from Marco Bungart in the Quarkus Artemis project. --- .../io/quarkus/annotation/processor/util/FilerUtil.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/util/FilerUtil.java b/core/processor/src/main/java/io/quarkus/annotation/processor/util/FilerUtil.java index fa8765cdc77986..ea289211fcc85e 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/util/FilerUtil.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/util/FilerUtil.java @@ -8,6 +8,7 @@ import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; @@ -158,9 +159,14 @@ public Optional> getExtensionMetadata() { return Optional.of(extensionMetadata); } + } catch (NoSuchFileException e) { + // ignore + // we could get the URI, create a Path and check that the path exists but it seems a bit overkill + return Optional.empty(); } catch (IOException e) { processingEnv.getMessager().printMessage(Kind.WARNING, - "Unable to read extension metadata file: " + extensionMetadataDescriptor + " because of " + e.getMessage()); + "Unable to read extension metadata file: " + extensionMetadataDescriptor + " because of " + + e.getClass().getName() + ": " + e.getMessage()); return Optional.empty(); } } From ee79fce4070840f1717a52dd3402a6045ecc0cab Mon Sep 17 00:00:00 2001 From: Vincent Sevel Date: Thu, 15 Aug 2024 08:20:06 +0200 Subject: [PATCH 39/41] Codestart support merging application profile yaml files --- .../codestarts/quarkus/project/quarkus/codestart.yml | 2 ++ .../core/strategy/CodestartFileStrategy.java | 8 ++++++++ .../core/strategy/CodestartFileStrategyTest.java | 12 ++++++++++++ 3 files changed, 22 insertions(+) diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/codestart.yml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/codestart.yml index 2822b07c03a855..4ee705f419ba34 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/codestart.yml +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/codestart.yml @@ -10,8 +10,10 @@ output-strategy: ".gitignore": append "src/main/resources/META-INF/resources/index.html": content-merge "src/main/resources/application.yml": smart-config-merge + "src/main/resources/application-*.yml": smart-config-merge "src/main/resources/application.properties": forbidden "src/test/resources/application.yml": smart-config-merge + "src/test/resources/application-*.yml": smart-config-merge "src/test/resources/application.properties": forbidden "*.java": smart-package "*.kt": smart-package diff --git a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategy.java b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategy.java index 45a6d426024216..d32808ff17cd45 100644 --- a/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategy.java +++ b/independent-projects/tools/codestarts/src/main/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategy.java @@ -33,6 +33,14 @@ public boolean test(String t) { return true; } } + int index = filter.indexOf("*"); + if (index != -1 && filter.length() > 1) { + String part1 = filter.substring(0, index); + String part2 = filter.substring(index + 1); + if (t.startsWith(part1) && t.endsWith(part2)) { + return true; + } + } return filter.equals(t); } diff --git a/independent-projects/tools/codestarts/src/test/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategyTest.java b/independent-projects/tools/codestarts/src/test/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategyTest.java index d92d0c2e58e905..bfcf7db24b5f6c 100644 --- a/independent-projects/tools/codestarts/src/test/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategyTest.java +++ b/independent-projects/tools/codestarts/src/test/java/io/quarkus/devtools/codestarts/core/strategy/CodestartFileStrategyTest.java @@ -30,6 +30,18 @@ void testFilterEnd() { assertThat(strategy.test("")).isFalse(); } + @Test + void testFilterMiddle() { + final CodestartFileStrategy strategy = new CodestartFileStrategy("/foo/bar/my*.txt", + mock(CodestartFileStrategyHandler.class)); + assertThat(strategy.test("/foo/bar/myfile.txt")).isTrue(); + assertThat(strategy.test("/foo/bar/baz/anoter_file")).isFalse(); + assertThat(strategy.test(null)).isFalse(); + assertThat(strategy.test("foo/bar/myfile.txt")).isFalse(); + assertThat(strategy.test("something")).isFalse(); + assertThat(strategy.test("")).isFalse(); + } + @Test void testFilter() { final CodestartFileStrategy strategy = new CodestartFileStrategy("/foo/bar/myfile.txt", From 0696f9f2e67cd7eee1d52a620ff30f5c2e58d0b4 Mon Sep 17 00:00:00 2001 From: vsevel Date: Thu, 15 Aug 2024 11:54:00 +0200 Subject: [PATCH 40/41] codestart support dockerfile.jvm.from override variable --- .../tooling/dockerfiles/base/Dockerfile-layout.include.qute | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/tooling/dockerfiles/base/Dockerfile-layout.include.qute b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/tooling/dockerfiles/base/Dockerfile-layout.include.qute index 26f92d39702789..602635478ad53d 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/tooling/dockerfiles/base/Dockerfile-layout.include.qute +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/tooling/dockerfiles/base/Dockerfile-layout.include.qute @@ -77,7 +77,11 @@ # accessed directly. (example: "foo.example.com,bar.example.com") # ### +{#if dockerfile.jvm.from} +FROM {dockerfile.jvm.from} +{#else} FROM registry.access.redhat.com/ubi8/openjdk-{java.version}:1.20 +{/if} ENV LANGUAGE='en_US:en' From ff11475ca492e2c28f1e35f5c15f026e6ecf7c25 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 16 Aug 2024 12:09:30 +0200 Subject: [PATCH 41/41] Do not try to create temp directory in test archives When dealing with test archives, such as in the Platform, we shouldn't try to create a temp directory in the archive. Related to the failure described here: https://github.com/quarkusio/quarkus-platform/pull/1255#issuecomment-2288766644 --- .../test/junit/AbstractJvmQuarkusTestExtension.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java index 9254f46e1d2712..ec8e3cb970ada5 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java @@ -176,7 +176,15 @@ protected PrepareResult createAugmentor(ExtensionContext context, Class