From 5b8248fefa0779e02c98e3bfcc9de85f3ada08ec Mon Sep 17 00:00:00 2001 From: Michal Karm Babacek Date: Fri, 14 Jun 2024 13:40:45 +0200 Subject: [PATCH 01/94] Adds -H:+GenerateBuildArtifactsFile, copies .so from remote container --- ...NativeImageBuildRemoteContainerRunner.java | 71 +++++++++++++++---- .../pkg/steps/NativeImageBuildRunner.java | 25 +++++-- .../pkg/steps/NativeImageBuildStep.java | 5 ++ 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRemoteContainerRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRemoteContainerRunner.java index cd433fb365c68..94d402bb2d211 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRemoteContainerRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRemoteContainerRunner.java @@ -3,6 +3,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; @@ -10,6 +11,11 @@ import org.jboss.logging.Logger; +import io.quarkus.builder.JsonReader; +import io.quarkus.builder.json.JsonArray; +import io.quarkus.builder.json.JsonObject; +import io.quarkus.builder.json.JsonString; +import io.quarkus.builder.json.JsonValue; import io.quarkus.deployment.pkg.NativeConfig; public class NativeImageBuildRemoteContainerRunner extends NativeImageBuildContainerRunner { @@ -34,22 +40,27 @@ protected void preBuild(Path outputDir, List buildArgs) throws Interrupt final List containerRuntimeArgs = Arrays.asList("-v", CONTAINER_BUILD_VOLUME_NAME + ":" + NativeImageBuildStep.CONTAINER_BUILD_VOLUME_PATH); final String[] createTempContainerCommand = buildCommand("create", containerRuntimeArgs, Collections.emptyList()); - containerId = runCommandAndReadOutput(createTempContainerCommand, "Failed to create temp container."); + try { + containerId = runCommandAndReadOutput(createTempContainerCommand).get(0); + } catch (RuntimeException | InterruptedException | IOException e) { + throw new RuntimeException("Failed to create temp container.", e); + } // docker cp :/project - String[] copyCommand = new String[] { containerRuntime.getExecutableName(), "cp", outputDir.toAbsolutePath() + "/.", + final String[] copyCommand = new String[] { + containerRuntime.getExecutableName(), "cp", outputDir.toAbsolutePath() + "/.", containerId + ":" + NativeImageBuildStep.CONTAINER_BUILD_VOLUME_PATH }; - runCommand(copyCommand, "Failed to copy source-jar and libs from host to builder container", null); + runCommand(copyCommand, "Failed to copy source-jar and libs from host to builder container"); super.preBuild(outputDir, buildArgs); } - private String runCommandAndReadOutput(String[] command, String errorMsg) throws IOException, InterruptedException { + private List runCommandAndReadOutput(String[] command) throws IOException, InterruptedException { log.info(String.join(" ", command).replace("$", "\\$")); - Process process = new ProcessBuilder(command).start(); + final Process process = new ProcessBuilder(command).start(); if (process.waitFor() != 0) { - throw new RuntimeException(errorMsg); + throw new RuntimeException("Command failed: " + String.join(" ", command)); } try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { - return reader.readLine(); + return reader.lines().toList(); } } @@ -57,15 +68,45 @@ private String runCommandAndReadOutput(String[] command, String errorMsg) throws protected void postBuild(Path outputDir, String nativeImageName, String resultingExecutableName) { copyFromContainerVolume(outputDir, resultingExecutableName, "Failed to copy native image from container volume back to the host."); + + // Note that podman cp does not support globbing i.e. cp /project/*.so will not work. + // Why only .so? How about .dynlib and .lib? Regardless of the host platform, + // the builder container is always Linux. So, we only need to copy .so files. + // + // We could either start the container again, exec `find' or `ls' to list the .so files, + // stop the container again and use that list. We could also use the build-artifacts.json + // to get the list of artifacts straight away which is what ended up doing here: + copyFromContainerVolume(outputDir, "build-artifacts.json", null); + try { + final Path buildArtifactsFile = outputDir.resolve("build-artifacts.json"); + if (Files.exists(buildArtifactsFile)) { + // The file is small enough to afford this read + final String buildArtifactsJson = Files.readString(buildArtifactsFile); + final JsonObject jsonRead = JsonReader.of(buildArtifactsJson).read(); + final JsonValue jdkLibraries = jsonRead.get("jdk_libraries"); + // The jdk_libraries field is optional, there might not be any. + if (jdkLibraries instanceof JsonArray) { + for (JsonValue lib : ((JsonArray) jdkLibraries).value()) { + copyFromContainerVolume(outputDir, ((JsonString) lib).value(), + "Failed to copy " + lib + " from container volume back to the host."); + } + } + } + } catch (IOException e) { + log.errorf(e, "Failed to list .so files in the build-artifacts.json. Skipping the step."); + } + if (nativeConfig.debug().enabled()) { - copyFromContainerVolume(outputDir, "sources", "Failed to copy sources from container volume back to the host."); - String symbols = String.format("%s.debug", nativeImageName); - copyFromContainerVolume(outputDir, symbols, "Failed to copy debug symbols from container volume back to the host."); + copyFromContainerVolume(outputDir, "sources", + "Failed to copy sources from container volume back to the host."); + final String symbols = String.format("%s.debug", nativeImageName); + copyFromContainerVolume(outputDir, symbols, + "Failed to copy debug symbols from container volume back to the host."); } // docker container rm final String[] rmTempContainerCommand = new String[] { containerRuntime.getExecutableName(), "container", "rm", containerId }; - runCommand(rmTempContainerCommand, "Failed to remove container: " + containerId, null); + runCommand(rmTempContainerCommand, "Failed to remove container: " + containerId); // docker volume rm rmVolume("Failed to remove volume: " + CONTAINER_BUILD_VOLUME_NAME); } @@ -73,20 +114,20 @@ protected void postBuild(Path outputDir, String nativeImageName, String resultin private void rmVolume(String errorMsg) { final String[] rmVolumeCommand = new String[] { containerRuntime.getExecutableName(), "volume", "rm", CONTAINER_BUILD_VOLUME_NAME }; - runCommand(rmVolumeCommand, errorMsg, null); + runCommand(rmVolumeCommand, errorMsg); } private void copyFromContainerVolume(Path outputDir, String path, String errorMsg) { // docker cp :/project/ - String[] copyCommand = new String[] { containerRuntime.getExecutableName(), "cp", + final String[] copyCommand = new String[] { containerRuntime.getExecutableName(), "cp", containerId + ":" + NativeImageBuildStep.CONTAINER_BUILD_VOLUME_PATH + "/" + path, outputDir.toAbsolutePath().toString() }; - runCommand(copyCommand, errorMsg, null); + runCommand(copyCommand, errorMsg); } @Override protected List getContainerRuntimeBuildArgs(Path outputDir) { - List containerRuntimeArgs = super.getContainerRuntimeBuildArgs(outputDir); + final List containerRuntimeArgs = super.getContainerRuntimeBuildArgs(outputDir); Collections.addAll(containerRuntimeArgs, "-v", CONTAINER_BUILD_VOLUME_NAME + ":" + NativeImageBuildStep.CONTAINER_BUILD_VOLUME_PATH); return containerRuntimeArgs; diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java index 899cf9c280a01..0b999594e66d3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java @@ -11,6 +11,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; import org.apache.commons.lang3.SystemUtils; import org.jboss.logging.Logger; @@ -132,22 +133,28 @@ static void runCommand(String[] command, String errorMsg, File workingDirectory) log.info(String.join(" ", command).replace("$", "\\$")); Process process = null; try { - final ProcessBuilder processBuilder = new ProcessBuilder(command); + final ProcessBuilder processBuilder = new ProcessBuilder(command) + .redirectErrorStream(true); if (workingDirectory != null) { processBuilder.directory(workingDirectory); } process = processBuilder.start(); final int exitCode = process.waitFor(); if (exitCode != 0) { + final String out; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + out = reader.lines().collect(Collectors.joining("\n")); + } if (errorMsg != null) { - log.error(errorMsg); + log.error(errorMsg + " Output: " + out); } else { - log.debugf("Command: " + String.join(" ", command) + " failed with exit code " + exitCode); + log.debugf( + "Command: " + String.join(" ", command) + " failed with exit code " + exitCode + " Output: " + out); } } } catch (IOException | InterruptedException e) { if (errorMsg != null) { - log.error(errorMsg); + log.errorf(e, errorMsg); } else { log.debugf(e, "Command: " + String.join(" ", command) + " failed."); } @@ -158,6 +165,16 @@ static void runCommand(String[] command, String errorMsg, File workingDirectory) } } + /** + * Run {@code command} and log error if {@code errorMsg} is not null. + * + * @param command + * @param errorMsg + */ + static void runCommand(String[] command, String errorMsg) { + runCommand(command, errorMsg, null); + } + static class Result { private final int exitCode; diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index 1c1d8f54b73e1..59cf46bf025ab 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -815,6 +815,11 @@ public NativeImageInvokerInfo build() { "-H:BuildOutputJSONFile=" + nativeImageName + "-build-output-stats.json"); } + // only available in GraalVM 23.0+, we want a file with the list of built artifacts + if (graalVMVersion.compareTo(GraalVM.Version.VERSION_23_0_0) >= 0) { + addExperimentalVMOption(nativeImageArgs, "-H:+GenerateBuildArtifactsFile"); + } + // only available in GraalVM 23.1.0+ if (graalVMVersion.compareTo(GraalVM.Version.VERSION_23_1_0) >= 0) { if (graalVMVersion.compareTo(GraalVM.Version.VERSION_24_0_0) < 0) { From 59b63bba03bb2626bb09a362bb5695b62dffef8d Mon Sep 17 00:00:00 2001 From: Michal Karm Babacek Date: Fri, 14 Jun 2024 18:36:21 +0200 Subject: [PATCH 02/94] Test Lambda and remote container packaging Test Lambda and remote container packaging --- integration-tests/awt-packaging/pom.xml | 125 ++++++++++++++++++ .../main/java/io/quarkus/it/jaxb/Book.java | 36 +++++ .../main/java/io/quarkus/it/jaxb/Lambda.java | 31 +++++ .../java/io/quarkus/it/jaxb/Resource.java | 33 +++++ .../src/main/resources/application.properties | 2 + .../java/io/quarkus/it/jaxb/AwtJaxbTest.java | 50 +++++++ .../io/quarkus/it/jaxb/AwtJaxbTestIT.java | 59 +++++++++ integration-tests/pom.xml | 1 + 8 files changed, 337 insertions(+) create mode 100644 integration-tests/awt-packaging/pom.xml create mode 100644 integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Book.java create mode 100644 integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Lambda.java create mode 100644 integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Resource.java create mode 100644 integration-tests/awt-packaging/src/main/resources/application.properties create mode 100644 integration-tests/awt-packaging/src/test/java/io/quarkus/it/jaxb/AwtJaxbTest.java create mode 100644 integration-tests/awt-packaging/src/test/java/io/quarkus/it/jaxb/AwtJaxbTestIT.java diff --git a/integration-tests/awt-packaging/pom.xml b/integration-tests/awt-packaging/pom.xml new file mode 100644 index 0000000000000..6fed85e314d3f --- /dev/null +++ b/integration-tests/awt-packaging/pom.xml @@ -0,0 +1,125 @@ + + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-integration-test-awt-packaging + Quarkus - Integration Tests - AWT Packaging + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jaxb + + + io.quarkus + quarkus-awt + + + io.quarkus + quarkus-amazon-lambda + + + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-junit5-mockito + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-test-amazon-lambda + test + + + + + io.quarkus + quarkus-resteasy-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-resteasy-jaxb-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-awt-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-amazon-lambda-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + diff --git a/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Book.java b/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Book.java new file mode 100644 index 0000000000000..efa5b83d3aeaa --- /dev/null +++ b/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Book.java @@ -0,0 +1,36 @@ +package io.quarkus.it.jaxb; + +import java.awt.Image; + +import jakarta.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class Book { + + private String title; + private Image cover; + + public Book() { + } + + public Book(String title, Image cover) { + this.title = title; + this.cover = cover; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Image getCover() { + return cover; + } + + public void setCover(Image cover) { + this.cover = cover; + } +} diff --git a/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Lambda.java b/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Lambda.java new file mode 100644 index 0000000000000..f887628550afd --- /dev/null +++ b/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Lambda.java @@ -0,0 +1,31 @@ +package io.quarkus.it.jaxb; + +import java.io.StringReader; + +import jakarta.inject.Named; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBException; + +import org.jboss.logging.Logger; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +@Named("test") +public class Lambda implements RequestHandler { + + private static final Logger LOGGER = Logger.getLogger(Lambda.class); + + @Override + public String handleRequest(String input, Context context) { + try { + final JAXBContext jaxbContext = JAXBContext.newInstance(Book.class); + final Book book = (Book) jaxbContext.createUnmarshaller().unmarshal(new StringReader(input)); + return String.valueOf(book.getCover().getHeight(null)); + } catch (JAXBException e) { + context.getLogger().log("Error: " + e.getMessage()); + LOGGER.error(e); + } + return null; + } +} diff --git a/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Resource.java b/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Resource.java new file mode 100644 index 0000000000000..dce419c63f1b7 --- /dev/null +++ b/integration-tests/awt-packaging/src/main/java/io/quarkus/it/jaxb/Resource.java @@ -0,0 +1,33 @@ +package io.quarkus.it.jaxb; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.jboss.logging.Logger; + +@Path("/jaxb") +@ApplicationScoped +public class Resource { + + private static final Logger LOGGER = Logger.getLogger(Resource.class); + + @Path("/book") + @POST + @Consumes(MediaType.APPLICATION_XML) + @Produces(MediaType.TEXT_PLAIN) + public Response postBook(Book book) { + LOGGER.info("Received book: " + book); + try { + return Response.accepted().entity(book.getCover().getHeight(null)).build(); + } catch (Exception e) { + LOGGER.error(e); + return Response.serverError().entity(e.getMessage()).build(); + } + } + +} diff --git a/integration-tests/awt-packaging/src/main/resources/application.properties b/integration-tests/awt-packaging/src/main/resources/application.properties new file mode 100644 index 0000000000000..f78355769f04a --- /dev/null +++ b/integration-tests/awt-packaging/src/main/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.lambda.handler=test +quarkus.native.remote-container-build=true diff --git a/integration-tests/awt-packaging/src/test/java/io/quarkus/it/jaxb/AwtJaxbTest.java b/integration-tests/awt-packaging/src/test/java/io/quarkus/it/jaxb/AwtJaxbTest.java new file mode 100644 index 0000000000000..8010793a0b4a8 --- /dev/null +++ b/integration-tests/awt-packaging/src/test/java/io/quarkus/it/jaxb/AwtJaxbTest.java @@ -0,0 +1,50 @@ +package io.quarkus.it.jaxb; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.MediaType.APPLICATION_XML; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + +import io.quarkus.amazon.lambda.test.LambdaClient; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class AwtJaxbTest { + + public static final String BOOK_WITH_IMAGE = "" + + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAIElEQVR4XmNgGCngPxSgi6MAZAU4FeOUQAdEKwQBdKsBOgof4SXid6kAAAAASUVORK5CYII=" + + + "Foundation" + + ""; + + /** + * Smoke tests that we have .so files + * copied over from the remote build container. + */ + @Test + public void book() { + given() + .when() + .header("Content-Type", APPLICATION_XML) + .body(BOOK_WITH_IMAGE) + .when() + .post("/jaxb/book") + .then() + .statusCode(HttpStatus.SC_ACCEPTED) + // The height in pixels of the book's cover image. + .body(is("10")); + } + + /** + * Smoke tests that our Lambda function makes at + * least some sense, but it doesn't talk to real AWS API. + */ + @Test + public void testLambdaStream() { + assertEquals("10", LambdaClient.invoke(String.class, BOOK_WITH_IMAGE)); + } + +} diff --git a/integration-tests/awt-packaging/src/test/java/io/quarkus/it/jaxb/AwtJaxbTestIT.java b/integration-tests/awt-packaging/src/test/java/io/quarkus/it/jaxb/AwtJaxbTestIT.java new file mode 100644 index 0000000000000..f8d98358f481a --- /dev/null +++ b/integration-tests/awt-packaging/src/test/java/io/quarkus/it/jaxb/AwtJaxbTestIT.java @@ -0,0 +1,59 @@ +package io.quarkus.it.jaxb; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class AwtJaxbTestIT extends AwtJaxbTest { + + /** + * Test is native image only, we need all artifacts to be packaged + * already, e.g. function.zip + *
+ * Tests that the same set of .so files that was copied over + * from the remote build container is also packaged into the + * zip file that will be deployed to AWS Lambda. + * + * @throws java.io.IOException + */ + @Test + public void testPackaging() throws IOException { + final Path targetPath = Paths.get(".", "target").toAbsolutePath(); + final Set localLibs = new HashSet<>(); + try (DirectoryStream stream = Files.newDirectoryStream(targetPath, "*.so")) { + for (Path entry : stream) { + localLibs.add(entry.getFileName().toString()); + } + } + + final Path zipPath = targetPath.resolve("function.zip").toAbsolutePath(); + assertTrue(Files.exists(zipPath), "Expected " + zipPath + " to exist"); + final Set awsLambdaLibs = new HashSet<>(); + try (ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipPath))) { + ZipEntry entry; + while ((entry = zipInputStream.getNextEntry()) != null) { + if (entry.getName().endsWith(".so")) { + awsLambdaLibs.add(entry.getName()); + } + zipInputStream.closeEntry(); + } + } + assertEquals(localLibs, awsLambdaLibs, + "The sets of .so libs produced by the build and the set in .zip file MUST be the same. It was: " + + localLibs + " vs. " + awsLambdaLibs); + } +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 5c1a676bc3b1d..487eb8dc9de25 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -164,6 +164,7 @@ avro-reload awt + awt-packaging no-awt bouncycastle bouncycastle-fips From 31507917bb03a3d85b614c995213d0830766d945 Mon Sep 17 00:00:00 2001 From: Michal Karm Babacek Date: Fri, 14 Jun 2024 19:06:25 +0200 Subject: [PATCH 03/94] Enables awt-packaging on GHA --- .github/native-tests.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/native-tests.json b/.github/native-tests.json index 95d36db4af702..5cb3b5ada7a27 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -145,9 +145,9 @@ "os-name": "ubuntu-latest" }, { - "category": "AWT, ImageIO and Java2D", - "timeout": 30, - "test-modules": "awt, no-awt", + "category": "AWT, ImageIO and Java2D, Packaging .so files", + "timeout": 40, + "test-modules": "awt, no-awt, awt-packaging", "os-name": "ubuntu-latest" } ] From f09837e2465a4b426fb88e5a326b1e5858973e4f Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 19 Jun 2024 11:45:14 +0200 Subject: [PATCH 04/94] Update the MappingStructure constant in the docs to match the enum values --- .../runtime/HibernateSearchStandaloneBuildTimeConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/hibernate-search-standalone-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/standalone/elasticsearch/runtime/HibernateSearchStandaloneBuildTimeConfig.java b/extensions/hibernate-search-standalone-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/standalone/elasticsearch/runtime/HibernateSearchStandaloneBuildTimeConfig.java index 8ef6845e7aa04..fc3de18ff9b91 100644 --- a/extensions/hibernate-search-standalone-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/standalone/elasticsearch/runtime/HibernateSearchStandaloneBuildTimeConfig.java +++ b/extensions/hibernate-search-standalone-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/standalone/elasticsearch/runtime/HibernateSearchStandaloneBuildTimeConfig.java @@ -269,7 +269,7 @@ interface MappingConfig { * Associations between entities must be bi-directional: * specifying the inverse side of associations through `@AssociationInverseSide` *is required*, * unless reindexing is disabled for that association through `@IndexingDependency(reindexOnUpdate = ...)`. - * `tree`:: + * `document`:: * Entities indexed through Hibernate Search are the root of a document, * i.e. an indexed entity "owns" other entities it references through associations, * which *cannot* be updated independently of the indexed entity. From 4514356d78f96b4bc2bff2ab35b2a11c3af0123b Mon Sep 17 00:00:00 2001 From: Jerome Prinet Date: Fri, 28 Jun 2024 10:59:17 +0200 Subject: [PATCH 05/94] Apply weekly cache key on quarkus-metadata entry --- .github/workflows/ci-actions-incremental.yml | 19 +++++++++++------ .../workflows/native-it-selected-graalvm.yml | 21 ++++++++++++------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci-actions-incremental.yml b/.github/workflows/ci-actions-incremental.yml index e5d1060da034e..5ecee173c551a 100644 --- a/.github/workflows/ci-actions-incremental.yml +++ b/.github/workflows/ci-actions-incremental.yml @@ -106,7 +106,9 @@ jobs: outputs: gib_args: ${{ steps.get-gib-args.outputs.gib_args }} gib_impacted: ${{ steps.get-gib-impacted.outputs.impacted_modules }} - m2-cache-key: ${{ steps.m2-cache-key.outputs.key }} + m2-cache-key: ${{ steps.cache-key.outputs.m2-cache-key }} + quarkus-metadata-cache-key: ${{ steps.cache-key.outputs.quarkus-metadata-cache-key }} + quarkus-metadata-cache-key-default: ${{ steps.cache-key.outputs.quarkus-metadata-cache-key-default }} steps: - name: Gradle Enterprise environment run: | @@ -125,17 +127,20 @@ jobs: with: distribution: temurin java-version: 17 - - name: Generate .m2 cache key - id: m2-cache-key + - name: Generate cache key + id: cache-key run: | - echo "key=m2-cache-$(/bin/date -u "+%Y-%U")" >> $GITHUB_OUTPUT + CURRENT_WEEK=$(/bin/date -u "+%Y-%U") + echo "m2-cache-key=m2-cache-${CURRENT_WEEK}" >> $GITHUB_OUTPUT + echo "quarkus-metadata-cache-key=quarkus-metadata-cache-${CURRENT_WEEK}-${{ github.ref_name }}" >> $GITHUB_OUTPUT + echo "quarkus-metadata-cache-key-default=quarkus-metadata-cache-${CURRENT_WEEK}-${{ github.event.repository.default_branch }}" >> $GITHUB_OUTPUT - name: Cache Maven Repository id: cache-maven uses: actions/cache@v4 with: path: ~/.m2/repository # refresh cache every week to avoid unlimited growth - key: ${{ steps.m2-cache-key.outputs.key }} + key: ${{ steps.cache-key.outputs.m2-cache-key }} - name: Verify native-tests.json run: ./.github/verify-tests-json.sh native-tests.json integration-tests/ - name: Verify virtual-threads-tests.json @@ -1105,7 +1110,9 @@ jobs: uses: actions/cache@v4 with: path: '**/.quarkus/quarkus-prod-config-dump' - key: ${{ runner.os }}-quarkus-metadata + key: ${{ needs.build-jdk17.outputs.quarkus-metadata-cache-key }} + # The key is restored from default branch if not found, but still branch specific to override the default after first run + restore-keys: ${{ needs.build-jdk17.outputs.quarkus-metadata-cache-key-default }} - name: Build env: TEST_MODULES: ${{matrix.test-modules}} diff --git a/.github/workflows/native-it-selected-graalvm.yml b/.github/workflows/native-it-selected-graalvm.yml index 8103432d9f1df..30276aaa5ff84 100644 --- a/.github/workflows/native-it-selected-graalvm.yml +++ b/.github/workflows/native-it-selected-graalvm.yml @@ -53,7 +53,9 @@ jobs: outputs: gib_args: ${{ steps.get-gib-args.outputs.gib_args }} gib_impacted: ${{ steps.get-gib-impacted.outputs.impacted_modules }} - m2-cache-key: ${{ steps.m2-cache-key.outputs.key }} + m2-cache-key: ${{ steps.cache-key.outputs.m2-cache-key }} + quarkus-metadata-cache-key: ${{ steps.cache-key.outputs.quarkus-metadata-cache-key }} + quarkus-metadata-cache-key-default: ${{ steps.cache-key.outputs.quarkus-metadata-cache-key-default }} steps: - name: Gradle Enterprise environment run: | @@ -73,17 +75,20 @@ jobs: with: distribution: temurin java-version: 17 - - name: Generate .m2 cache key - id: m2-cache-key + - name: Generate cache key + id: cache-key run: | - echo "key=m2-cache-$(/bin/date -u "+%Y-%U")" >> $GITHUB_OUTPUT + CURRENT_WEEK=$(/bin/date -u "+%Y-%U") + echo "m2-cache-key=m2-cache-${CURRENT_WEEK}" >> $GITHUB_OUTPUT + echo "quarkus-metadata-cache-key=quarkus-metadata-cache-${CURRENT_WEEK}-${{ github.ref_name }}" >> $GITHUB_OUTPUT + echo "quarkus-metadata-cache-key-default=quarkus-metadata-cache-${CURRENT_WEEK}-${{ github.event.repository.default_branch }}" >> $GITHUB_OUTPUT - name: Cache Maven Repository id: cache-maven uses: actions/cache@v4 with: path: ~/.m2/repository # refresh cache every week to avoid unlimited growth - key: ${{ steps.m2-cache-key.outputs.key }} + key: ${{ steps.cache-key.outputs.m2-cache-key }} - name: Verify native-tests.json run: ./.github/verify-tests-json.sh native-tests.json integration-tests/ - name: Verify virtual-threads-tests.json @@ -198,7 +203,7 @@ jobs: json=$(echo $json | jq 'del(.include[] | select(."os-name" == "windows-latest"))') json=$(echo $json | tr -d '\n') echo "${json}" - echo "matrix=${json}" >> $GITHUB_OUTPUT + echo "matrix=${json}" >> $GITHUB_OUTPUT virtual-thread-native-tests: name: Native Tests - Virtual Thread - ${{matrix.category}} - ${{inputs.NATIVE_COMPILER}} ${{inputs.NATIVE_COMPILER_VERSION}} - ${{inputs.BRANCH}} @@ -341,7 +346,9 @@ jobs: uses: actions/cache@v4 with: path: '**/.quarkus/quarkus-prod-config-dump' - key: ${{ runner.os }}-quarkus-metadata + key: ${{ needs.build-jdk17.outputs.quarkus-metadata-cache-key }} + # The key is restored from default branch if not found, but still branch specific to override the default after first run + restore-keys: ${{ needs.build-jdk17.outputs.quarkus-metadata-cache-key-default }} - name: Build env: TEST_MODULES: ${{matrix.test-modules}} From 7120304aabb7e0c458f2e650a82d3a0ce99c5e50 Mon Sep 17 00:00:00 2001 From: Gwenneg Lepage Date: Wed, 3 Jul 2024 00:07:39 +0200 Subject: [PATCH 06/94] Handle duplicated Vert.x context in CaffeineCacheImpl --- .../cache/runtime/CacheResultInterceptor.java | 48 ----------------- .../runtime/caffeine/CaffeineCacheImpl.java | 52 ++++++++++++++++++- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java index f635d965a518e..f16d6674ee7ed 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java @@ -1,7 +1,6 @@ package io.quarkus.cache.runtime; import java.time.Duration; -import java.util.concurrent.Executor; import java.util.function.Function; import java.util.function.Supplier; @@ -17,10 +16,6 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.TimeoutException; import io.smallrye.mutiny.Uni; -import io.vertx.core.Context; -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.impl.ContextInternal; @CacheResult(cacheName = "") // The `cacheName` attribute is @Nonbinding. @Interceptor @@ -58,7 +53,6 @@ public Object intercept(InvocationContext invocationContext) throws Throwable { try { ReturnType returnType = determineReturnType(invocationContext.getMethod().getReturnType()); if (returnType != ReturnType.NonAsync) { - Context context = Vertx.currentContext(); Uni cacheValue = cache.getAsync(key, new Function>() { @SuppressWarnings("unchecked") @Override @@ -76,48 +70,6 @@ public Uni apply(Object key) { public Uni apply(Throwable throwable) { return cache.invalidate(key).replaceWith(throwable); } - }).emitOn(new Executor() { - // We need make sure we go back to the original context when the cache value is computed. - // Otherwise, we would always emit on the context having computed the value, which could - // break the duplicated context isolation. - @Override - public void execute(Runnable command) { - Context ctx = Vertx.currentContext(); - if (context == null) { - // We didn't capture a context - if (ctx == null) { - // We are not on a context => we can execute immediately. - command.run(); - } else { - // We are on a context. - // We cannot continue on the current context as we may share a duplicated context. - // We need a new one. Note that duplicate() does not duplicate the duplicated context, - // but the root context. - ((ContextInternal) ctx).duplicate() - .runOnContext(new Handler() { - @Override - public void handle(Void ignored) { - command.run(); - } - }); - } - } else { - // We captured a context. - if (ctx == context) { - // We are on the same context => we can execute immediately - command.run(); - } else { - // 1) We are not on a context (ctx == null) => we need to switch to the captured context. - // 2) We are on a different context (ctx != null) => we need to switch to the captured context. - context.runOnContext(new Handler() { - @Override - public void handle(Void ignored) { - command.run(); - } - }); - } - } - } }); if (binding.lockTimeout() <= 0) { diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java index dff31eb8dab1e..278c1ebc3ab15 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/caffeine/CaffeineCacheImpl.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executor; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -26,6 +27,10 @@ import io.quarkus.cache.runtime.AbstractCache; import io.quarkus.cache.runtime.NullValueConverter; import io.smallrye.mutiny.Uni; +import io.vertx.core.Context; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.impl.ContextInternal; /** * This class is an internal Quarkus cache implementation using Caffeine. Do not use it explicitly from your Quarkus @@ -99,6 +104,7 @@ public CompletionStage get() { @Override public Uni getAsync(K key, Function> valueLoader) { Objects.requireNonNull(key, NULL_KEYS_NOT_SUPPORTED_MSG); + Context context = Vertx.currentContext(); return Uni.createFrom() .completionStage(new Supplier>() { @Override @@ -119,7 +125,51 @@ public CompletableFuture apply(Object key) { recorder.doRecord(key); return result; } - }).map(fromCacheValue()); + }) + .map(fromCacheValue()) + .emitOn(new Executor() { + // We need make sure we go back to the original context when the cache value is computed. + // Otherwise, we would always emit on the context having computed the value, which could + // break the duplicated context isolation. + @Override + public void execute(Runnable command) { + Context ctx = Vertx.currentContext(); + if (context == null) { + // We didn't capture a context + if (ctx == null) { + // We are not on a context => we can execute immediately. + command.run(); + } else { + // We are on a context. + // We cannot continue on the current context as we may share a duplicated context. + // We need a new one. Note that duplicate() does not duplicate the duplicated context, + // but the root context. + ((ContextInternal) ctx).duplicate() + .runOnContext(new Handler() { + @Override + public void handle(Void ignored) { + command.run(); + } + }); + } + } else { + // We captured a context. + if (ctx == context) { + // We are on the same context => we can execute immediately + command.run(); + } else { + // 1) We are not on a context (ctx == null) => we need to switch to the captured context. + // 2) We are on a different context (ctx != null) => we need to switch to the captured context. + context.runOnContext(new Handler() { + @Override + public void handle(Void ignored) { + command.run(); + } + }); + } + } + } + }); } @Override From bb875c81b40f1fd0fb4975c200f24e9731433f5a Mon Sep 17 00:00:00 2001 From: mkrueger92 <7305571+mkrueger92@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:24:26 +0200 Subject: [PATCH 07/94] Expose validate-on-borrow Expose the option to validate the connection on borrow as supported by Agroal. Fix #41616 --- .../agroal/runtime/DataSourceJdbcRuntimeConfig.java | 9 +++++++++ .../main/java/io/quarkus/agroal/runtime/DataSources.java | 1 + 2 files changed, 10 insertions(+) diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceJdbcRuntimeConfig.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceJdbcRuntimeConfig.java index 392903d7d3105..d2c1528f87715 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceJdbcRuntimeConfig.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSourceJdbcRuntimeConfig.java @@ -115,6 +115,15 @@ public interface DataSourceJdbcRuntimeConfig { */ Optional validationQuerySql(); + /** + * Forces connection validation prior to acquisition (foreground validation) regardless of the idle status. + *

+ * Because of the overhead of performing validation on every call, it’s recommended to rely on default idle validation + * instead, and to leave this to `false`. + */ + @WithDefault("false") + boolean validateOnBorrow(); + /** * Disable pooling to prevent reuse of Connections. Use this when an external pool manages the life-cycle * of Connections. diff --git a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java index 3b8694dcc5025..a8b005fc4aa53 100644 --- a/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java +++ b/extensions/agroal/runtime/src/main/java/io/quarkus/agroal/runtime/DataSources.java @@ -410,6 +410,7 @@ public boolean isValid(Connection connection) { } }); } + poolConfiguration.validateOnBorrow(dataSourceJdbcRuntimeConfig.validateOnBorrow()); poolConfiguration.reapTimeout(dataSourceJdbcRuntimeConfig.idleRemovalInterval()); if (dataSourceJdbcRuntimeConfig.leakDetectionInterval().isPresent()) { poolConfiguration.leakTimeout(dataSourceJdbcRuntimeConfig.leakDetectionInterval().get()); From 2891ca4f73acdb919f2cbe1fa254d052087fdfaf Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Mon, 1 Jul 2024 11:10:05 +0200 Subject: [PATCH 08/94] Update to gRPC 1.65.0 and Netty 4.1.111 This commit updates the gRPC version to 1.65.0 and enables the switch to Netty 4.1.111. Additionally, it updates several gRPC-related dependencies: - jprotoc to 1.2.2 - protoc to 3.25.3 - protobuf-java to 3.25.3 Disable Stork "least response time" IT when using the gRPC Java client. This is because in gRPC 1.65.0, the load balancer is only called once if the calls are queued. The Vert.x-based client is still behaving normally. When using gRPC Java client, it's a change in behavior, but the load balancer will still be called for non-concurrent calls. --- bom/application/pom.xml | 2 +- docs/src/main/asciidoc/grpc-service-consumption.adoc | 2 -- docs/src/main/asciidoc/stork-reference.adoc | 2 +- .../stork/GrpcStorkResponseTimeCollectionTest.java | 9 +++++++++ pom.xml | 6 +++--- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index dc850a752b6af..e426450fc6435 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -134,7 +134,7 @@ 15.0.5.Final 5.0.5.Final 3.1.5 - 4.1.110.Final + 4.1.111.Final 1.16.0 1.0.4 3.6.0.Final diff --git a/docs/src/main/asciidoc/grpc-service-consumption.adoc b/docs/src/main/asciidoc/grpc-service-consumption.adoc index b22b313b1da9c..5fb6f420708d6 100644 --- a/docs/src/main/asciidoc/grpc-service-consumption.adoc +++ b/docs/src/main/asciidoc/grpc-service-consumption.adoc @@ -211,8 +211,6 @@ The `client-name` is the name set in the `@GrpcClient` or derived from the injec The following examples uses _hello_ as the client name. Don't forget to replace it with the name you used in the `@GrpcClient` annotation. -IMPORTANT: When you enable `quarkus.grpc.clients."client-name".use-quarkus-grpc-client`, you are then using the new Vert.x gRPC channel implementation, so not all configuration properties can still be applied. And currently there is no Stork support yet. - IMPORTANT: When you enable `quarkus.grpc.clients."client-name".xds.enabled`, it's the xDS that should handle most of the configuration above. === Enabling TLS diff --git a/docs/src/main/asciidoc/stork-reference.adoc b/docs/src/main/asciidoc/stork-reference.adoc index 0e7c9ec907b0d..138a07a989418 100644 --- a/docs/src/main/asciidoc/stork-reference.adoc +++ b/docs/src/main/asciidoc/stork-reference.adoc @@ -20,7 +20,7 @@ include::{includes}/extension-status.adoc[] The current integration of Stork supports: * the REST Client -* the gRPC clients +* the gRPC clients (using the Vert.x gRPC client is recommended) Warning: The gRPC client integration does not support statistic-based load balancers. diff --git a/integration-tests/grpc-stork-response-time/src/test/java/io/quarkus/grpc/examples/stork/GrpcStorkResponseTimeCollectionTest.java b/integration-tests/grpc-stork-response-time/src/test/java/io/quarkus/grpc/examples/stork/GrpcStorkResponseTimeCollectionTest.java index 2cd13722866fa..05043f8b73555 100644 --- a/integration-tests/grpc-stork-response-time/src/test/java/io/quarkus/grpc/examples/stork/GrpcStorkResponseTimeCollectionTest.java +++ b/integration-tests/grpc-stork-response-time/src/test/java/io/quarkus/grpc/examples/stork/GrpcStorkResponseTimeCollectionTest.java @@ -1,7 +1,16 @@ package io.quarkus.grpc.examples.stork; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + import io.quarkus.test.junit.QuarkusTest; @QuarkusTest class GrpcStorkResponseTimeCollectionTest extends GrpcStorkResponseTimeCollectionTestBase { + + @Test + @Disabled("Disabled because of https://github.com/grpc/grpc-java/commit/8844cf7b87a04dd2d2e4a74cd0f0e3f4fed14113 which does not call the load balancer for each call anymore.") + public void shouldCallConfigurableIfFaster() { + + } } diff --git a/pom.xml b/pom.xml index 46f3f07e4b0cc..fcc3b9eb4c89b 100644 --- a/pom.xml +++ b/pom.xml @@ -80,9 +80,9 @@ 7.1.1.Final - 1.64.0 - 1.2.1 - 3.25.0 + 1.65.0 + 1.2.2 + 3.25.3 ${protoc.version} 2.41.0 From 2cabc493147a6d033cb5610ee4f72ce1420ccb92 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 21 Jun 2024 16:13:57 +0200 Subject: [PATCH 09/94] ArC: fix Types.getParameterizedType() in case of a nested generic class The previous code always used `null` as the owner type, which is incorrect in case of nested generic classes. This commit fixes that in a not entirely precise but good enough way. --- .../quarkus/arc/processor/BeanGenerator.java | 4 +- .../java/io/quarkus/arc/processor/Types.java | 48 +++++++++++++------ 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java index 2e41703a0e002..da197f726362b 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java @@ -41,6 +41,7 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; +import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; import org.jboss.logging.Logger; @@ -1100,7 +1101,8 @@ private void implementCreateForSyntheticBean(ClassCreator beanCreator, BeanInfo TryBlock tryBlock = doCreate.tryBlock(); ResultHandle requiredType; try { - requiredType = Types.getTypeHandle(tryBlock, injectionPoint.getType(), tccl); + IndexView index = bean.getDeployment().getBeanArchiveIndex(); + requiredType = Types.getTypeHandle(tryBlock, injectionPoint.getType(), tccl, index); } catch (IllegalArgumentException e) { throw new IllegalStateException( "Unable to construct the type handle for " + injectionPoint.getType() + ": " + e.getMessage()); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java index 3960051e04b96..a2018013dc4c4 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Types.java @@ -22,6 +22,7 @@ import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; import org.jboss.jandex.FieldInfo; import org.jboss.jandex.IndexView; @@ -89,9 +90,13 @@ public static ResultHandle getTypeHandle(BytecodeCreator creator, Type type) { } public static ResultHandle getTypeHandle(BytecodeCreator creator, Type type, ResultHandle tccl) { + return getTypeHandle(creator, type, tccl, null); + } + + public static ResultHandle getTypeHandle(BytecodeCreator creator, Type type, ResultHandle tccl, IndexView index) { AssignableResultHandle result = creator.createVariable(Object.class); TypeVariables typeVariables = new TypeVariables(); - getTypeHandle(result, creator, type, tccl, null, typeVariables); + getTypeHandle(result, creator, type, tccl, null, typeVariables, index); typeVariables.patchReferences(creator); return result; } @@ -184,12 +189,12 @@ void patchReferences(BytecodeCreator creator) { static void getTypeHandle(AssignableResultHandle variable, BytecodeCreator creator, Type type, ResultHandle tccl, TypeCache cache) { TypeVariables typeVariables = new TypeVariables(); - getTypeHandle(variable, creator, type, tccl, cache, typeVariables); + getTypeHandle(variable, creator, type, tccl, cache, typeVariables, null); typeVariables.patchReferences(creator); } private static void getTypeHandle(AssignableResultHandle variable, BytecodeCreator creator, Type type, - ResultHandle tccl, TypeCache cache, TypeVariables typeVariables) { + ResultHandle tccl, TypeCache cache, TypeVariables typeVariables, IndexView index) { if (cache != null) { ResultHandle cachedType = cache.get(type, creator); BranchResult cachedNull = creator.ifNull(cachedType); @@ -218,7 +223,7 @@ private static void getTypeHandle(AssignableResultHandle variable, BytecodeCreat boundsHandle = creator.newArray(java.lang.reflect.Type.class, creator.load(bounds.size())); for (int i = 0; i < bounds.size(); i++) { AssignableResultHandle boundHandle = creator.createVariable(Object.class); - getTypeHandle(boundHandle, creator, bounds.get(i), tccl, cache, typeVariables); + getTypeHandle(boundHandle, creator, bounds.get(i), tccl, cache, typeVariables, index); creator.writeArrayValue(boundsHandle, i, boundHandle); } } @@ -234,7 +239,7 @@ private static void getTypeHandle(AssignableResultHandle variable, BytecodeCreat } else if (Kind.PARAMETERIZED_TYPE.equals(type.kind())) { // E.g. List -> new ParameterizedTypeImpl(List.class, String.class) - getParameterizedType(variable, creator, tccl, type.asParameterizedType(), cache, typeVariables); + getParameterizedType(variable, creator, tccl, type.asParameterizedType(), cache, typeVariables, index); } else if (Kind.ARRAY.equals(type.kind())) { ArrayType array = type.asArrayType(); @@ -249,7 +254,7 @@ private static void getTypeHandle(AssignableResultHandle variable, BytecodeCreat // E.g. List[] -> new GenericArrayTypeImpl(new ParameterizedTypeImpl(List.class, String.class)) Type componentType = type.asArrayType().constituent(); AssignableResultHandle componentTypeHandle = creator.createVariable(Object.class); - getTypeHandle(componentTypeHandle, creator, componentType, tccl, cache, typeVariables); + getTypeHandle(componentTypeHandle, creator, componentType, tccl, cache, typeVariables, index); arrayHandle = creator.newInstance( MethodDescriptor.ofConstructor(GenericArrayTypeImpl.class, java.lang.reflect.Type.class), componentTypeHandle); @@ -265,14 +270,14 @@ private static void getTypeHandle(AssignableResultHandle variable, BytecodeCreat ResultHandle wildcardHandle; if (wildcardType.superBound() == null) { AssignableResultHandle extendsBoundHandle = creator.createVariable(Object.class); - getTypeHandle(extendsBoundHandle, creator, wildcardType.extendsBound(), tccl, cache, typeVariables); + getTypeHandle(extendsBoundHandle, creator, wildcardType.extendsBound(), tccl, cache, typeVariables, index); wildcardHandle = creator.invokeStaticMethod( MethodDescriptor.ofMethod(WildcardTypeImpl.class, "withUpperBound", java.lang.reflect.WildcardType.class, java.lang.reflect.Type.class), extendsBoundHandle); } else { AssignableResultHandle superBoundHandle = creator.createVariable(Object.class); - getTypeHandle(superBoundHandle, creator, wildcardType.superBound(), tccl, cache, typeVariables); + getTypeHandle(superBoundHandle, creator, wildcardType.superBound(), tccl, cache, typeVariables, index); wildcardHandle = creator.invokeStaticMethod( MethodDescriptor.ofMethod(WildcardTypeImpl.class, "withLowerBound", java.lang.reflect.WildcardType.class, java.lang.reflect.Type.class), @@ -331,12 +336,12 @@ private static void getTypeHandle(AssignableResultHandle variable, BytecodeCreat } private static void getParameterizedType(AssignableResultHandle variable, BytecodeCreator creator, ResultHandle tccl, - ParameterizedType parameterizedType, TypeCache cache, TypeVariables typeVariables) { + ParameterizedType parameterizedType, TypeCache cache, TypeVariables typeVariables, IndexView index) { List arguments = parameterizedType.arguments(); ResultHandle typeArgsHandle = creator.newArray(java.lang.reflect.Type.class, creator.load(arguments.size())); for (int i = 0; i < arguments.size(); i++) { AssignableResultHandle argumentHandle = creator.createVariable(Object.class); - getTypeHandle(argumentHandle, creator, arguments.get(i), tccl, cache, typeVariables); + getTypeHandle(argumentHandle, creator, arguments.get(i), tccl, cache, typeVariables, index); creator.writeArrayValue(typeArgsHandle, i, argumentHandle); } Type rawType = Type.create(parameterizedType.name(), Kind.CLASS); @@ -350,10 +355,25 @@ private static void getParameterizedType(AssignableResultHandle variable, Byteco cache.put(rawType, rawTypeHandle, creator); } } + AssignableResultHandle ownerTypeHandle = creator.createVariable(Object.class); + if (parameterizedType.owner() != null) { + getTypeHandle(ownerTypeHandle, creator, parameterizedType.owner(), tccl, cache, typeVariables, index); + } else if (index != null) { + ClassInfo clazz = index.getClassByName(parameterizedType.name()); + if (clazz != null && clazz.enclosingClass() != null) { + // this is not entirely precise, but generic classes with more than 1 level of nesting are very rare + ClassType owner = ClassType.create(clazz.enclosingClass()); + getTypeHandle(ownerTypeHandle, creator, owner, tccl, cache, typeVariables, index); + } else { + creator.assign(ownerTypeHandle, creator.loadNull()); + } + } else { + creator.assign(ownerTypeHandle, creator.loadNull()); + } ResultHandle parameterizedTypeHandle = creator.newInstance( MethodDescriptor.ofConstructor(ParameterizedTypeImpl.class, java.lang.reflect.Type.class, - java.lang.reflect.Type[].class), - rawTypeHandle, typeArgsHandle); + java.lang.reflect.Type[].class, java.lang.reflect.Type.class), + rawTypeHandle, typeArgsHandle, ownerTypeHandle); if (cache != null) { cache.put(parameterizedType, parameterizedTypeHandle, creator); } @@ -363,7 +383,7 @@ private static void getParameterizedType(AssignableResultHandle variable, Byteco public static void getParameterizedType(AssignableResultHandle variable, BytecodeCreator creator, ResultHandle tccl, ParameterizedType parameterizedType) { TypeVariables typeVariables = new TypeVariables(); - getParameterizedType(variable, creator, tccl, parameterizedType, null, typeVariables); + getParameterizedType(variable, creator, tccl, parameterizedType, null, typeVariables, null); typeVariables.patchReferences(creator); } @@ -371,7 +391,7 @@ public static ResultHandle getParameterizedType(BytecodeCreator creator, ResultH ParameterizedType parameterizedType) { AssignableResultHandle result = creator.createVariable(Object.class); TypeVariables typeVariables = new TypeVariables(); - getParameterizedType(result, creator, tccl, parameterizedType, null, typeVariables); + getParameterizedType(result, creator, tccl, parameterizedType, null, typeVariables, null); typeVariables.patchReferences(creator); return result; } From 44256159e4b6663043a7930c98bf4439a1d86177 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 28 Jun 2024 16:51:21 +0200 Subject: [PATCH 10/94] ArC: make some SubclassGenerator methods easier to reuse --- .../arc/processor/AbstractGenerator.java | 2 +- .../arc/processor/ClientProxyGenerator.java | 2 +- .../arc/processor/SubclassGenerator.java | 171 ++++++++++-------- 3 files changed, 98 insertions(+), 77 deletions(-) diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AbstractGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AbstractGenerator.java index d21ed1d024824..5c800668859f7 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AbstractGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AbstractGenerator.java @@ -52,7 +52,7 @@ static String generatedNameFromTarget(String targetPackage, String baseName, Str } } - protected String getBaseName(BeanInfo bean, String beanClassName) { + protected String getBaseName(String beanClassName) { String name = Types.getSimpleName(beanClassName); return name.substring(0, name.lastIndexOf(BeanGenerator.BEAN_SUFFIX)); } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java index 3cf0934171c44..b7c262df9af8b 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java @@ -91,7 +91,7 @@ Collection generate(BeanInfo bean, String beanClassName, ProviderType providerType = new ProviderType(bean.getProviderType()); ClassInfo providerClass = getClassByName(bean.getDeployment().getBeanArchiveIndex(), providerType.name()); - String baseName = getBaseName(bean, beanClassName); + String baseName = getBaseName(beanClassName); String targetPackage = bean.getClientProxyPackageName(); String generatedName = generatedNameFromTarget(targetPackage, baseName, CLIENT_PROXY_SUFFIX); if (existingClasses.contains(generatedName)) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java index 04b8bc44075ef..b14ee140c82b4 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java @@ -106,7 +106,7 @@ Collection generate(BeanInfo bean, String beanClassName) { Type providerType = bean.getProviderType(); ClassInfo providerClass = getClassByName(bean.getDeployment().getBeanArchiveIndex(), providerType.name()); String providerTypeName = providerClass.name().toString(); - String baseName = getBaseName(bean, beanClassName); + String baseName = getBaseName(beanClassName); String generatedName = generatedName(providerType.name(), baseName); if (existingClasses.contains(generatedName)) { return Collections.emptyList(); @@ -188,8 +188,9 @@ protected FieldDescriptor createConstructor(ClassOutput classOutput, BeanInfo be Map forwardingMethods = new HashMap<>(); List interceptedOrDecoratedMethods = bean.getInterceptedOrDecoratedMethods(); for (MethodInfo method : interceptedOrDecoratedMethods) { - forwardingMethods.put(MethodDescriptor.of(method), - createForwardingMethod(subclass, providerTypeName, method)); + forwardingMethods.put(MethodDescriptor.of(method), createForwardingMethod(subclass, providerTypeName, method, + (bytecode, virtualMethod, params) -> bytecode.invokeSpecialMethod(virtualMethod, bytecode.getThis(), + params))); } // If a decorator is associated: @@ -253,68 +254,10 @@ protected FieldDescriptor createConstructor(ClassOutput classOutput, BeanInfo be // Shared interceptor bindings literals Map bindingsLiterals = new HashMap<>(); - Function bindingsLiteralFun = new Function() { - @Override - public ResultHandle apply(AnnotationInstanceEquivalenceProxy binding) { - // Create annotation literal if needed - ClassInfo bindingClass = bean.getDeployment().getInterceptorBinding(binding.get().name()); - return annotationLiterals.create(constructor, bindingClass, binding.get()); - } - }; - - Function, String> interceptorChainKeysFun = new Function, String>() { - @Override - public String apply(List interceptors) { - String key = "i" + chainIdx.i++; - if (interceptors.size() == 1) { - // List chain = Collections.singletonList(...); - InterceptorInfo interceptor = interceptors.get(0); - ResultHandle interceptorInstance = interceptorInstanceToResultHandle.get(interceptor.getIdentifier()); - ResultHandle interceptionInvocation = constructor.invokeStaticMethod( - MethodDescriptors.INTERCEPTOR_INVOCATION_AROUND_INVOKE, - interceptorToResultHandle.get(interceptor.getIdentifier()), interceptorInstance); - constructor.invokeInterfaceMethod(MethodDescriptors.MAP_PUT, interceptorChainMap, constructor.load(key), - constructor.invokeStaticMethod(MethodDescriptors.COLLECTIONS_SINGLETON_LIST, - interceptionInvocation)); - } else { - // List chain = new ArrayList<>(); - ResultHandle chainHandle = constructor.newInstance(MethodDescriptor.ofConstructor(ArrayList.class)); - for (InterceptorInfo interceptor : interceptors) { - // m1Chain.add(InvocationContextImpl.InterceptorInvocation.aroundInvoke(p3,interceptorInstanceMap.get(InjectableInterceptor.getIdentifier()))) - ResultHandle interceptorInstance = interceptorInstanceToResultHandle.get(interceptor.getIdentifier()); - ResultHandle interceptionInvocation = constructor.invokeStaticMethod( - MethodDescriptors.INTERCEPTOR_INVOCATION_AROUND_INVOKE, - interceptorToResultHandle.get(interceptor.getIdentifier()), interceptorInstance); - constructor.invokeInterfaceMethod(MethodDescriptors.LIST_ADD, chainHandle, interceptionInvocation); - } - constructor.invokeInterfaceMethod(MethodDescriptors.MAP_PUT, interceptorChainMap, constructor.load(key), - chainHandle); - } - return key; - } - }; - - Function, String> bindingsFun = new Function, String>() { - @Override - public String apply(Set bindings) { - String key = "b" + bindingIdx.i++; - if (bindings.size() == 1) { - constructor.invokeInterfaceMethod(MethodDescriptors.MAP_PUT, bindingsMap, constructor.load(key), - constructor.invokeStaticMethod(MethodDescriptors.COLLECTIONS_SINGLETON, - bindingsLiterals.computeIfAbsent(bindings.iterator().next(), bindingsLiteralFun))); - } else { - ResultHandle bindingsArray = constructor.newArray(Object.class, bindings.size()); - int bindingsIndex = 0; - for (AnnotationInstanceEquivalenceProxy binding : bindings) { - constructor.writeArrayValue(bindingsArray, bindingsIndex++, - bindingsLiterals.computeIfAbsent(binding, bindingsLiteralFun)); - } - constructor.invokeInterfaceMethod(MethodDescriptors.MAP_PUT, bindingsMap, constructor.load(key), - constructor.invokeStaticMethod(MethodDescriptors.SETS_OF, bindingsArray)); - } - return key; - } - }; + Function, String> interceptorChainKeysFun = createInterceptorChainKeysFun(chainIdx, + constructor, interceptorChainMap, interceptorInstanceToResultHandle, interceptorToResultHandle); + Function, String> bindingsFun = createBindingsFun(bindingIdx, + constructor, bindingsMap, bindingsLiterals, bean, annotationLiterals); int methodIdx = 1; for (MethodInfo method : interceptedOrDecoratedMethods) { @@ -502,8 +445,8 @@ public String apply(Set bindings) { reflectionRegistration.registerMethod(method); // Finally create the intercepted method - createInterceptedMethod(bean, method, subclass, providerTypeName, metadataField, - constructedField.getFieldDescriptor(), forwardDescriptor); + createInterceptedMethod(method, subclass, metadataField, constructedField.getFieldDescriptor(), + forwardDescriptor, BytecodeCreator::getThis); } else { // Only decorators are applied MethodCreator decoratedMethod = subclass.getMethodCreator(methodDescriptor); @@ -558,6 +501,78 @@ public String apply(Set bindings) { return preDestroysField != null ? preDestroysField.getFieldDescriptor() : null; } + static Function, String> createBindingsFun(IntegerHolder bindingIdx, + MethodCreator bytecode, ResultHandle bindingsMap, + Map bindingsLiterals, + BeanInfo bean, AnnotationLiteralProcessor annotationLiterals) { + Function bindingsLiteralFun = new Function() { + @Override + public ResultHandle apply(AnnotationInstanceEquivalenceProxy binding) { + // Create annotation literal if needed + ClassInfo bindingClass = bean.getDeployment().getInterceptorBinding(binding.get().name()); + return annotationLiterals.create(bytecode, bindingClass, binding.get()); + } + }; + + return new Function, String>() { + @Override + public String apply(Set bindings) { + String key = "b" + bindingIdx.i++; + if (bindings.size() == 1) { + bytecode.invokeInterfaceMethod(MethodDescriptors.MAP_PUT, bindingsMap, bytecode.load(key), + bytecode.invokeStaticMethod(MethodDescriptors.COLLECTIONS_SINGLETON, + bindingsLiterals.computeIfAbsent(bindings.iterator().next(), bindingsLiteralFun))); + } else { + ResultHandle bindingsArray = bytecode.newArray(Object.class, bindings.size()); + int bindingsIndex = 0; + for (AnnotationInstanceEquivalenceProxy binding : bindings) { + bytecode.writeArrayValue(bindingsArray, bindingsIndex++, + bindingsLiterals.computeIfAbsent(binding, bindingsLiteralFun)); + } + bytecode.invokeInterfaceMethod(MethodDescriptors.MAP_PUT, bindingsMap, bytecode.load(key), + bytecode.invokeStaticMethod(MethodDescriptors.SETS_OF, bindingsArray)); + } + return key; + } + }; + } + + static Function, String> createInterceptorChainKeysFun(IntegerHolder chainIdx, MethodCreator bytecode, + ResultHandle interceptorChainMap, Map interceptorInstanceToResultHandle, + Map interceptorToResultHandle) { + return new Function, String>() { + @Override + public String apply(List interceptors) { + String key = "i" + chainIdx.i++; + if (interceptors.size() == 1) { + // List chain = Collections.singletonList(...); + InterceptorInfo interceptor = interceptors.get(0); + ResultHandle interceptorInstance = interceptorInstanceToResultHandle.get(interceptor.getIdentifier()); + ResultHandle interceptionInvocation = bytecode.invokeStaticMethod( + MethodDescriptors.INTERCEPTOR_INVOCATION_AROUND_INVOKE, + interceptorToResultHandle.get(interceptor.getIdentifier()), interceptorInstance); + bytecode.invokeInterfaceMethod(MethodDescriptors.MAP_PUT, interceptorChainMap, bytecode.load(key), + bytecode.invokeStaticMethod(MethodDescriptors.COLLECTIONS_SINGLETON_LIST, + interceptionInvocation)); + } else { + // List chain = new ArrayList<>(); + ResultHandle chainHandle = bytecode.newInstance(MethodDescriptor.ofConstructor(ArrayList.class)); + for (InterceptorInfo interceptor : interceptors) { + // m1Chain.add(InvocationContextImpl.InterceptorInvocation.aroundInvoke(p3,interceptorInstanceMap.get(InjectableInterceptor.getIdentifier()))) + ResultHandle interceptorInstance = interceptorInstanceToResultHandle.get(interceptor.getIdentifier()); + ResultHandle interceptionInvocation = bytecode.invokeStaticMethod( + MethodDescriptors.INTERCEPTOR_INVOCATION_AROUND_INVOKE, + interceptorToResultHandle.get(interceptor.getIdentifier()), interceptorInstance); + bytecode.invokeInterfaceMethod(MethodDescriptors.LIST_ADD, chainHandle, interceptionInvocation); + } + bytecode.invokeInterfaceMethod(MethodDescriptors.MAP_PUT, interceptorChainMap, bytecode.load(key), + chainHandle); + } + return key; + } + }; + } + private ResultHandle invokeInterceptorMethod(BytecodeCreator creator, MethodInfo interceptorMethod, boolean isApplicationClass, ResultHandle invocationContext, ResultHandle targetInstance) { ResultHandle ret; @@ -847,7 +862,13 @@ private boolean isDecorated(Set decoratedMethodDescriptors, Me return false; } - private MethodDescriptor createForwardingMethod(ClassCreator subclass, String providerTypeName, MethodInfo method) { + @FunctionalInterface + interface ForwardInvokeGenerator { + ResultHandle generate(BytecodeCreator bytecode, MethodDescriptor virtualMethod, ResultHandle[] params); + } + + static MethodDescriptor createForwardingMethod(ClassCreator subclass, String providerTypeName, MethodInfo method, + ForwardInvokeGenerator forwardInvokeGenerator) { MethodDescriptor methodDescriptor = MethodDescriptor.of(method); String forwardMethodName = method.name() + "$$superforward"; MethodDescriptor forwardDescriptor = MethodDescriptor.ofMethod(subclass.getClassName(), forwardMethodName, @@ -861,13 +882,13 @@ private MethodDescriptor createForwardingMethod(ClassCreator subclass, String pr } MethodDescriptor virtualMethod = MethodDescriptor.ofMethod(providerTypeName, methodDescriptor.getName(), methodDescriptor.getReturnType(), methodDescriptor.getParameterTypes()); - forward.returnValue(forward.invokeSpecialMethod(virtualMethod, forward.getThis(), params)); + forward.returnValue(forwardInvokeGenerator.generate(forward, virtualMethod, params)); return forwardDescriptor; } - private void createInterceptedMethod(BeanInfo bean, MethodInfo method, ClassCreator subclass, - String providerTypeName, FieldDescriptor metadataField, FieldDescriptor constructedField, - MethodDescriptor forwardMethod) { + static void createInterceptedMethod(MethodInfo method, ClassCreator subclass, FieldDescriptor metadataField, + FieldDescriptor constructedField, MethodDescriptor forwardMethod, + Function getTarget) { MethodDescriptor originalMethodDescriptor = MethodDescriptor.of(method); MethodCreator interceptedMethod = subclass.getMethodCreator(originalMethodDescriptor); @@ -940,7 +961,7 @@ private void createInterceptedMethod(BeanInfo bean, MethodInfo method, ClassCrea // InvocationContexts.performAroundInvoke(...) ResultHandle methodMetadataHandle = tryCatch.readInstanceField(metadataField, tryCatch.getThis()); ResultHandle ret = tryCatch.invokeStaticMethod(MethodDescriptors.INVOCATION_CONTEXTS_PERFORM_AROUND_INVOKE, - tryCatch.getThis(), paramsHandle, methodMetadataHandle); + getTarget.apply(tryCatch), paramsHandle, methodMetadataHandle); tryCatch.returnValue(ret); } @@ -989,8 +1010,8 @@ protected void createDestroy(ClassOutput classOutput, BeanInfo bean, ClassCreato } } - private static class IntegerHolder { - private int i = 1; + static class IntegerHolder { + int i = 1; } } From 1320d048a19536c8961e9aa9bf721d626757699c Mon Sep 17 00:00:00 2001 From: Shivansh Date: Wed, 3 Jul 2024 18:46:28 +0530 Subject: [PATCH 11/94] Created ad-hoc signed jar for testing filtering of jar file --- core/deployment/pom.xml | 35 +++----- .../pkg/steps/JarResultBuildStepTest.java | 82 +++++++++++++++++-- 2 files changed, 88 insertions(+), 29 deletions(-) diff --git a/core/deployment/pom.xml b/core/deployment/pom.xml index d3a61ff993555..1fc87443c5d34 100644 --- a/core/deployment/pom.xml +++ b/core/deployment/pom.xml @@ -147,34 +147,21 @@ org.junit.jupiter junit-jupiter + + org.bouncycastle + bcpkix-jdk18on + test + + + org.jboss.shrinkwrap + shrinkwrap-depchain + test + pom + - - maven-dependency-plugin - - - download-signed-jar - generate-test-resources - - copy - - - - - org.eclipse.jgit - org.eclipse.jgit.ssh.apache - 6.10.0.202406032230-r - jar - signed.jar - - - ${project.build.testOutputDirectory} - - - - maven-surefire-plugin diff --git a/core/deployment/src/test/java/io/quarkus/deployment/pkg/steps/JarResultBuildStepTest.java b/core/deployment/src/test/java/io/quarkus/deployment/pkg/steps/JarResultBuildStepTest.java index 7cfb2c4ece496..9e50d306377ff 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/pkg/steps/JarResultBuildStepTest.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/pkg/steps/JarResultBuildStepTest.java @@ -2,15 +2,44 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.File; +import java.io.FileOutputStream; +import java.math.BigInteger; import java.nio.file.Path; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.Calendar; +import java.util.Date; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; +import java.util.zip.ZipFile; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.exporter.ZipExporter; +import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import jdk.security.jarsigner.JarSigner; + /** * Test for {@link JarResultBuildStep} */ @@ -18,11 +47,19 @@ class JarResultBuildStepTest { @Test void should_unsign_jar_when_filtered(@TempDir Path tempDir) throws Exception { - Path signedJarFilePath = Path.of(getClass().getClassLoader().getResource("signed.jar").toURI()); - Path jarFilePath = tempDir.resolve("unsigned.jar"); - JarResultBuildStep.filterJarFile(signedJarFilePath, jarFilePath, - Set.of("org/eclipse/jgit/transport/sshd/SshdSessionFactory.class")); - try (JarFile jarFile = new JarFile(jarFilePath.toFile())) { + JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "myarchive.jar") + .addClasses(Integer.class); + Path unsignedJarPath = tempDir.resolve("unsigned-jar.jar"); + Path signedJarPath = tempDir.resolve("signed-jar.jar"); + Path unsignedJarToTestPath = tempDir.resolve("unsigned.jar"); + archive.as(ZipExporter.class).exportTo(new File(unsignedJarPath.toUri()), true); + JarSigner signer = new JarSigner.Builder(createPrivateKeyEntry()).build(); + try (ZipFile in = new ZipFile(unsignedJarPath.toFile()); + FileOutputStream out = new FileOutputStream(signedJarPath.toFile())) { + signer.sign(in, out); + } + JarResultBuildStep.filterJarFile(signedJarPath, unsignedJarToTestPath, Set.of("java/lang/Integer.class")); + try (JarFile jarFile = new JarFile(unsignedJarToTestPath.toFile())) { assertThat(jarFile.stream().map(JarEntry::getName)).doesNotContain("META-INF/ECLIPSE_.RSA", "META-INF/ECLIPSE_.SF"); // Check that the manifest is still present Manifest manifest = jarFile.getManifest(); @@ -31,4 +68,39 @@ void should_unsign_jar_when_filtered(@TempDir Path tempDir) throws Exception { } } + private static KeyStore.PrivateKeyEntry createPrivateKeyEntry() + throws NoSuchAlgorithmException, CertificateException, OperatorCreationException, CertIOException { + KeyPairGenerator ky = KeyPairGenerator.getInstance("RSA"); + ky.initialize(2048); + KeyPair keyPair = ky.generateKeyPair(); + Certificate[] chain = { createCertificate(keyPair, "cn=Unknown") }; + KeyStore.PrivateKeyEntry keyEntry = new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), chain); + return keyEntry; + } + + private static Certificate createCertificate(KeyPair keyPair, String subjectDN) + throws OperatorCreationException, CertificateException, CertIOException { + Provider bcProvider = new BouncyCastleProvider(); + Security.addProvider(bcProvider); + long now = System.currentTimeMillis(); + X500Name dnName = new X500Name(subjectDN); + BigInteger certSerialNumber = new BigInteger(Long.toString(now)); + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.YEAR, 1); + Date endDate = calendar.getTime(); + String signatureAlgorithm = "SHA256WithRSA"; + + ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(keyPair.getPrivate()); + + JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dnName, certSerialNumber, new Date(now), + endDate, + dnName, keyPair.getPublic()); + + BasicConstraints basicConstraints = new BasicConstraints(true); + + certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, basicConstraints); + return new JcaX509CertificateConverter().setProvider(bcProvider).getCertificate(certBuilder.build(contentSigner)); + + } + } From ed8410f7bf5f40468baee5923ea6d93ee60be0c2 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 28 Jun 2024 16:51:41 +0200 Subject: [PATCH 12/94] ArC: support interception of producer methods and synthetic beans --- docs/src/main/asciidoc/cdi-reference.adoc | 185 +++++++++ ...ProducerWithFinalInterceptedClassTest.java | 74 ++++ ...roducerWithFinalInterceptedMethodTest.java | 74 ++++ ...ivateZeroParamCtorAndInterceptionTest.java | 72 ++++ ...thoutZeroParamCtorAndInterceptionTest.java | 72 ++++ .../arc/processor/BeanConfigurator.java | 19 +- .../arc/processor/BeanConfiguratorBase.java | 65 ++++ .../quarkus/arc/processor/BeanDeployment.java | 5 + .../io/quarkus/arc/processor/BeanInfo.java | 89 ++++- .../quarkus/arc/processor/BeanProcessor.java | 34 ++ .../java/io/quarkus/arc/processor/Beans.java | 48 ++- .../arc/processor/BindingsDiscovery.java | 54 +++ .../io/quarkus/arc/processor/BuiltinBean.java | 37 ++ .../quarkus/arc/processor/DecoratorInfo.java | 2 +- .../io/quarkus/arc/processor/DotNames.java | 4 + .../processor/InterceptionProxyGenerator.java | 357 ++++++++++++++++++ .../arc/processor/InterceptionProxyInfo.java | 46 +++ .../arc/processor/InterceptorInfo.java | 4 +- .../io/quarkus/arc/processor/Methods.java | 34 +- .../java/io/quarkus/arc/BindingsSource.java | 38 ++ .../io/quarkus/arc/InterceptionProxy.java | 40 ++ .../arc/SyntheticCreationalContext.java | 8 + .../impl/SyntheticCreationalContextImpl.java | 12 + ...evelInterceptorsAndBindingsSourceTest.java | 117 ++++++ ...thClassAndMethodLevelInterceptorsTest.java | 102 +++++ ...evelInterceptorsAndBindingsSourceTest.java | 97 +++++ ...roducerWithClassLevelInterceptorsTest.java | 90 +++++ ...ProducerWithFinalInterceptedClassTest.java | 71 ++++ ...roducerWithFinalInterceptedMethodTest.java | 71 ++++ ...lassInterceptionAndBindingsSourceTest.java | 105 ++++++ ...ducerWithGenericClassInterceptionTest.java | 98 +++++ ...cerWithInterceptionAndDestructionTest.java | 100 +++++ ...faceInterceptionAndBindingsSourceTest.java | 111 ++++++ ...ProducerWithInterfaceInterceptionTest.java | 104 +++++ ...evelInterceptorsAndBindingsSourceTest.java | 115 ++++++ ...oducerWithMethodLevelInterceptorsTest.java | 100 +++++ ...thMultipleInterceptionProxyParamsTest.java | 47 +++ ...erizedConstructorsAndInterceptionTest.java | 155 ++++++++ ...thParameterizedInterceptedMethodsTest.java | 102 +++++ ...ivateZeroParamCtorAndInterceptionTest.java | 74 ++++ ...thoutZeroParamCtorAndInterceptionTest.java | 74 ++++ ...ynthBeanWithFinalInterceptedClassTest.java | 82 ++++ ...nthBeanWithFinalInterceptedMethodTest.java | 82 ++++ ...lassInterceptionAndBindingsSourceTest.java | 124 ++++++ ...erizedConstructorsAndInterceptionTest.java | 167 ++++++++ ...thParameterizedInterceptedMethodsTest.java | 114 ++++++ 46 files changed, 3620 insertions(+), 55 deletions(-) create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedClassTest.java create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedMethodTest.java create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithPrivateZeroParamCtorAndInterceptionTest.java create mode 100644 extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java create mode 100644 independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BindingsDiscovery.java create mode 100644 independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptionProxyGenerator.java create mode 100644 independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptionProxyInfo.java create mode 100644 independent-projects/arc/runtime/src/main/java/io/quarkus/arc/BindingsSource.java create mode 100644 independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InterceptionProxy.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsAndBindingsSourceTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsAndBindingsSourceTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithFinalInterceptedClassTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithFinalInterceptedMethodTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithGenericClassInterceptionAndBindingsSourceTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithGenericClassInterceptionTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterceptionAndDestructionTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterfaceInterceptionAndBindingsSourceTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterfaceInterceptionTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMethodLevelInterceptorsAndBindingsSourceTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMethodLevelInterceptorsTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMultipleInterceptionProxyParamsTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithParameterizedConstructorsAndInterceptionTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithParameterizedInterceptedMethodsTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithPrivateZeroParamCtorAndInterceptionTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithFinalInterceptedClassTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithFinalInterceptedMethodTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithGenericClassInterceptionAndBindingsSourceTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithParameterizedConstructorsAndInterceptionTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithParameterizedInterceptedMethodsTest.java diff --git a/docs/src/main/asciidoc/cdi-reference.adoc b/docs/src/main/asciidoc/cdi-reference.adoc index e0057958e21e4..41161c3799adf 100644 --- a/docs/src/main/asciidoc/cdi-reference.adoc +++ b/docs/src/main/asciidoc/cdi-reference.adoc @@ -726,6 +726,7 @@ class Producers { } ---- +[[interception_of_static_methods]] === Interception of Static Methods The Interceptors specification is clear that _around-invoke_ methods must not be declared static. @@ -1098,6 +1099,190 @@ public class MyService { In the above example, any code calling the `doSomething()` method triggers interception - in this case, the method becomes transactional. This is regardless of whether the invocation originated directly from the `MyService` bean (such as `MyService#doSomethingElse`) or from some other bean. +=== Intercepting Producer Methods and Synthetic Beans + +By default, interception is only supported for managed beans (also known as class-based beans). +To support interception of producer methods and synthetic beans, the CDI specification includes an `InterceptionFactory`, which is a runtime oriented concept and therefore cannot be supported in Quarkus. + +Instead, Quarkus has its own API: `InterceptionProxy` and `@BindingsSource`. +The `InterceptionProxy` is very similar to `InterceptionFactory`: it creates a proxy that applies `@AroundInvoke` interceptors before forwarding the method call to the target instance. +The `@BindingsSource` annotation allows setting interceptor bindings in case the intercepted class is external and cannot be changed. + +[source,java] +---- +import io.quarkus.arc.InterceptionProxy; + +@ApplicationScoped +class MyProducer { + @Produces + MyClass produce(InterceptionProxy proxy) { // <1> + return proxy.create(new MyClass()); // <2> + } +} +---- + +<1> Declares an injection point of type `InterceptionProxy`. + This means that at build time, a subclass of `MyClass` is generated that does the interception and forwarding. + Note that the type argument must be identical to the return type of the producer method. +<2> Creates an instance of the interception proxy for the given instance of `MyClass`. + The method calls will be forwarded to this target instance after all interceptors run. + +In this example, interceptor bindings are read from the `MyClass` class. + +IMPORTANT: Note that `InterceptionProxy` only supports `@AroundInvoke` interceptors declared on interceptor classes. +Other kinds of interception, as well as `@AroundInvoke` interceptors declared on the target class and its superclasses, are not supported. + +The intercepted class should be https://jakarta.ee/specifications/cdi/4.1/jakarta-cdi-spec-4.1#unproxyable[proxyable] and therefore should not be `final`, should not have non-private `final` methods, and should have a non-private zero-parameter constructor. +If it isn't, a bytecode transformation will attempt to fix it if <>, but note that adding a zero-parameter constructor is not always possible. + +It is often the case that the produced classes come from external libraries and don't contain interceptor binding annotations at all. +To support such cases, the `@BindingsSource` annotation may be declared on the `InterceptionProxy` parameter: + +[source,java] +---- +import io.quarkus.arc.BindingsSource; +import io.quarkus.arc.InterceptionProxy; + +abstract class MyClassBindings { // <1> + @MyInterceptorBinding + abstract String doSomething(); +} + +@ApplicationScoped +class MyProducer { + @Produces + MyClass produce(@BindingsSource(MyClassBindings.class) InterceptionProxy proxy) { // <2> + return proxy.create(new MyClass()); + } +} +---- + +<1> A class that mirrors the `MyClass` structure and contains interceptor bindings. +<2> The `@BindingsSource` annotation says that interceptor bindings for `MyClass` should be read from `MyClassBindings`. + +The concept of _bindings source_ is a build-time friendly equivalent of `InterceptionFactory.configure()`. + +NOTE: Producer method interception and synthetic bean interception only works for instance methods. +<> is not supported for producer methods and synthetic beans. + +==== Declaring `@BindingsSource` + +The `@BindingsSource` annotation specifies a class that mirrors the structure of the intercepted class. +Interceptor bindings are then read from that class and treated as if they were declared on the intercepted class. + +Specifically: class-level interceptor bindings declared on the bindings source class are treated as class-level bindings of the intercepted class. +Method-level interceptor bindings declared on the bindings source class are treated as method-level bindings of a method with the same name, return type, parameter types and `static` flag of the intercepted class. + +It is common to make the bindings source class and methods `abstract` so that you don't have to write method bodies: + +[source,java] +---- +abstract class MyClassBindings { + @MyInterceptorBinding + abstract String doSomething(); +} +---- + +Since this class is never instantiated and its method are never invoked, this is okay, but it's also possible to create a non-`abstract` class: + +[source,java] +---- +class MyClassBindings { + @MyInterceptorBinding + String doSomething() { + return null; // <1> + } +} +---- + +<1> The method body does not matter. + +Note that for generic classes, the type variable names must also be identical. +For example, for the following class: + +[source,java] +---- +class MyClass { + T doSomething() { + ... + } + + void doSomethingElse(T param) { + ... + } +} +---- + +the bindings source class must also use `T` as the name of the type variable: + +[source,java] +---- +abstract class MyClassBindings { + @MyInterceptorBinding + abstract T doSomething(); +} +---- + +You don't need to declare methods that are not annotated simply because they exist on the intercepted class. +If you want to add method-level bindings to a subset of methods, you only have to declare the methods that are supposed to have an interceptor binding. +If you only want to add class-level bindings, you don't have to declare any methods at all. + +These annotations can be present on a bindings source class: + +* _interceptor bindings_: on the class and on the methods +* _stereotypes_: on the class +* `@NoClassInterceptors`: on the methods + +Any other annotation present on a bindings source class is ignored. + +==== Synthetic Beans + +Using `InterceptionProxy` in synthetic beans is similar. + +First, you have to declare that your synthetic bean injects the `InterceptionProxy`: + +[source,java] +---- +public void register(RegistrationContext context) { + context.configure(MyClass.class) + .types(MyClass.class) + .injectInterceptionProxy() // <1> + .creator(MyClassCreator.class) + .done(); +} +---- + +<1> Once again, this means that at build time, a subclass of `MyClass` is generated that does the interception and forwarding. + +Second, you have to obtain the `InterceptionProxy` from the `SyntheticCreationalContext` in the `BeanCreator` and use it: + +[source,java] +---- +public MyClass create(SyntheticCreationalContext context) { + InterceptionProxy proxy = context.getInterceptionProxy(); // <1> + return proxy.create(new MyClass()); +} +---- + +<1> Obtains the `InterceptionProxy` for `MyClass`, as declared above. + It would also be possible to use the `getInjectedReference()` method, passing a `TypeLiteral`, but `getInterceptionProxy()` is easier. + +There's also an equivalent of `@BindingsSource`. +The `injectInterceptionProxy()` method has an overload with a parameter: + +[source,java] +---- +public void register(RegistrationContext context) { + context.configure(MyClass.class) + .types(MyClass.class) + .injectInterceptionProxy(MyClassBindings.class) // <1> + .creator(MyClassCreator.class) + .done(); +} +---- + +<1> The argument is the bindings source class. + [[reactive_pitfalls]] == Pitfalls with Reactive Programming diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedClassTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedClassTest.java new file mode 100644 index 0000000000000..d4ebef0dafb03 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedClassTest.java @@ -0,0 +1,74 @@ +package io.quarkus.arc.test.interceptor.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.Unremovable; +import io.quarkus.test.QuarkusUnitTest; + +public class ProducerWithFinalInterceptedClassTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar + .addClasses(MyBinding.class, MyInterceptor.class, MyNonbean.class, MyProducer.class)); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted: hello1", nonbean.hello1()); + assertEquals("hello2", nonbean.hello2()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static final class MyNonbean { + @MyBinding + String hello1() { + return "hello1"; + } + + String hello2() { + return "hello2"; + } + } + + @Dependent + static class MyProducer { + @Produces + @Unremovable + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedMethodTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedMethodTest.java new file mode 100644 index 0000000000000..ca25329c0c71c --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithFinalInterceptedMethodTest.java @@ -0,0 +1,74 @@ +package io.quarkus.arc.test.interceptor.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.Unremovable; +import io.quarkus.test.QuarkusUnitTest; + +public class ProducerWithFinalInterceptedMethodTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar + .addClasses(MyBinding.class, MyInterceptor.class, MyNonbean.class, MyProducer.class)); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted: hello1", nonbean.hello1()); + assertEquals("hello2", nonbean.hello2()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static class MyNonbean { + @MyBinding + final String hello1() { + return "hello1"; + } + + final String hello2() { + return "hello2"; + } + } + + @Dependent + static class MyProducer { + @Produces + @Unremovable + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithPrivateZeroParamCtorAndInterceptionTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithPrivateZeroParamCtorAndInterceptionTest.java new file mode 100644 index 0000000000000..866773417b5a0 --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithPrivateZeroParamCtorAndInterceptionTest.java @@ -0,0 +1,72 @@ +package io.quarkus.arc.test.interceptor.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.Unremovable; +import io.quarkus.test.QuarkusUnitTest; + +public class ProducerWithPrivateZeroParamCtorAndInterceptionTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar + .addClasses(MyBinding.class, MyInterceptor.class, MyNonbean.class, MyProducer.class)); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted: hello", nonbean.hello()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static class MyNonbean { + private MyNonbean() { + } + + @MyBinding + String hello() { + return "hello"; + } + } + + @Dependent + static class MyProducer { + @Produces + @Unremovable + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java new file mode 100644 index 0000000000000..a9433aecbbc7d --- /dev/null +++ b/extensions/arc/deployment/src/test/java/io/quarkus/arc/test/interceptor/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java @@ -0,0 +1,72 @@ +package io.quarkus.arc.test.interceptor.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.Unremovable; +import io.quarkus.test.QuarkusUnitTest; + +public class ProducerWithoutZeroParamCtorAndInterceptionTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar + .addClasses(MyBinding.class, MyInterceptor.class, MyNonbean.class, MyProducer.class)); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted: hello", nonbean.hello()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static class MyNonbean { + MyNonbean(int ignored) { + } + + @MyBinding + String hello() { + return "hello"; + } + } + + @Dependent + static class MyProducer { + @Produces + @Unremovable + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean(0)); + } + } +} diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfigurator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfigurator.java index c402acd10822f..b033a0cdd1837 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfigurator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfigurator.java @@ -9,6 +9,10 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.ParameterizedType; +import org.jboss.jandex.Type; + +import io.quarkus.arc.InterceptionProxy; /** * Synthetic bean configurator. An alternative to {@link jakarta.enterprise.inject.spi.configurator.BeanConfigurator}. @@ -69,6 +73,18 @@ public void done() { priority = Beans.initStereotypeAlternativePriority(stereotypes, implClass, beanDeployment); } + InterceptionProxyInfo interceptionProxy = this.interceptionProxy; + if (interceptionProxy != null) { + Type providerType = this.providerType; + if (providerType == null) { + providerType = BeanInfo.initProviderType(null, implClass); + } + interceptionProxy = new InterceptionProxyInfo(providerType.name(), interceptionProxy.getBindingsSourceClass()); + addInjectionPoint(ParameterizedType.builder(InterceptionProxy.class) + .addArgument(providerType) + .build()); + } + BeanInfo.Builder builder = new BeanInfo.Builder() .implClazz(implClass) .identifier(identifier) @@ -88,7 +104,8 @@ public void done() { .removable(removable) .forceApplicationClass(forceApplicationClass) .targetPackageName(targetPackageName) - .startupPriority(startupPriority); + .startupPriority(startupPriority) + .interceptionProxy(interceptionProxy); if (!injectionPoints.isEmpty()) { builder.injections(Collections.singletonList(Injection.forSyntheticBean(injectionPoints))); diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java index 4d7f2069daa42..102df4d62e6f5 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java @@ -13,6 +13,7 @@ import jakarta.enterprise.context.NormalScope; import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.Default; +import jakarta.enterprise.inject.spi.DefinitionException; import jakarta.enterprise.inject.spi.ObserverMethod; import org.jboss.jandex.AnnotationInstance; @@ -25,6 +26,7 @@ import io.quarkus.arc.BeanDestroyer; import io.quarkus.arc.InjectableBean; import io.quarkus.arc.InjectableReferenceProvider; +import io.quarkus.arc.InterceptionProxy; import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.arc.processor.InjectionPointInfo.TypeAndQualifiers; import io.quarkus.gizmo.FieldDescriptor; @@ -56,6 +58,7 @@ public abstract class BeanConfiguratorBase injectionPoints; protected Integer startupPriority; + protected InterceptionProxyInfo interceptionProxy; protected BeanConfiguratorBase(DotName implClazz) { this.implClazz = implClazz; @@ -97,6 +100,7 @@ public THIS read(BeanConfiguratorBase base) { injectionPoints.clear(); injectionPoints.addAll(base.injectionPoints); startupPriority = base.startupPriority; + interceptionProxy = base.interceptionProxy; return self(); } @@ -278,6 +282,67 @@ public THIS startup() { return startup(ObserverMethod.DEFAULT_PRIORITY); } + /** + * Declares that this synthetic bean has an injection point of type {@code InterceptionProxy}, + * where {@code PT} is the {@linkplain #providerType(Type) provider type} of this bean. + * An instance of {@code PT} may be used as a parameter to {@link InterceptionProxy#create(Object)} + * in the {@linkplain BeanCreator creator} of this synthetic bean. + *

+ * The class of the provider type is scanned for interceptor binding annotations. + *

+ * This method may only be called once. If called multiple times, the last call wins and + * the previous calls are lost. + * + * @return self + */ + public THIS injectInterceptionProxy() { + return injectInterceptionProxy((DotName) null); + } + + /** + * Declares that this synthetic bean has an injection point of type {@code InterceptionProxy}, + * where {@code PT} is the {@linkplain #providerType(Type) provider type} of this bean. + * An instance of {@code PT} may be used as a parameter to {@link InterceptionProxy#create(Object)} + * in the {@linkplain BeanCreator creator} of this synthetic bean. + *

+ * If the {@code bindingsSource} is not {@code null}, interceptor bindings on the class + * of the provider type are ignored; instead, the {@code bindingsSource} class is scanned + * for interceptor binding annotations as defined in {@link io.quarkus.arc.BindingsSource}. + *

+ * This method may only be called once. If called multiple times, the last call wins and + * the previous calls are lost. + * + * @param bindingsSource the bindings source class, may be {@code null} + * @return self + */ + public THIS injectInterceptionProxy(Class bindingsSource) { + return injectInterceptionProxy(bindingsSource != null ? DotName.createSimple(bindingsSource) : null); + } + + /** + * Declares that this synthetic bean has an injection point of type {@code InterceptionProxy}, + * where {@code PT} is the {@linkplain #providerType(Type) provider type} of this bean. + * An instance of {@code PT} may be used as a parameter to {@link InterceptionProxy#create(Object)} + * in the {@linkplain BeanCreator creator} of this synthetic bean. + *

+ * If the {@code bindingsSource} is not {@code null}, interceptor bindings on the class + * of the provider type are ignored; instead, the {@code bindingsSource} class is scanned + * for interceptor binding annotations as defined in {@link io.quarkus.arc.BindingsSource}. + *

+ * This method may only be called once. If called multiple times, the last call wins and + * the previous calls are lost. + * + * @param bindingsSource the bindings source class, may be {@code null} + * @return self + */ + public THIS injectInterceptionProxy(DotName bindingsSource) { + if (interceptionProxy != null) { + throw new DefinitionException("Calling injectInterceptionProxy() more than once is invalid"); + } + interceptionProxy = new InterceptionProxyInfo(bindingsSource); + return self(); + } + public THIS creator(Class> creatorClazz) { return creator(mc -> { // return new FooBeanCreator().create(syntheticCreationalContext) diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index 6bced2ec696e4..c7434b5700df8 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -424,6 +424,11 @@ private Set removeUnusedInterceptors(Set remov removable = false; break; } + if (bean.getInterceptionProxy() != null && bean.getInterceptionProxy().getPseudoBean() + .getBoundInterceptors().contains(interceptor)) { + removable = false; + break; + } } } if (removable) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java index d4b95f0790de3..8b3d1ceca018d 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java @@ -32,6 +32,7 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; @@ -84,6 +85,8 @@ public class BeanInfo implements InjectionTargetInfo { private final List aroundInvokes; + private final InterceptionProxyInfo interceptionProxy; + // Following fields are only used by synthetic beans private final boolean removable; @@ -103,19 +106,18 @@ public class BeanInfo implements InjectionTargetInfo { BeanInfo(AnnotationTarget target, BeanDeployment beanDeployment, ScopeInfo scope, Set types, Set qualifiers, List injections, BeanInfo declaringBean, DisposerInfo disposer, boolean alternative, List stereotypes, String name, boolean isDefaultBean, String targetPackageName, - Integer priority, Set unrestrictedTypes) { + Integer priority, Set unrestrictedTypes, InterceptionProxyInfo interceptionProxy) { this(null, null, target, beanDeployment, scope, types, qualifiers, injections, declaringBean, disposer, alternative, stereotypes, name, isDefaultBean, null, null, Collections.emptyMap(), true, false, - targetPackageName, priority, null, unrestrictedTypes, null); + targetPackageName, priority, null, unrestrictedTypes, null, interceptionProxy); } BeanInfo(ClassInfo implClazz, Type providerType, AnnotationTarget target, BeanDeployment beanDeployment, ScopeInfo scope, Set types, Set qualifiers, List injections, BeanInfo declaringBean, - DisposerInfo disposer, boolean alternative, - List stereotypes, String name, boolean isDefaultBean, Consumer creatorConsumer, - Consumer destroyerConsumer, Map params, boolean isRemovable, - boolean forceApplicationClass, String targetPackageName, Integer priority, String identifier, - Set unrestrictedTypes, Integer startupPriority) { + DisposerInfo disposer, boolean alternative, List stereotypes, String name, boolean isDefaultBean, + Consumer creatorConsumer, Consumer destroyerConsumer, Map params, + boolean isRemovable, boolean forceApplicationClass, String targetPackageName, Integer priority, String identifier, + Set unrestrictedTypes, Integer startupPriority, InterceptionProxyInfo interceptionProxy) { this.target = Optional.ofNullable(target); if (implClazz == null && target != null) { @@ -148,6 +150,7 @@ public class BeanInfo implements InjectionTargetInfo { this.destroyerConsumer = destroyerConsumer; this.removable = isRemovable; this.params = params; + this.interceptionProxy = interceptionProxy; // Identifier must be unique for a specific deployment this.identifier = Hashes.sha1_base64((identifier != null ? identifier : "") + toString() + beanDeployment.toString()); this.interceptedMethods = Collections.emptyMap(); @@ -559,6 +562,10 @@ public OptionalInt getStartupPriority() { return startupPriority != null ? OptionalInt.of(startupPriority) : OptionalInt.empty(); } + InterceptionProxyInfo getInterceptionProxy() { + return interceptionProxy; + } + /** * @param requiredType * @param requiredQualifiers @@ -628,7 +635,17 @@ public String getClientProxyPackageName() { void validate(List errors, Consumer bytecodeTransformerConsumer, Set classesReceivingNoArgsCtor, Set injectedBeans) { - Beans.validateBean(this, errors, bytecodeTransformerConsumer, classesReceivingNoArgsCtor, injectedBeans); + + // by default, we fail deployment due to unproxyability for all beans, but in strict mode, + // we only do that for beans that are injected somewhere -- and defer the error to runtime otherwise, + // due to CDI spec requirements + boolean failIfNotProxyable = beanDeployment.strictCompatibility ? injectedBeans.contains(this) : true; + Beans.validateBean(this, errors, bytecodeTransformerConsumer, classesReceivingNoArgsCtor, failIfNotProxyable); + + if (interceptionProxy != null) { + Beans.validateBean(interceptionProxy.getPseudoBean(), errors, bytecodeTransformerConsumer, + classesReceivingNoArgsCtor, true); + } } void validateInterceptorDecorator(List errors, Consumer bytecodeTransformerConsumer) { @@ -654,12 +671,33 @@ void init(List errors, Consumer bytecodeTransfor if (disposer != null) { disposer.init(errors); } - interceptedMethods = Map - .copyOf(initInterceptedMethods(errors, bytecodeTransformerConsumer, transformUnproxyableClasses)); + interceptedMethods = Map.copyOf(initInterceptedMethods(errors, bytecodeTransformerConsumer, + transformUnproxyableClasses, null)); decoratedMethods = Map.copyOf(initDecoratedMethods()); if (errors.isEmpty()) { lifecycleInterceptors = Map.copyOf(initLifecycleInterceptors()); } + + if (interceptionProxy != null) { + IndexView index = beanDeployment.getBeanArchiveIndex(); + ClassInfo targetClass = index.getClassByName(interceptionProxy.getTargetClass()); + ClassInfo bindingsSourceClass = null; + if (interceptionProxy.getBindingsSourceClass() != null) { + bindingsSourceClass = index.getClassByName(interceptionProxy.getBindingsSourceClass()); + } + + BeanInfo pseudoBean = new BeanInfo.Builder() + .beanDeployment(beanDeployment) + .target(targetClass) + .types(new HashSet<>(Set.of(ClassType.create(interceptionProxy.getTargetClass())))) + .qualifiers(new HashSet<>()) + .build(); + pseudoBean.interceptedMethods = Map.copyOf(pseudoBean.initInterceptedMethods(errors, + bytecodeTransformerConsumer, transformUnproxyableClasses, bindingsSourceClass)); + pseudoBean.decoratedMethods = Map.of(); + pseudoBean.lifecycleInterceptors = Map.of(); + interceptionProxy.init(pseudoBean); + } } protected String getType() { @@ -675,18 +713,20 @@ protected String getType() { } private Map initInterceptedMethods(List errors, - Consumer bytecodeTransformerConsumer, boolean transformUnproxyableClasses) { + Consumer bytecodeTransformerConsumer, boolean transformUnproxyableClasses, + ClassInfo bindingsSourceClass) { if (!isInterceptor() && !isDecorator() && isClassBean()) { - Map interceptedMethods = new HashMap<>(); - Map> candidates = new HashMap<>(); - ClassInfo targetClass = target.get().asClass(); + List classLevelBindings = new ArrayList<>(); - addClassLevelBindings(targetClass, classLevelBindings); + addClassLevelBindings(bindingsSourceClass != null ? bindingsSourceClass : targetClass, classLevelBindings); Interceptors.checkClassLevelInterceptorBindings(classLevelBindings, targetClass, beanDeployment); - Set finalMethods = Methods.addInterceptedMethodCandidates(this, candidates, classLevelBindings, - bytecodeTransformerConsumer, transformUnproxyableClasses); + BindingsDiscovery bindingsDiscovery = new BindingsDiscovery(beanDeployment, bindingsSourceClass); + Map> candidates = new HashMap<>(); + Set finalMethods = Methods.addInterceptedMethodCandidates(beanDeployment, + targetClass, bindingsDiscovery, candidates, classLevelBindings, bytecodeTransformerConsumer, + transformUnproxyableClasses, hasAroundInvokes()); if (!finalMethods.isEmpty()) { String additionalError = ""; if (finalMethods.stream().anyMatch(KotlinUtils::isNoninterceptableKotlinMethod)) { @@ -700,6 +740,7 @@ private Map initInterceptedMethods(List return Collections.emptyMap(); } + Map interceptedMethods = new HashMap<>(); for (Entry> entry : candidates.entrySet()) { List interceptors = beanDeployment.getInterceptorResolver() .resolve(InterceptionType.AROUND_INVOKE, entry.getValue()); @@ -826,7 +867,8 @@ private Map initLifecycleInterceptors() { putLifecycleInterceptors(lifecycleInterceptors, classLevelBindings, InterceptionType.POST_CONSTRUCT); putLifecycleInterceptors(lifecycleInterceptors, classLevelBindings, InterceptionType.PRE_DESTROY); MethodInfo interceptedConstructor = findInterceptedConstructor(target.get().asClass()); - if (beanDeployment.getAnnotation(interceptedConstructor, DotNames.NO_CLASS_INTERCEPTORS) == null) { + if (interceptedConstructor != null + && beanDeployment.getAnnotation(interceptedConstructor, DotNames.NO_CLASS_INTERCEPTORS) == null) { constructorLevelBindings = Methods.mergeMethodAndClassLevelBindings(constructorLevelBindings, classLevelBindings); } @@ -941,7 +983,7 @@ public boolean equals(Object obj) { return Objects.equals(identifier, other.identifier); } - private Type initProviderType(AnnotationTarget target, ClassInfo implClazz) { + static Type initProviderType(AnnotationTarget target, ClassInfo implClazz) { if (target != null) { switch (target.kind()) { case CLASS: @@ -1086,6 +1128,8 @@ static class Builder { private Integer startupPriority; + private InterceptionProxyInfo interceptionProxy; + Builder() { injections = Collections.emptyList(); stereotypes = Collections.emptyList(); @@ -1210,11 +1254,16 @@ Builder targetPackageName(String name) { return this; } + Builder interceptionProxy(InterceptionProxyInfo interceptionProxy) { + this.interceptionProxy = interceptionProxy; + return this; + } + BeanInfo build() { return new BeanInfo(implClazz, providerType, target, beanDeployment, scope, types, qualifiers, injections, declaringBean, disposer, alternative, stereotypes, name, isDefaultBean, creatorConsumer, destroyerConsumer, params, removable, forceApplicationClass, targetPackageName, priority, - identifier, null, startupPriority); + identifier, null, startupPriority, interceptionProxy); } public Builder forceApplicationClass(boolean forceApplicationClass) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java index 35f237fa9ded2..6604c38013350 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanProcessor.java @@ -259,6 +259,12 @@ public List generateResources(ReflectionRegistration reflectionRegistr injectionPointAnnotationsPredicate); Collection invokers = beanDeployment.getInvokers(); + // this is different to `SubclassGenerator` in that it generates support classes + // for interception of producer methods and synthetic beans and only supports + // limited form of interception (and no decoration) + InterceptionProxyGenerator interceptionGenerator = new InterceptionProxyGenerator(generateSources, + applicationClassPredicate, annotationLiterals, reflectionRegistration); + List resources = new ArrayList<>(); if (executor != null) { @@ -351,6 +357,23 @@ public Collection call() throws Exception { } })); } + + if (bean.getInterceptionProxy() != null) { + secondaryTasks.add(executor.submit(new Callable>() { + @Override + public Collection call() throws Exception { + Collection interceptionResources = interceptionGenerator.generate(bean); + for (Resource r : interceptionResources) { + if (r.getSpecialType() == SpecialType.SUBCLASS) { + refReg.registerSubclass(bean.getInterceptionProxy().getTargetClass(), + r.getFullyQualifiedName()); + break; + } + } + return interceptionResources; + } + })); + } } } return beanResources; @@ -452,6 +475,17 @@ public Collection call() throws Exception { } resources.addAll(subclassResources); } + if (bean.getInterceptionProxy() != null) { + Collection interceptionResources = interceptionGenerator.generate(bean); + for (Resource r : interceptionResources) { + if (r.getSpecialType() == SpecialType.SUBCLASS) { + refReg.registerSubclass(bean.getInterceptionProxy().getTargetClass(), + r.getFullyQualifiedName()); + break; + } + } + resources.addAll(interceptionResources); + } } } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java index b5eb22584600c..da540feb91778 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java @@ -31,6 +31,7 @@ import org.jboss.jandex.FieldInfo; import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.MethodParameterInfo; import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; import org.jboss.logging.Logger; @@ -191,11 +192,36 @@ static BeanInfo createProducerMethod(TypeClosure typeClosure, MethodInfo produce + "its scope must be @Dependent: " + producerMethod); } + InterceptionProxyInfo interceptionProxy = null; + for (MethodParameterInfo parameter : producerMethod.parameters()) { + if (parameter.type().name().equals(DotNames.INTERCEPTION_PROXY)) { + if (interceptionProxy != null) { + throw new DefinitionException( + "Declaring more than one InterceptionProxy parameter is invalid: " + producerMethod); + } + if (parameter.type().kind() != Kind.PARAMETERIZED_TYPE) { + throw new DefinitionException( + "InterceptionProxy parameter must be a parameterized type: " + producerMethod); + } + DotName targetClass = producerMethod.returnType().name(); + DotName bindingsSource = null; + if (parameter.hasAnnotation(DotNames.BINDINGS_SOURCE)) { + Type bindingsSourceType = parameter.annotation(DotNames.BINDINGS_SOURCE).value().asClass(); + if (bindingsSourceType.kind() != Kind.CLASS) { + throw new DefinitionException("@BindingsSource may only define a class type, got " + + bindingsSourceType.kind() + ": " + producerMethod); + } + bindingsSource = bindingsSourceType.name(); + } + interceptionProxy = new InterceptionProxyInfo(targetClass, bindingsSource); + } + } + List injections = Injection.forBean(producerMethod, declaringBean, beanDeployment, transformer, Injection.BeanType.PRODUCER_METHOD); BeanInfo bean = new BeanInfo(producerMethod, beanDeployment, scope, typeClosure.types(), qualifiers, injections, - declaringBean, - disposer, isAlternative, stereotypes, name, isDefaultBean, null, priority, typeClosure.unrestrictedTypes()); + declaringBean, disposer, isAlternative, stereotypes, name, isDefaultBean, null, priority, + typeClosure.unrestrictedTypes(), interceptionProxy); for (Injection injection : injections) { injection.init(bean); } @@ -309,7 +335,7 @@ static BeanInfo createProducerField(FieldInfo producerField, BeanInfo declaringB BeanInfo bean = new BeanInfo(producerField, beanDeployment, scope, typeClosure.types(), qualifiers, Collections.emptyList(), declaringBean, disposer, isAlternative, stereotypes, name, isDefaultBean, null, priority, - typeClosure.unrestrictedTypes()); + typeClosure.unrestrictedTypes(), null); return bean; } @@ -792,12 +818,7 @@ static void validateInterceptorDecorator(BeanInfo bean, List errors, } static void validateBean(BeanInfo bean, List errors, Consumer bytecodeTransformerConsumer, - Set classesReceivingNoArgsCtor, Set injectedBeans) { - - // by default, we fail deployment due to unproxyability for all beans, but in strict mode, - // we only do that for beans that are injected somewhere -- and defer the error to runtime otherwise, - // due to CDI spec requirements - boolean failIfNotProxyable = bean.getDeployment().strictCompatibility ? injectedBeans.contains(bean) : true; + Set classesReceivingNoArgsCtor, boolean failIfNotProxyable) { if (bean.isClassBean()) { ClassInfo beanClass = bean.getTarget().get().asClass(); @@ -827,8 +848,9 @@ static void validateBean(BeanInfo bean, List errors, Consumer errors, Consumer getAnnotations(MethodInfo method) { + if (bindingsSourceClass == null) { + return beanDeployment.getAnnotations(method); + } + + MethodInfo corresponding = findCorrespondingMethod(method); + return corresponding != null ? beanDeployment.getAnnotations(corresponding) : Set.of(); + } + + private MethodInfo findCorrespondingMethod(MethodInfo method) { + for (MethodInfo candidate : bindingsSourceClass.methods()) { + if (method.name().equals(candidate.name()) + && method.returnType().equals(candidate.returnType()) + && method.parameterTypes().equals(candidate.parameterTypes()) + && Modifier.isStatic(method.flags()) == Modifier.isStatic(candidate.flags())) { + return candidate; + } + } + + // no inheritance for now + + return null; + } +} diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java index 46f2029b164e7..571afe47a3c28 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java @@ -66,6 +66,9 @@ public enum BuiltinBean { LIST(BuiltinBean::generateListBytecode, (ip, names) -> cdiAndRawTypeMatches(ip, DotNames.LIST) && ip.getRequiredQualifier(DotNames.ALL) != null, BuiltinBean::validateList, DotNames.LIST), + INTERCEPTION_PROXY(BuiltinBean::generateInterceptionProxyBytecode, + BuiltinBean::cdiAndRawTypeMatches, BuiltinBean::validateInterceptionProxy, + DotNames.INTERCEPTION_PROXY), ; private final DotName[] rawTypeDotNames; @@ -430,6 +433,16 @@ private static void generateListBytecode(GeneratorContext ctx) { ctx.constructor.getThis(), listProviderSupplier); } + private static void generateInterceptionProxyBytecode(GeneratorContext ctx) { + BeanInfo bean = ctx.targetInfo.asBean(); + String name = InterceptionProxyGenerator.interceptionProxyProviderName(bean); + + ResultHandle supplier = ctx.constructor.newInstance(MethodDescriptor.ofConstructor(name)); + ctx.constructor.writeInstanceField( + FieldDescriptor.of(ctx.clazzCreator.getClassName(), ctx.providerName, Supplier.class.getName()), + ctx.constructor.getThis(), supplier); + } + private static ResultHandle loadInvokerTargetBean(InvokerInfo invoker, BytecodeCreator bytecode) { ResultHandle arc = bytecode.invokeStaticMethod(MethodDescriptors.ARC_CONTAINER); return bytecode.invokeInterfaceMethod(MethodDescriptors.ARC_CONTAINER_BEAN, arc, @@ -519,4 +532,28 @@ private static void validateEventMetadata(InjectionTargetInfo injectionTarget, I } } + private static void validateInterceptionProxy(InjectionTargetInfo injectionTarget, + InjectionPointInfo injectionPoint, Consumer errors) { + if (injectionTarget.kind() != TargetKind.BEAN + || (!injectionTarget.asBean().isProducerMethod() && !injectionTarget.asBean().isSynthetic()) + || injectionTarget.asBean().getInterceptionProxy() == null) { + errors.accept(new DefinitionException( + "InterceptionProxy can only be injected into a producer method or a synthetic bean")); + } + if (injectionPoint.getType().kind() != Kind.PARAMETERIZED_TYPE) { + errors.accept(new DefinitionException("InterceptionProxy must be a parameterized type")); + } + Type interceptionProxyType = injectionPoint.getType().asParameterizedType().arguments().get(0); + if (interceptionProxyType.kind() != Kind.CLASS && interceptionProxyType.kind() != Kind.PARAMETERIZED_TYPE) { + errors.accept(new DefinitionException( + "Type argument of InterceptionProxy may only be a class or parameterized type")); + } + if (!injectionTarget.asBean().getProviderType().equals(interceptionProxyType)) { + String msg = injectionTarget.asBean().isProducerMethod() + ? "Type argument of InterceptionProxy must be equal to the return type of the producer method" + : "Type argument of InterceptionProxy must be equal to the bean provider type"; + errors.accept(new DefinitionException(msg)); + } + } + } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DecoratorInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DecoratorInfo.java index f9e08bbb44bda..4522b43631722 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DecoratorInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DecoratorInfo.java @@ -23,7 +23,7 @@ public class DecoratorInfo extends BeanInfo implements Comparable Set decoratedTypes, List injections, int priority) { super(target, beanDeployment, BuiltinScope.DEPENDENT.getInfo(), Sets.singletonHashSet(Type.create(target.asClass().name(), Kind.CLASS)), new HashSet<>(), injections, - null, null, false, Collections.emptyList(), null, false, null, priority, null); + null, null, false, Collections.emptyList(), null, false, null, priority, null, null); this.delegateInjectionPoint = delegateInjectionPoint; this.decoratedTypes = decoratedTypes; } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DotNames.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DotNames.java index cd3b1a523587f..bdfdd2a990e83 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DotNames.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/DotNames.java @@ -59,10 +59,12 @@ import io.quarkus.arc.All; import io.quarkus.arc.ArcInvocationContext; +import io.quarkus.arc.BindingsSource; import io.quarkus.arc.DefaultBean; import io.quarkus.arc.InjectableBean; import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.InstanceHandle; +import io.quarkus.arc.InterceptionProxy; import io.quarkus.arc.NoClassInterceptors; import io.quarkus.arc.Unremovable; import io.quarkus.arc.VetoedProducer; @@ -141,6 +143,8 @@ public final class DotNames { public static final DotName INSTANCE_HANDLE = create(InstanceHandle.class); public static final DotName NO_CLASS_INTERCEPTORS = create(NoClassInterceptors.class); public static final DotName DEPRECATED = create(Deprecated.class); + public static final DotName INTERCEPTION_PROXY = create(InterceptionProxy.class); + public static final DotName BINDINGS_SOURCE = create(BindingsSource.class); /** * @deprecated use {@link KotlinUtils}; this constant will be removed at some time after Quarkus 3.6 diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptionProxyGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptionProxyGenerator.java new file mode 100644 index 0000000000000..49d790bb94d31 --- /dev/null +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptionProxyGenerator.java @@ -0,0 +1,357 @@ +package io.quarkus.arc.processor; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.interceptor.InvocationContext; + +import org.jboss.jandex.AnnotationInstanceEquivalenceProxy; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.Type; +import org.objectweb.asm.Opcodes; + +import io.quarkus.arc.InjectableReferenceProvider; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.Subclass; +import io.quarkus.arc.impl.InterceptedMethodMetadata; +import io.quarkus.arc.processor.ResourceOutput.Resource; +import io.quarkus.gizmo.BytecodeCreator; +import io.quarkus.gizmo.ClassCreator; +import io.quarkus.gizmo.ClassOutput; +import io.quarkus.gizmo.FieldCreator; +import io.quarkus.gizmo.FieldDescriptor; +import io.quarkus.gizmo.FunctionCreator; +import io.quarkus.gizmo.Gizmo; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.MethodDescriptor; +import io.quarkus.gizmo.ResultHandle; + +public class InterceptionProxyGenerator extends AbstractGenerator { + private static final String INTERCEPTION_SUBCLASS = "_InterceptionSubclass"; + + private final Predicate applicationClassPredicate; + private final AnnotationLiteralProcessor annotationLiterals; + private final ReflectionRegistration reflectionRegistration; + + InterceptionProxyGenerator(boolean generateSources, Predicate applicationClassPredicate, + AnnotationLiteralProcessor annotationLiterals, ReflectionRegistration reflectionRegistration) { + super(generateSources); + this.applicationClassPredicate = applicationClassPredicate; + this.annotationLiterals = annotationLiterals; + this.reflectionRegistration = reflectionRegistration; + } + + Collection generate(BeanInfo bean) { + if (bean.getInterceptionProxy() == null) { + return Collections.emptyList(); + } + + Function specialTypeFunction = className -> { + if (className.endsWith(INTERCEPTION_SUBCLASS)) { + return Resource.SpecialType.SUBCLASS; + } + return null; + }; + ResourceClassOutput classOutput = new ResourceClassOutput(applicationClassPredicate.test(bean.getBeanClass()), + specialTypeFunction, generateSources); + + createInterceptionProxyProvider(classOutput, bean); + createInterceptionProxy(classOutput, bean); + createInterceptionSubclass(classOutput, bean.getInterceptionProxy()); + + return classOutput.getResources(); + } + + // --- + + static String interceptionProxyProviderName(BeanInfo bean) { + return bean.getBeanClass().toString() + "_InterceptionProxyProvider_" + bean.getIdentifier(); + } + + private static String interceptionProxyName(BeanInfo bean) { + return bean.getBeanClass().toString() + "_InterceptionProxy_" + bean.getIdentifier(); + } + + private static String interceptionSubclassName(InterceptionProxyInfo interceptionProxy) { + return interceptionProxy.getTargetClass() + INTERCEPTION_SUBCLASS; + } + + private void createInterceptionProxyProvider(ClassOutput classOutput, BeanInfo bean) { + try (ClassCreator clazz = ClassCreator.builder() + .classOutput(classOutput) + .className(interceptionProxyProviderName(bean)) + .interfaces(Supplier.class, InjectableReferenceProvider.class) + .build()) { + + // Supplier + MethodCreator get0 = clazz.getMethodCreator("get", Object.class); + get0.returnValue(get0.getThis()); + + // InjectableReferenceProvider + MethodCreator get1 = clazz.getMethodCreator("get", Object.class, CreationalContext.class); + String targetName = interceptionProxyName(bean); + ResultHandle result = get1.newInstance(MethodDescriptor.ofConstructor(targetName, CreationalContext.class), + get1.getMethodParam(0)); + get1.returnValue(result); + } + } + + private void createInterceptionProxy(ClassOutput classOutput, BeanInfo bean) { + try (ClassCreator clazz = ClassCreator.builder() + .classOutput(classOutput) + .className(interceptionProxyName(bean)) + .interfaces(InterceptionProxy.class) + .build()) { + + FieldCreator cc = clazz.getFieldCreator("creationalContext", CreationalContext.class) + .setModifiers(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL); + + MethodCreator ctor = clazz.getConstructorCreator(CreationalContext.class); + ctor.invokeSpecialMethod(MethodDescriptor.ofConstructor(Object.class), ctor.getThis()); + ctor.writeInstanceField(cc.getFieldDescriptor(), ctor.getThis(), ctor.getMethodParam(0)); + ctor.returnVoid(); + + MethodCreator create = clazz.getMethodCreator("create", Object.class, Object.class); + + ResultHandle ccHandle = create.readInstanceField(cc.getFieldDescriptor(), create.getThis()); + ResultHandle delegateHandle = create.getMethodParam(0); + + BytecodeCreator isInstance = create.ifFalse( + create.instanceOf(delegateHandle, bean.getInterceptionProxy().getTargetClass().toString())) + .falseBranch(); + isInstance.returnValue(isInstance.newInstance(MethodDescriptor.ofConstructor( + interceptionSubclassName(bean.getInterceptionProxy()), CreationalContext.class, Object.class), + ccHandle, delegateHandle)); + + ResultHandle exceptionMessage = Gizmo.newStringBuilder(create) + .append("InterceptionProxy for ") + .append(create.load(bean.toString())) + .append(" got unknown delegate: ") + .append(delegateHandle) + .callToString(); + ResultHandle exception = create.newInstance( + MethodDescriptor.ofConstructor(IllegalArgumentException.class, String.class), exceptionMessage); + create.throwException(exception); + create.returnNull(); + } + } + + private void createInterceptionSubclass(ClassOutput classOutput, InterceptionProxyInfo interceptionProxy) { + BeanInfo pseudoBean = interceptionProxy.getPseudoBean(); + ClassInfo pseudoBeanClass = pseudoBean.getImplClazz(); + String pseudoBeanClassName = pseudoBeanClass.name().toString(); + boolean isInterface = pseudoBeanClass.isInterface(); + + String superClass = isInterface ? Object.class.getName() : pseudoBeanClassName; + String[] interfaces = isInterface + ? new String[] { pseudoBeanClassName, Subclass.class.getName() } + : new String[] { Subclass.class.getName() }; + + try (ClassCreator clazz = ClassCreator.builder() + .classOutput(classOutput) + .className(interceptionSubclassName(interceptionProxy)) + .superClass(superClass) + .interfaces(interfaces) + .build()) { + + FieldCreator delegate = clazz.getFieldCreator("delegate", Object.class) + .setModifiers(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL); + + Map interceptorToResultHandle = new HashMap<>(); + Map interceptorInstanceToResultHandle = new HashMap<>(); + + MethodCreator ctor = clazz.getConstructorCreator(CreationalContext.class, Object.class); + ctor.invokeSpecialMethod(MethodDescriptor.ofConstructor(superClass), ctor.getThis()); + ctor.writeInstanceField(delegate.getFieldDescriptor(), ctor.getThis(), ctor.getMethodParam(1)); + ResultHandle arc = ctor.invokeStaticMethod(MethodDescriptors.ARC_CONTAINER); + ResultHandle creationalContextHandle = ctor.getMethodParam(0); + for (InterceptorInfo interceptorInfo : pseudoBean.getBoundInterceptors()) { + ResultHandle interceptorBean = ctor.invokeInterfaceMethod(MethodDescriptors.ARC_CONTAINER_BEAN, arc, + ctor.load(interceptorInfo.getIdentifier())); + interceptorToResultHandle.put(interceptorInfo.getIdentifier(), interceptorBean); + + ResultHandle creationalContext = ctor.invokeStaticMethod(MethodDescriptors.CREATIONAL_CTX_CHILD, + creationalContextHandle); + ResultHandle interceptorInstance = ctor.invokeInterfaceMethod(MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, + interceptorBean, creationalContext); + interceptorInstanceToResultHandle.put(interceptorInfo.getIdentifier(), interceptorInstance); + } + + Map forwardingMethods = new HashMap<>(); + + for (MethodInfo method : pseudoBean.getInterceptedMethods().keySet()) { + forwardingMethods.put(MethodDescriptor.of(method), SubclassGenerator.createForwardingMethod(clazz, + pseudoBeanClassName, method, (bytecode, virtualMethod, params) -> { + ResultHandle delegateHandle = bytecode.readInstanceField(delegate.getFieldDescriptor(), + bytecode.getThis()); + return isInterface + ? bytecode.invokeInterfaceMethod(virtualMethod, delegateHandle, params) + : bytecode.invokeVirtualMethod(virtualMethod, delegateHandle, params); + })); + } + + FieldCreator constructedField = clazz.getFieldCreator(SubclassGenerator.FIELD_NAME_CONSTRUCTED, boolean.class) + .setModifiers(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL); + + // Initialize maps of shared interceptor chains and interceptor bindings + SubclassGenerator.IntegerHolder chainIdx = new SubclassGenerator.IntegerHolder(); + SubclassGenerator.IntegerHolder bindingIdx = new SubclassGenerator.IntegerHolder(); + Map, String> interceptorChainKeys = new HashMap<>(); + Map, String> bindingKeys = new HashMap<>(); + + ResultHandle interceptorChainMap = ctor.newInstance(MethodDescriptor.ofConstructor(HashMap.class)); + ResultHandle bindingsMap = ctor.newInstance(MethodDescriptor.ofConstructor(HashMap.class)); + + // Shared interceptor bindings literals + Map bindingsLiterals = new HashMap<>(); + Function, String> bindingsFun = SubclassGenerator.createBindingsFun( + bindingIdx, ctor, bindingsMap, bindingsLiterals, pseudoBean, annotationLiterals); + Function, String> interceptorChainKeysFun = SubclassGenerator.createInterceptorChainKeysFun( + chainIdx, ctor, interceptorChainMap, interceptorInstanceToResultHandle, interceptorToResultHandle); + + int methodIdx = 1; + for (BeanInfo.InterceptionInfo interception : pseudoBean.getInterceptedMethods().values()) { + // Each intercepted method has a corresponding InterceptedMethodMetadata field + clazz.getFieldCreator("arc$" + methodIdx++, InterceptedMethodMetadata.class.getName()) + .setModifiers(Opcodes.ACC_PRIVATE); + interceptorChainKeys.computeIfAbsent(interception.interceptors, interceptorChainKeysFun); + bindingKeys.computeIfAbsent(interception.bindingsEquivalenceProxies(), bindingsFun); + } + + // Split initialization of InterceptedMethodMetadata into multiple methods + int group = 0; + int groupLimit = 30; + MethodCreator initMetadataMethod = null; + + // to avoid repeatedly looking for the exact same thing in the maps + Map chainHandles = new HashMap<>(); + Map bindingsHandles = new HashMap<>(); + + methodIdx = 1; + for (MethodInfo method : pseudoBean.getInterceptedMethods().keySet()) { + if (initMetadataMethod == null || methodIdx >= (group * groupLimit)) { + if (initMetadataMethod != null) { + // End the bytecode of the current initMetadata method + initMetadataMethod.returnVoid(); + initMetadataMethod.close(); + // Invoke arc$initMetadataX(interceptorChainMap,bindingsMap) in the ctor method + ctor.invokeVirtualMethod(initMetadataMethod.getMethodDescriptor(), ctor.getThis(), + interceptorChainMap, bindingsMap); + } + initMetadataMethod = clazz.getMethodCreator("arc$initMetadata" + group++, void.class, Map.class, Map.class) + .setModifiers(Opcodes.ACC_PRIVATE); + chainHandles.clear(); + bindingsHandles.clear(); + } + + MethodDescriptor methodDescriptor = MethodDescriptor.of(method); + BeanInfo.InterceptionInfo interception = pseudoBean.getInterceptedMethods().get(method); + MethodDescriptor forwardDescriptor = forwardingMethods.get(methodDescriptor); + List parameters = method.parameterTypes(); + + final MethodCreator initMetadataMethodFinal = initMetadataMethod; + + // 1. Interceptor chain + String interceptorChainKey = interceptorChainKeys.get(interception.interceptors); + ResultHandle chainHandle = chainHandles.computeIfAbsent(interceptorChainKey, ignored -> { + return initMetadataMethodFinal.invokeInterfaceMethod(MethodDescriptors.MAP_GET, + initMetadataMethodFinal.getMethodParam(0), initMetadataMethodFinal.load(interceptorChainKey)); + }); + + // 2. Method method = Reflections.findMethod(org.jboss.weld.arc.test.interceptors.SimpleBean.class,"foo",java.lang.String.class) + ResultHandle[] paramsHandles = new ResultHandle[3]; + paramsHandles[0] = initMetadataMethod.loadClass(pseudoBeanClassName); + paramsHandles[1] = initMetadataMethod.load(method.name()); + if (!parameters.isEmpty()) { + ResultHandle paramsArray = initMetadataMethod.newArray(Class.class, + initMetadataMethod.load(parameters.size())); + for (ListIterator iterator = parameters.listIterator(); iterator.hasNext();) { + initMetadataMethod.writeArrayValue(paramsArray, iterator.nextIndex(), + initMetadataMethod.loadClass(iterator.next().name().toString())); + } + paramsHandles[2] = paramsArray; + } else { + paramsHandles[2] = initMetadataMethod + .readStaticField(FieldDescriptors.ANNOTATION_LITERALS_EMPTY_CLASS_ARRAY); + } + ResultHandle methodHandle = initMetadataMethod.invokeStaticMethod(MethodDescriptors.REFLECTIONS_FIND_METHOD, + paramsHandles); + + // 3. Interceptor bindings + // Note that we use a shared set if possible + String bindingKey = bindingKeys.get(interception.bindingsEquivalenceProxies()); + ResultHandle bindingsHandle = bindingsHandles.computeIfAbsent(bindingKey, ignored -> { + return initMetadataMethodFinal.invokeInterfaceMethod(MethodDescriptors.MAP_GET, + initMetadataMethodFinal.getMethodParam(1), initMetadataMethodFinal.load(bindingKey)); + }); + + // Instantiate the forwarding function + // BiFunction forward = (target, ctx) -> target.foo$$superforward((java.lang.String)ctx.getParameters()[0]) + FunctionCreator func = initMetadataMethod.createFunction(BiFunction.class); + BytecodeCreator funcBytecode = func.getBytecode(); + ResultHandle targetHandle = funcBytecode.getMethodParam(0); + ResultHandle ctxHandle = funcBytecode.getMethodParam(1); + ResultHandle[] superParamHandles; + if (parameters.isEmpty()) { + superParamHandles = new ResultHandle[0]; + } else { + superParamHandles = new ResultHandle[parameters.size()]; + ResultHandle ctxParamsHandle = funcBytecode.invokeInterfaceMethod( + MethodDescriptor.ofMethod(InvocationContext.class, "getParameters", Object[].class), + ctxHandle); + // autoboxing is handled inside Gizmo + for (int i = 0; i < superParamHandles.length; i++) { + superParamHandles[i] = funcBytecode.readArrayValue(ctxParamsHandle, i); + } + } + + ResultHandle superResult = isInterface + ? funcBytecode.invokeInterfaceMethod(methodDescriptor, targetHandle, superParamHandles) + : funcBytecode.invokeVirtualMethod(methodDescriptor, targetHandle, superParamHandles); + funcBytecode.returnValue(superResult != null ? superResult : funcBytecode.loadNull()); + + ResultHandle aroundForwardFun = func.getInstance(); + + // Now create metadata for the given intercepted method + ResultHandle methodMetadataHandle = initMetadataMethod.newInstance( + MethodDescriptors.INTERCEPTED_METHOD_METADATA_CONSTRUCTOR, + chainHandle, methodHandle, bindingsHandle, aroundForwardFun); + + FieldDescriptor metadataField = FieldDescriptor.of(clazz.getClassName(), "arc$" + methodIdx++, + InterceptedMethodMetadata.class.getName()); + + initMetadataMethod.writeInstanceField(metadataField, initMetadataMethod.getThis(), methodMetadataHandle); + + // Needed when running on native image + reflectionRegistration.registerMethod(method); + + // Finally create the intercepted method + SubclassGenerator.createInterceptedMethod(method, clazz, metadataField, constructedField.getFieldDescriptor(), + forwardDescriptor, bc -> bc.readInstanceField(delegate.getFieldDescriptor(), bc.getThis())); + } + + if (initMetadataMethod != null) { + // Make sure we end the bytecode of the last initMetadata method + initMetadataMethod.returnVoid(); + // Invoke arc$initMetadataX(interceptorChainMap,bindingsMap) in the ctor + ctor.invokeVirtualMethod(initMetadataMethod.getMethodDescriptor(), ctor.getThis(), + interceptorChainMap, bindingsMap); + } + + ctor.writeInstanceField(constructedField.getFieldDescriptor(), ctor.getThis(), ctor.load(true)); + ctor.returnVoid(); + } + } +} diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptionProxyInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptionProxyInfo.java new file mode 100644 index 0000000000000..1085838402336 --- /dev/null +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptionProxyInfo.java @@ -0,0 +1,46 @@ +package io.quarkus.arc.processor; + +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; + +import org.jboss.jandex.DotName; + +final class InterceptionProxyInfo { + private final DotName targetClass; + private final DotName bindingsSourceClass; + private BeanInfo pseudoBean; + + /** + * Creates an interim instance with an unknown target class. + * Shall be replaced later, once the target class is known. + */ + InterceptionProxyInfo(DotName bindingsSourceClass) { + this(DotName.OBJECT_NAME, bindingsSourceClass); + } + + InterceptionProxyInfo(DotName targetClass, DotName bindingsSourceClass) { + this.targetClass = Objects.requireNonNull(targetClass); + this.bindingsSourceClass = bindingsSourceClass; + } + + void init(BeanInfo pseudoBean) { + this.pseudoBean = pseudoBean; + } + + DotName getTargetClass() { + return targetClass; + } + + DotName getBindingsSourceClass() { + return bindingsSourceClass; + } + + /** + * Note that this method only returns non-{@code null} value + * after {@link BeanDeployment#init(Consumer, List)}. + */ + BeanInfo getPseudoBean() { + return pseudoBean; + } +} diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java index ff209cc42794d..2264fde9a8bd5 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java @@ -66,7 +66,7 @@ public class InterceptorInfo extends BeanInfo implements Comparable injections, int priority) { super(target, beanDeployment, BuiltinScope.DEPENDENT.getInfo(), Sets.singletonHashSet(Type.create(target.asClass().name(), Kind.CLASS)), new HashSet<>(), injections, - null, null, false, Collections.emptyList(), null, false, null, priority, null); + null, null, false, Collections.emptyList(), null, false, null, priority, null, null); this.bindings = bindings; this.interceptionType = null; this.creatorClass = null; diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java index e807eac73d317..a13a392dda3cd 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java @@ -161,22 +161,20 @@ static boolean isObjectToString(MethodInfo method) { return method.declaringClass().name().equals(DotNames.OBJECT) && method.name().equals(TO_STRING); } - static Set addInterceptedMethodCandidates(BeanInfo bean, - Map> candidates, + static Set addInterceptedMethodCandidates(BeanDeployment beanDeployment, ClassInfo targetClass, + BindingsDiscovery bindingsDiscovery, Map> candidates, List classLevelBindings, Consumer bytecodeTransformerConsumer, - boolean transformUnproxyableClasses) { - BeanDeployment beanDeployment = bean.getDeployment(); - ClassInfo classInfo = bean.getTarget().get().asClass(); - return addInterceptedMethodCandidates(beanDeployment, classInfo, classInfo, candidates, Set.copyOf(classLevelBindings), - bytecodeTransformerConsumer, transformUnproxyableClasses, + boolean transformUnproxyableClasses, boolean hasAroundInvokes) { + return addInterceptedMethodCandidates(beanDeployment, targetClass, targetClass, bindingsDiscovery, candidates, + Set.copyOf(classLevelBindings), bytecodeTransformerConsumer, transformUnproxyableClasses, new SubclassSkipPredicate(beanDeployment.getAssignabilityCheck()::isAssignableFrom, beanDeployment.getBeanArchiveIndex(), beanDeployment.getObserverAndProducerMethods(), beanDeployment.getAnnotationStore()), - false, new HashSet<>(), bean.hasAroundInvokes()); + false, new HashSet<>(), hasAroundInvokes); } private static Set addInterceptedMethodCandidates(BeanDeployment beanDeployment, ClassInfo classInfo, - ClassInfo originalClassInfo, + ClassInfo originalClassInfo, BindingsDiscovery bindingsDiscovery, Map> candidates, Set classLevelBindings, Consumer bytecodeTransformerConsumer, boolean transformUnproxyableClasses, SubclassSkipPredicate skipPredicate, boolean ignoreMethodLevelBindings, @@ -194,7 +192,7 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea // Note that we must merge the bindings first Set bindings = mergeBindings(beanDeployment, originalClassInfo, classLevelBindings, - ignoreMethodLevelBindings, method, noClassInterceptorsMethods); + ignoreMethodLevelBindings, method, noClassInterceptorsMethods, bindingsDiscovery); boolean possiblyIntercepted = !bindings.isEmpty() || targetHasAroundInvokes; if (skipPredicate.test(method)) { continue; @@ -224,9 +222,9 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea ClassInfo superClassInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), classInfo.superName()); if (superClassInfo != null) { finalMethodsFoundAndNotChanged - .addAll(addInterceptedMethodCandidates(beanDeployment, superClassInfo, classInfo, candidates, - classLevelBindings, bytecodeTransformerConsumer, transformUnproxyableClasses, skipPredicate, - ignoreMethodLevelBindings, noClassInterceptorsMethods, targetHasAroundInvokes)); + .addAll(addInterceptedMethodCandidates(beanDeployment, superClassInfo, classInfo, bindingsDiscovery, + candidates, classLevelBindings, bytecodeTransformerConsumer, transformUnproxyableClasses, + skipPredicate, ignoreMethodLevelBindings, noClassInterceptorsMethods, targetHasAroundInvokes)); } } @@ -234,8 +232,8 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea ClassInfo interfaceInfo = getClassByName(beanDeployment.getBeanArchiveIndex(), i); if (interfaceInfo != null) { //interfaces can't have final methods - addInterceptedMethodCandidates(beanDeployment, interfaceInfo, originalClassInfo, candidates, - classLevelBindings, bytecodeTransformerConsumer, transformUnproxyableClasses, + addInterceptedMethodCandidates(beanDeployment, interfaceInfo, originalClassInfo, bindingsDiscovery, + candidates, classLevelBindings, bytecodeTransformerConsumer, transformUnproxyableClasses, skipPredicate, true, noClassInterceptorsMethods, targetHasAroundInvokes); } } @@ -244,10 +242,10 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea private static Set mergeBindings(BeanDeployment beanDeployment, ClassInfo classInfo, Set classLevelBindings, boolean ignoreMethodLevelBindings, MethodInfo method, - Set noClassInterceptorsMethods) { + Set noClassInterceptorsMethods, BindingsDiscovery bindingsDiscovery) { MethodKey key = new MethodKey(method); - if (beanDeployment.getAnnotation(method, DotNames.NO_CLASS_INTERCEPTORS) != null + if (bindingsDiscovery.hasAnnotation(method, DotNames.NO_CLASS_INTERCEPTORS) || noClassInterceptorsMethods.contains(key)) { // The set of methods with `@NoClassInterceptors` is shared in the traversal of class hierarchy, so once // a method with the annotation is found, all subsequent occurences of the "same" method are treated @@ -270,7 +268,7 @@ private static Set mergeBindings(BeanDeployment beanDeployme return classLevelBindings; } - Collection methodAnnotations = beanDeployment.getAnnotations(method); + Collection methodAnnotations = bindingsDiscovery.getAnnotations(method); if (methodAnnotations.isEmpty()) { // No annotations declared on the method return classLevelBindings; diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/BindingsSource.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/BindingsSource.java new file mode 100644 index 0000000000000..8b36726351e4d --- /dev/null +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/BindingsSource.java @@ -0,0 +1,38 @@ +package io.quarkus.arc; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines a source of interceptor binding annotations. When this annotation + * is present on an {@link InterceptionProxy} parameter of a CDI producer method, + * the interceptor binding annotations present on the target class are ignored + * and instead are read from the class defined by this annotation. + *

+ * Class-level interceptor bindings of the target class are equal to class-level + * interceptor bindings on the bindings source class. + *

+ * Method-level interceptor bindings of a method on the target class are equal to + * method-level bindings of a method with the same name, return type, parameter + * types and {@code static} flag declared on the bindings source class. + *

+ * These annotations can be present on the {@link #value()} class: + *

+ *

    + *
  • interceptor bindings: on the class and on the methods
  • + *
  • stereotypes: on the class
  • + *
  • {@link NoClassInterceptors}: on the methods
  • + *
+ *

+ * Other annotations on the {@code value()} class are ignored. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface BindingsSource { + /** + * The class from which interceptor binding annotations are read. + */ + Class value(); +} diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InterceptionProxy.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InterceptionProxy.java new file mode 100644 index 0000000000000..82a53a862d974 --- /dev/null +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InterceptionProxy.java @@ -0,0 +1,40 @@ +package io.quarkus.arc; + +/** + * A factory for proxies that perform {@link jakarta.interceptor.AroundInvoke AroundInvoke} + * interception before forwarding the method call to the target instance ({@code delegate}). + *

+ * This construct only supports {@code @AroundInvoke} interceptors declared on + * interceptor classes; other kinds of interception, as well as {@code @AroundInvoke} + * interceptors declared on the target class and its superclasses, are not supported. + *

+ * The container provides a built-in bean with scope {@link jakarta.enterprise.context.Dependent Dependent} + * and qualifier {@link jakarta.enterprise.inject.Default Default} that can be injected + * into a method parameter of a producer method. Synthetic bean creation function can + * obtain an instance by calling {@link SyntheticCreationalContext#getInterceptionProxy()}. + * The type argument {@code T} must be equal to the return type of the producer method + * or the provider type of the synthetic bean. + * + *

+ * @Produces
+ * public MyClass produce(InterceptionProxy<MyClass> proxy) {
+ *     return proxy.create(new MyClass());
+ * }
+ * 
+ * + * By default, interceptor binding annotations are obtained from the target class (that is, + * the class of the return type of the producer method or the provider type of the synthetic + * bean). If you want to override that, use {@link BindingsSource}. + * + * @param the type of the target instance (for which the proxy is created) + */ +public interface InterceptionProxy { + /** + * Creates a proxy that wraps given {@code delegate} and performs interception + * before forwarding the method call to the target instance. + * + * @param delegate the target instance + * @return the interception proxy + */ + T create(T delegate); +} diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/SyntheticCreationalContext.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/SyntheticCreationalContext.java index 9e819ccbbb091..e603b951a0a0f 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/SyntheticCreationalContext.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/SyntheticCreationalContext.java @@ -41,4 +41,12 @@ public interface SyntheticCreationalContext extends CreationalContext { */ R getInjectedReference(TypeLiteral requiredType, Annotation... qualifiers); + /** + * Returns an {@link InterceptionProxy} for this synthetic bean. + * + * @return an {@link InterceptionProxy} for this synthetic bean + * @throws IllegalArgumentException if no {@link InterceptionProxy} was registered for this synthetic bean + */ + InterceptionProxy getInterceptionProxy(); + } \ No newline at end of file diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/SyntheticCreationalContextImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/SyntheticCreationalContextImpl.java index 250238c58925e..55d77e42fc5ee 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/SyntheticCreationalContextImpl.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/SyntheticCreationalContextImpl.java @@ -10,6 +10,7 @@ import jakarta.enterprise.inject.Default; import jakarta.enterprise.util.TypeLiteral; +import io.quarkus.arc.InterceptionProxy; import io.quarkus.arc.SyntheticCreationalContext; public final class SyntheticCreationalContextImpl implements SyntheticCreationalContext { @@ -54,6 +55,17 @@ public R getInjectedReference(TypeLiteral requiredType, Annotation... qua return getReference(requiredType.getType(), qualifiers); } + @Override + public InterceptionProxy getInterceptionProxy() { + for (Map.Entry entry : injectedReferences.entrySet()) { + if (entry.getKey().requiredType.getTypeName().startsWith(InterceptionProxy.class.getName())) { + return (InterceptionProxy) entry.getValue(); + } + } + throw new IllegalArgumentException( + "No InterceptionProxy registered for this synthetic bean; call injectInterceptionProxy()"); + } + @SuppressWarnings("unchecked") private R getReference(Type requiredType, Annotation... qualifiers) { if (qualifiers.length == 0) { diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsAndBindingsSourceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsAndBindingsSourceTest.java new file mode 100644 index 0000000000000..08fd3fd371207 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsAndBindingsSourceTest.java @@ -0,0 +1,117 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BindingsSource; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithClassAndMethodLevelInterceptorsAndBindingsSourceTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1", nonbean.hello1()); + assertEquals("intercepted1: hello2", nonbean.hello2()); + assertEquals("hello3", nonbean.hello3()); + assertEquals("intercepted2: hello4", nonbean.hello4()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + static class MyNonbean { + String hello1() { + return "hello1"; + } + + String hello2() { + return "hello2"; + } + + String hello3() { + return "hello3"; + } + + String hello4() { + return "hello4"; + } + } + + @MyBinding1 + static class MyNonbeanBindings { + @MyBinding2 + String hello1() { + return null; + } + + @NoClassInterceptors + String hello3() { + return null; + } + + @NoClassInterceptors + @MyBinding2 + String hello4() { + return null; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(@BindingsSource(MyNonbeanBindings.class) InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsTest.java new file mode 100644 index 0000000000000..6610d28524221 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassAndMethodLevelInterceptorsTest.java @@ -0,0 +1,102 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithClassAndMethodLevelInterceptorsTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1", nonbean.hello1()); + assertEquals("intercepted1: hello2", nonbean.hello2()); + assertEquals("hello3", nonbean.hello3()); + assertEquals("intercepted2: hello4", nonbean.hello4()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + @MyBinding1 + static class MyNonbean { + @MyBinding2 + String hello1() { + return "hello1"; + } + + String hello2() { + return "hello2"; + } + + @NoClassInterceptors + String hello3() { + return "hello3"; + } + + @NoClassInterceptors + @MyBinding2 + String hello4() { + return "hello4"; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsAndBindingsSourceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsAndBindingsSourceTest.java new file mode 100644 index 0000000000000..f1b88ffade4fd --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsAndBindingsSourceTest.java @@ -0,0 +1,97 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BindingsSource; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithClassLevelInterceptorsAndBindingsSourceTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1", nonbean.hello1()); + assertEquals("hello2", nonbean.hello2()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + static class MyNonbean { + String hello1() { + return "hello1"; + } + + String hello2() { + return "hello2"; + } + } + + @MyBinding1 + @MyBinding2 + static class MyNonbeanBindings { + @NoClassInterceptors + String hello2() { + return null; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(@BindingsSource(MyNonbeanBindings.class) InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsTest.java new file mode 100644 index 0000000000000..67f28a11ef1d7 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithClassLevelInterceptorsTest.java @@ -0,0 +1,90 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithClassLevelInterceptorsTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1", nonbean.hello1()); + assertEquals("hello2", nonbean.hello2()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + @MyBinding1 + @MyBinding2 + static class MyNonbean { + String hello1() { + return "hello1"; + } + + @NoClassInterceptors + String hello2() { + return "hello2"; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithFinalInterceptedClassTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithFinalInterceptedClassTest.java new file mode 100644 index 0000000000000..cf18a3a1d9bba --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithFinalInterceptedClassTest.java @@ -0,0 +1,71 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithFinalInterceptedClassTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding.class, MyInterceptor.class, MyProducer.class) + .shouldFail() + .build(); + + @Test + public void test() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DeploymentException); + assertTrue(error.getMessage().contains("must not be final")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static final class MyNonbean { + @MyBinding + String hello() { + return "hello"; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithFinalInterceptedMethodTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithFinalInterceptedMethodTest.java new file mode 100644 index 0000000000000..125ba96351e04 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithFinalInterceptedMethodTest.java @@ -0,0 +1,71 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithFinalInterceptedMethodTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding.class, MyInterceptor.class, MyProducer.class) + .shouldFail() + .build(); + + @Test + public void test() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DeploymentException); + assertTrue(error.getMessage().contains("may not be declared final")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static class MyNonbean { + @MyBinding + final String hello() { + return "hello"; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithGenericClassInterceptionAndBindingsSourceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithGenericClassInterceptionAndBindingsSourceTest.java new file mode 100644 index 0000000000000..5fb208f7f8e5c --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithGenericClassInterceptionAndBindingsSourceTest.java @@ -0,0 +1,105 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.util.TypeLiteral; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BindingsSource; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithGenericClassInterceptionAndBindingsSourceTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(new TypeLiteral>() { + }).get(); + assertEquals("intercepted1: hello1", nonbean.hello1("hello1")); + assertEquals("intercepted1: intercepted2: hello2", nonbean.hello2("hello2")); + assertEquals("intercepted2: hello3", nonbean.hello3("hello3")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + static class MyNonbean { + public T hello1(T param) { + return param; + } + + public T hello2(T param) { + return param; + } + + public T hello3(T param) { + return param; + } + } + + @MyBinding1 + static abstract class MyNonbeanBindings { + @MyBinding2 + abstract T hello2(T param); + + @MyBinding2 + @NoClassInterceptors + abstract T hello3(T param); + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(@BindingsSource(MyNonbeanBindings.class) InterceptionProxy> proxy) { + return proxy.create(new MyNonbean<>()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithGenericClassInterceptionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithGenericClassInterceptionTest.java new file mode 100644 index 0000000000000..3f6fbb085643b --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithGenericClassInterceptionTest.java @@ -0,0 +1,98 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.util.TypeLiteral; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithGenericClassInterceptionTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(new TypeLiteral>() { + }).get(); + assertEquals("intercepted1: hello1", nonbean.hello1("hello1")); + assertEquals("intercepted1: intercepted2: hello2", nonbean.hello2("hello2")); + assertEquals("intercepted2: hello3", nonbean.hello3("hello3")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + @MyBinding1 + static class MyNonbean { + public T hello1(T param) { + return param; + } + + @MyBinding2 + public T hello2(T param) { + return param; + } + + @MyBinding2 + @NoClassInterceptors + public T hello3(T param) { + return param; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy> proxy) { + return proxy.create(new MyNonbean<>()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterceptionAndDestructionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterceptionAndDestructionTest.java new file mode 100644 index 0000000000000..09c74b273d834 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterceptionAndDestructionTest.java @@ -0,0 +1,100 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithInterceptionAndDestructionTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyDependency.class, MyBinding.class, MyInterceptor.class, + MyProducer.class); + + @Test + public void test() { + InstanceHandle instance = Arc.container().instance(MyNonbean.class); + MyNonbean nonbean = instance.get(); + assertEquals("intercepted: hello", nonbean.hello()); + + assertFalse(MyProducer.disposed); + assertFalse(MyDependency.destroyed); + instance.destroy(); + assertTrue(MyProducer.disposed); + // this seems to be underspecified and when using `InterceptionFactory`, it is `false` in Weld too + // (technically, it could be `true` as well, but I didn't look too deep into what that would take) + assertFalse(MyDependency.destroyed); + } + + @Dependent + static class MyDependency { + static boolean destroyed = false; + + @PreDestroy + void destroy() { + destroyed = true; + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @Inject + MyDependency dependency; + + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static class MyNonbean { + @MyBinding + String hello() { + return "hello"; + } + } + + @Dependent + static class MyProducer { + static boolean disposed = false; + + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + + void dispose(@Disposes MyNonbean nonbean) { + disposed = true; + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterfaceInterceptionAndBindingsSourceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterfaceInterceptionAndBindingsSourceTest.java new file mode 100644 index 0000000000000..b678a91aae887 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterfaceInterceptionAndBindingsSourceTest.java @@ -0,0 +1,111 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BindingsSource; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithInterfaceInterceptionAndBindingsSourceTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: hello1", nonbean.hello1()); + assertEquals("intercepted1: intercepted2: hello2", nonbean.hello2()); + assertEquals("intercepted2: hello3", nonbean.hello3()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + interface MyNonbean { + String hello1(); + + String hello2(); + + default String hello3() { + return "hello3"; + } + } + + static class MyNonbeanImpl implements MyNonbean { + @Override + public String hello1() { + return "hello1"; + } + + @Override + public String hello2() { + return "hello2"; + } + } + + @MyBinding1 + static abstract class MyNonbeanBindings { + @MyBinding2 + abstract String hello2(); + + @MyBinding2 + @NoClassInterceptors + abstract String hello3(); + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(@BindingsSource(MyNonbeanBindings.class) InterceptionProxy proxy) { + return proxy.create(new MyNonbeanImpl()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterfaceInterceptionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterfaceInterceptionTest.java new file mode 100644 index 0000000000000..28c96f8b9e0c1 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithInterfaceInterceptionTest.java @@ -0,0 +1,104 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithInterfaceInterceptionTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: hello1", nonbean.hello1()); + assertEquals("intercepted1: intercepted2: hello2", nonbean.hello2()); + assertEquals("intercepted2: hello3", nonbean.hello3()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + @MyBinding1 + interface MyNonbean { + String hello1(); + + @MyBinding2 + String hello2(); + + @MyBinding2 + @NoClassInterceptors + default String hello3() { + return "hello3"; + } + } + + static class MyNonbeanImpl implements MyNonbean { + @Override + public String hello1() { + return "hello1"; + } + + @Override + public String hello2() { + return "hello2"; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbeanImpl()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMethodLevelInterceptorsAndBindingsSourceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMethodLevelInterceptorsAndBindingsSourceTest.java new file mode 100644 index 0000000000000..9611dcd4f5e46 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMethodLevelInterceptorsAndBindingsSourceTest.java @@ -0,0 +1,115 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BindingsSource; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithMethodLevelInterceptorsAndBindingsSourceTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: hello1", nonbean.hello1()); + assertEquals("intercepted2: hello2", nonbean.hello2()); + assertEquals("intercepted1: intercepted2: hello3", nonbean.hello3()); + assertEquals("hello4", nonbean.hello4()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + static class MyNonbean { + String hello1() { + return "hello1"; + } + + String hello2() { + return "hello2"; + } + + String hello3() { + return "hello3"; + } + + String hello4() { + return "hello4"; + } + } + + static class MyNonbeanBindings { + @MyBinding1 + String hello1() { + return null; + } + + @MyBinding2 + String hello2() { + return null; + } + + @MyBinding1 + @MyBinding2 + String hello3() { + return null; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(@BindingsSource(MyNonbeanBindings.class) InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMethodLevelInterceptorsTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMethodLevelInterceptorsTest.java new file mode 100644 index 0000000000000..2682dead22556 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMethodLevelInterceptorsTest.java @@ -0,0 +1,100 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithMethodLevelInterceptorsTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: hello1", nonbean.hello1()); + assertEquals("intercepted2: hello2", nonbean.hello2()); + assertEquals("intercepted1: intercepted2: hello3", nonbean.hello3()); + assertEquals("hello4", nonbean.hello4()); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + static class MyNonbean { + @MyBinding1 + String hello1() { + return "hello1"; + } + + @MyBinding2 + String hello2() { + return "hello2"; + } + + @MyBinding1 + @MyBinding2 + String hello3() { + return "hello3"; + } + + String hello4() { + return "hello4"; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMultipleInterceptionProxyParamsTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMultipleInterceptionProxyParamsTest.java new file mode 100644 index 0000000000000..03b42ffd24f10 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithMultipleInterceptionProxyParamsTest.java @@ -0,0 +1,47 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.DefinitionException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithMultipleInterceptionProxyParamsTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyProducer.class) + .shouldFail() + .build(); + + @Test + public void test() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DefinitionException); + assertTrue(error.getMessage().contains("more than one InterceptionProxy parameter")); + } + + static class MyNonbean1 { + } + + static class MyNonbean2 { + } + + static class MyNonbean3 { + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean1 nonbean1(InterceptionProxy nonbean2, InterceptionProxy nonbean3) { + return null; + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithParameterizedConstructorsAndInterceptionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithParameterizedConstructorsAndInterceptionTest.java new file mode 100644 index 0000000000000..930caf9d925f9 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithParameterizedConstructorsAndInterceptionTest.java @@ -0,0 +1,155 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithParameterizedConstructorsAndInterceptionTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void constructorWithNoParams() { + MyProducer.constructorId = 0; + + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_0_0", nonbean.hello1()); + assertEquals("intercepted2: hello2_0_0_6", nonbean.hello2(6)); + } + + @Test + public void constructorWithIntParam() { + MyProducer.constructorId = 1; + + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_1_0", nonbean.hello1()); + assertEquals("intercepted2: hello2_1_0_7", nonbean.hello2(7)); + } + + @Test + public void constructorWithIntIntParams() { + MyProducer.constructorId = 2; + + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_2_3", nonbean.hello1()); + assertEquals("intercepted2: hello2_2_3_8", nonbean.hello2(8)); + } + + @Test + public void constructorWithByteShortParams() { + MyProducer.constructorId = 3; + + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_4_5", nonbean.hello1()); + assertEquals("intercepted2: hello2_4_5_9", nonbean.hello2(9)); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + @MyBinding1 + static class MyNonbean { + private final int i; + private final int j; + + MyNonbean() { + this.i = 0; + this.j = 0; + } + + MyNonbean(int i) { + this.i = i; + this.j = 0; + } + + MyNonbean(int i, int j) { + this.i = i; + this.j = j; + } + + MyNonbean(byte i, short j) { + this.i = i; + this.j = j; + } + + @MyBinding2 + String hello1() { + return "hello1_" + i + "_" + j; + } + + @NoClassInterceptors + @MyBinding2 + String hello2(int k) { + return "hello2_" + i + "_" + j + "_" + k; + } + } + + @Dependent + static class MyProducer { + static int constructorId; + + @Produces + MyNonbean produce(InterceptionProxy proxy) { + if (constructorId == 0) { + return proxy.create(new MyNonbean()); + } else if (constructorId == 1) { + return proxy.create(new MyNonbean(1)); + } else if (constructorId == 2) { + return proxy.create(new MyNonbean(2, 3)); + } else if (constructorId == 3) { + return proxy.create(new MyNonbean((byte) 4, (short) 5)); + } else { + throw new IllegalStateException(); + } + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithParameterizedInterceptedMethodsTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithParameterizedInterceptedMethodsTest.java new file mode 100644 index 0000000000000..980d9910e6733 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithParameterizedInterceptedMethodsTest.java @@ -0,0 +1,102 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithParameterizedInterceptedMethodsTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer(MyBinding1.class, MyInterceptor1.class, + MyBinding2.class, MyInterceptor2.class, MyProducer.class); + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_1", nonbean.hello1(1)); + assertEquals("intercepted1: hello2_2_3", nonbean.hello2(2, 3)); + assertEquals("intercepted2: hello3_4_5_6", nonbean.hello3(4, 5, 6)); + assertEquals("hello4_7", nonbean.hello4(7)); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + @MyBinding1 + static class MyNonbean { + @MyBinding2 + String hello1(int i) { + return "hello1_" + i; + } + + String hello2(int i, int j) { + return "hello2_" + i + "_" + j; + } + + @NoClassInterceptors + @MyBinding2 + String hello3(int i, int j, int k) { + return "hello3_" + i + "_" + j + "_" + k; + } + + @NoClassInterceptors + String hello4(int i) { + return "hello4_" + i; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithPrivateZeroParamCtorAndInterceptionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithPrivateZeroParamCtorAndInterceptionTest.java new file mode 100644 index 0000000000000..a3a4fed447c79 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithPrivateZeroParamCtorAndInterceptionTest.java @@ -0,0 +1,74 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithPrivateZeroParamCtorAndInterceptionTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding.class, MyInterceptor.class, MyProducer.class) + .shouldFail() + .build(); + + @Test + public void test() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DeploymentException); + assertTrue(error.getMessage().contains("is not proxyable because it has a private no-args constructor")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static class MyNonbean { + private MyNonbean() { + } + + @MyBinding + String hello() { + return "hello"; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean()); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java new file mode 100644 index 0000000000000..acc7546f20148 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/producer/ProducerWithoutZeroParamCtorAndInterceptionTest.java @@ -0,0 +1,74 @@ +package io.quarkus.arc.test.interceptors.producer; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.test.ArcTestContainer; + +public class ProducerWithoutZeroParamCtorAndInterceptionTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding.class, MyInterceptor.class, MyProducer.class) + .shouldFail() + .build(); + + @Test + public void test() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DeploymentException); + assertTrue(error.getMessage().contains("must declare a non-private constructor with no parameters")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static class MyNonbean { + MyNonbean(int ignored) { + } + + @MyBinding + String hello() { + return "hello"; + } + } + + @Dependent + static class MyProducer { + @Produces + MyNonbean produce(InterceptionProxy proxy) { + return proxy.create(new MyNonbean(0)); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithFinalInterceptedClassTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithFinalInterceptedClassTest.java new file mode 100644 index 0000000000000..527f584a64906 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithFinalInterceptedClassTest.java @@ -0,0 +1,82 @@ +package io.quarkus.arc.test.interceptors.synthbean; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.BeanCreator; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.arc.processor.BeanRegistrar; +import io.quarkus.arc.test.ArcTestContainer; + +public class SynthBeanWithFinalInterceptedClassTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding.class, MyInterceptor.class) + .beanRegistrars(new BeanRegistrar() { + @Override + public void register(RegistrationContext context) { + context.configure(MyNonbean.class) + .types(MyNonbean.class) + .injectInterceptionProxy(MyNonbean.class) + .creator(MyNonbeanCreator.class) + .done(); + } + }) + .shouldFail() + .build(); + + static class MyNonbeanCreator implements BeanCreator { + @Override + public MyNonbean create(SyntheticCreationalContext context) { + InterceptionProxy proxy = context.getInterceptionProxy(); + return proxy.create(new MyNonbean()); + } + } + + @Test + public void test() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DeploymentException); + assertTrue(error.getMessage().contains("must not be final")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static final class MyNonbean { + @MyBinding + String hello() { + return "hello"; + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithFinalInterceptedMethodTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithFinalInterceptedMethodTest.java new file mode 100644 index 0000000000000..b441f189adff2 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithFinalInterceptedMethodTest.java @@ -0,0 +1,82 @@ +package io.quarkus.arc.test.interceptors.synthbean; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.BeanCreator; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.arc.processor.BeanRegistrar; +import io.quarkus.arc.test.ArcTestContainer; + +public class SynthBeanWithFinalInterceptedMethodTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding.class, MyInterceptor.class) + .beanRegistrars(new BeanRegistrar() { + @Override + public void register(RegistrationContext context) { + context.configure(MyNonbean.class) + .types(MyNonbean.class) + .injectInterceptionProxy(MyNonbean.class) + .creator(MyNonbeanCreator.class) + .done(); + } + }) + .shouldFail() + .build(); + + static class MyNonbeanCreator implements BeanCreator { + @Override + public MyNonbean create(SyntheticCreationalContext context) { + InterceptionProxy proxy = context.getInterceptionProxy(); + return proxy.create(new MyNonbean()); + } + } + + @Test + public void test() { + Throwable error = container.getFailure(); + assertNotNull(error); + assertTrue(error instanceof DeploymentException); + assertTrue(error.getMessage().contains("may not be declared final")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding { + } + + @MyBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + + static class MyNonbean { + @MyBinding + final String hello() { + return "hello"; + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithGenericClassInterceptionAndBindingsSourceTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithGenericClassInterceptionAndBindingsSourceTest.java new file mode 100644 index 0000000000000..8186c033b71ad --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithGenericClassInterceptionAndBindingsSourceTest.java @@ -0,0 +1,124 @@ +package io.quarkus.arc.test.interceptors.synthbean; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.util.TypeLiteral; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.jboss.jandex.DotName; +import org.jboss.jandex.ParameterizedType; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BeanCreator; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.arc.processor.BeanRegistrar; +import io.quarkus.arc.test.ArcTestContainer; + +public class SynthBeanWithGenericClassInterceptionAndBindingsSourceTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding1.class, MyInterceptor1.class, MyBinding2.class, MyInterceptor2.class) + .beanRegistrars(new BeanRegistrar() { + @Override + public void register(RegistrationContext context) { + ParameterizedType type = ParameterizedType.builder(MyNonbean.class) + .addArgument(String.class) + .build(); + + context.configure(MyNonbean.class) + .types(type) + .providerType(type) + .injectInterceptionProxy(DotName.createSimple(MyNonbeanBindings.class)) + .creator(MyNonbeanCreator.class) + .done(); + + } + }) + .build(); + + static class MyNonbeanCreator implements BeanCreator> { + @Override + public MyNonbean create(SyntheticCreationalContext> context) { + InterceptionProxy> proxy = context.getInterceptionProxy(); + return proxy.create(new MyNonbean<>()); + } + } + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(new TypeLiteral>() { + }).get(); + assertEquals("intercepted1: hello1", nonbean.hello1("hello1")); + assertEquals("intercepted1: intercepted2: hello2", nonbean.hello2("hello2")); + assertEquals("intercepted2: hello3", nonbean.hello3("hello3")); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + static class MyNonbean { + public T hello1(T param) { + return param; + } + + public T hello2(T param) { + return param; + } + + public T hello3(T param) { + return param; + } + } + + @MyBinding1 + static abstract class MyNonbeanBindings { + @MyBinding2 + abstract T hello2(T param); + + @MyBinding2 + @NoClassInterceptors + abstract T hello3(T param); + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithParameterizedConstructorsAndInterceptionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithParameterizedConstructorsAndInterceptionTest.java new file mode 100644 index 0000000000000..d320bd5c4a972 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithParameterizedConstructorsAndInterceptionTest.java @@ -0,0 +1,167 @@ +package io.quarkus.arc.test.interceptors.synthbean; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BeanCreator; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.arc.processor.BeanRegistrar; +import io.quarkus.arc.test.ArcTestContainer; + +public class SynthBeanWithParameterizedConstructorsAndInterceptionTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding1.class, MyInterceptor1.class, MyBinding2.class, MyInterceptor2.class) + .beanRegistrars(new BeanRegistrar() { + @Override + public void register(RegistrationContext context) { + context.configure(MyNonbean.class) + .types(MyNonbean.class) + .injectInterceptionProxy(MyNonbean.class) + .creator(MyNonbeanCreator.class) + .done(); + } + }) + .build(); + + static class MyNonbeanCreator implements BeanCreator { + static int constructorId; + + @Override + public MyNonbean create(SyntheticCreationalContext context) { + InterceptionProxy proxy = context.getInterceptionProxy(); + if (constructorId == 0) { + return proxy.create(new MyNonbean()); + } else if (constructorId == 1) { + return proxy.create(new MyNonbean(1)); + } else if (constructorId == 2) { + return proxy.create(new MyNonbean(2, 3)); + } else if (constructorId == 3) { + return proxy.create(new MyNonbean((byte) 4, (short) 5)); + } else { + throw new IllegalStateException(); + } + } + } + + @Test + public void constructorWithNoParams() { + MyNonbeanCreator.constructorId = 0; + + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_0_0", nonbean.hello1()); + assertEquals("intercepted2: hello2_0_0_6", nonbean.hello2(6)); + } + + @Test + public void constructorWithIntParam() { + MyNonbeanCreator.constructorId = 1; + + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_1_0", nonbean.hello1()); + assertEquals("intercepted2: hello2_1_0_7", nonbean.hello2(7)); + } + + @Test + public void constructorWithIntIntParams() { + MyNonbeanCreator.constructorId = 2; + + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_2_3", nonbean.hello1()); + assertEquals("intercepted2: hello2_2_3_8", nonbean.hello2(8)); + } + + @Test + public void constructorWithByteShortParams() { + MyNonbeanCreator.constructorId = 3; + + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_4_5", nonbean.hello1()); + assertEquals("intercepted2: hello2_4_5_9", nonbean.hello2(9)); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + @MyBinding1 + static class MyNonbean { + private final int i; + private final int j; + + MyNonbean() { + this.i = 0; + this.j = 0; + } + + MyNonbean(int i) { + this.i = i; + this.j = 0; + } + + MyNonbean(int i, int j) { + this.i = i; + this.j = j; + } + + MyNonbean(byte i, short j) { + this.i = i; + this.j = j; + } + + @MyBinding2 + String hello1() { + return "hello1_" + i + "_" + j; + } + + @NoClassInterceptors + @MyBinding2 + String hello2(int k) { + return "hello2_" + i + "_" + j + "_" + k; + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithParameterizedInterceptedMethodsTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithParameterizedInterceptedMethodsTest.java new file mode 100644 index 0000000000000..b048d216f6682 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/synthbean/SynthBeanWithParameterizedInterceptedMethodsTest.java @@ -0,0 +1,114 @@ +package io.quarkus.arc.test.interceptors.synthbean; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BeanCreator; +import io.quarkus.arc.InterceptionProxy; +import io.quarkus.arc.NoClassInterceptors; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.arc.processor.BeanRegistrar; +import io.quarkus.arc.test.ArcTestContainer; + +public class SynthBeanWithParameterizedInterceptedMethodsTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(MyBinding1.class, MyInterceptor1.class, MyBinding2.class, MyInterceptor2.class) + .beanRegistrars(new BeanRegistrar() { + @Override + public void register(RegistrationContext context) { + context.configure(MyNonbean.class) + .types(MyNonbean.class) + .injectInterceptionProxy(MyNonbean.class) + .creator(MyNonbeanCreator.class) + .done(); + } + }) + .build(); + + static class MyNonbeanCreator implements BeanCreator { + @Override + public MyNonbean create(SyntheticCreationalContext context) { + InterceptionProxy proxy = context.getInterceptionProxy(); + return proxy.create(new MyNonbean()); + } + } + + @Test + public void test() { + MyNonbean nonbean = Arc.container().instance(MyNonbean.class).get(); + assertEquals("intercepted1: intercepted2: hello1_1", nonbean.hello1(1)); + assertEquals("intercepted1: hello2_2_3", nonbean.hello2(2, 3)); + assertEquals("intercepted2: hello3_4_5_6", nonbean.hello3(4, 5, 6)); + assertEquals("hello4_7", nonbean.hello4(7)); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding1 { + } + + @MyBinding1 + @Priority(1) + @Interceptor + static class MyInterceptor1 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted1: " + ctx.proceed(); + } + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR }) + @InterceptorBinding + @interface MyBinding2 { + } + + @MyBinding2 + @Priority(2) + @Interceptor + static class MyInterceptor2 { + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + return "intercepted2: " + ctx.proceed(); + } + } + + @MyBinding1 + static class MyNonbean { + @MyBinding2 + String hello1(int i) { + return "hello1_" + i; + } + + String hello2(int i, int j) { + return "hello2_" + i + "_" + j; + } + + @NoClassInterceptors + @MyBinding2 + String hello3(int i, int j, int k) { + return "hello3_" + i + "_" + j + "_" + k; + } + + @NoClassInterceptors + String hello4(int i) { + return "hello4_" + i; + } + } +} From fe4ca67e744d0da20f7f5ab6371bd99be63eca92 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Wed, 3 Jul 2024 15:44:38 +0200 Subject: [PATCH 13/94] Switch to SmallRye Certificate Generator Migrate from me.escoffier.certs:certificate-generator to io.smallrye.certs:smallrye-certificate-generator. As the certificate generator sees increased usage, including its upcoming role in TLS commands for generating CA and signed certificates, transitioning the code to SmallRye for is a logical step. --- build-parent/pom.xml | 8 +++++--- extensions/grpc/deployment/pom.xml | 4 ++-- .../grpc/client/HelloWorldTlsEndpointTest.java | 6 +++--- .../tls/MtlsWithJKSTrustStoreWithHttpServerTest.java | 6 +++--- ...WithJKSTrustStoreWithHttpServerWithAliasTest.java | 8 ++++---- ...STrustStoreWithHttpServerWithTlsRegistryTest.java | 6 +++--- .../tls/MtlsWithP12TrustStoreWithHttpServerTest.java | 6 +++--- ...WithP12TrustStoreWithHttpServerWithAliasTest.java | 8 ++++---- ...2TrustStoreWithHttpServerWithTlsRegistryTest.java | 6 +++--- .../tls/MtlsWithPemTrustStoreWithHttpServerTest.java | 6 +++--- ...mTrustStoreWithHttpServerWithTlsRegistryTest.java | 6 +++--- .../tls/TlsWithJKSTrustStoreAndTlsRegistryTest.java | 6 +++--- .../grpc/client/tls/TlsWithJKSTrustStoreTest.java | 6 +++--- ...KSTrustStoreWithHttpServerAndTlsRegistryTest.java | 6 +++--- .../tls/TlsWithJKSTrustStoreWithHttpServerTest.java | 6 +++--- .../tls/TlsWithP12TrustStoreAndTlsRegistryTest.java | 6 +++--- .../grpc/client/tls/TlsWithP12TrustStoreTest.java | 6 +++--- ...12TrustStoreWithHttpServerAndTlsRegistryTest.java | 6 +++--- .../tls/TlsWithP12TrustStoreWithHttpServerTest.java | 6 +++--- .../grpc/client/tls/TlsWithPemTrustStoreTest.java | 6 +++--- .../tls/TlsWithPemTrustStoreWithHttpServerTest.java | 6 +++--- ...mTrustStoreWithHttpServerWithTlsRegistryTest.java | 6 +++--- .../tls/TlsWithPemTrustStoreWithTlsRegistryTest.java | 6 +++--- .../grpc/server/MutinyGrpcServiceWithSSLTest.java | 6 +++--- .../RegularGrpcServiceWithSSLFromClasspathTest.java | 6 +++--- .../grpc/server/RegularGrpcServiceWithSSLTest.java | 6 +++--- .../TlsWithHttpServerUsingJKSAndTlsRegistryTest.java | 6 +++--- .../server/tls/TlsWithHttpServerUsingJKSTest.java | 6 +++--- .../tls/TlsWithHttpServerUsingJKSWithAliasTest.java | 8 ++++---- .../TlsWithHttpServerUsingP12AndTlsRegistryTest.java | 6 +++--- .../server/tls/TlsWithHttpServerUsingP12Test.java | 6 +++--- .../tls/TlsWithHttpServerUsingP12WithAliasTest.java | 8 ++++---- .../TlsWithHttpServerUsingPemAndTlsRegistryTest.java | 6 +++--- .../server/tls/TlsWithHttpServerUsingPemTest.java | 6 +++--- .../server/tls/TlsWithJksKeyStoreAndAliasTest.java | 8 ++++---- .../grpc/server/tls/TlsWithJksKeyStoreTest.java | 6 +++--- .../server/tls/TlsWithP12KeyStoreAndAliasTest.java | 8 ++++---- .../grpc/server/tls/TlsWithP12KeyStoreTest.java | 6 +++--- .../grpc/server/tls/TlsWithPemKeyStoreTest.java | 6 +++--- extensions/mailer/runtime/pom.xml | 4 ++-- .../io/quarkus/mailer/runtime/FakeSmtpTestBase.java | 6 +++--- .../resteasy-reactive/rest-client/deployment/pom.xml | 4 ++-- .../reactive/tls/MtlsConfigFromRegistryCdiTest.java | 6 +++--- .../reactive/tls/TlsConfigFromPropertiesCdiTest.java | 6 +++--- .../reactive/tls/TlsConfigFromRegistryCdiTest.java | 6 +++--- .../tls/TlsConfigFromRegistryManualTest.java | 6 +++--- extensions/tls-registry/deployment/pom.xml | 4 ++-- .../io/quarkus/tls/BuildTimeRegistrationTest.java | 6 +++--- .../tls/DefaultJKSKeyStoreAndTrustStoreTest.java | 6 +++--- .../java/io/quarkus/tls/DefaultJKSKeyStoreTest.java | 6 +++--- .../quarkus/tls/DefaultJKSKeyStoreWithAliasTest.java | 11 ++++++----- .../io/quarkus/tls/DefaultJKSTrustStoreTest.java | 6 +++--- .../tls/DefaultJKSTrustStoreWithAliasTest.java | 11 ++++++----- ...aultJKSTrustStoreWithCredentialsProviderTest.java | 6 +++--- ...ustStoreWithCredentialsProviderWithAliasTest.java | 8 ++++---- ...toreWithCredentialsProviderWithCustomKeyTest.java | 6 +++--- .../java/io/quarkus/tls/DefaultP12KeyStoreTest.java | 6 +++--- .../quarkus/tls/DefaultP12KeyStoreWithAliasTest.java | 11 ++++++----- .../io/quarkus/tls/DefaultP12TrustStoreTest.java | 6 +++--- .../tls/DefaultP12TrustStoreWithAliasTest.java | 11 ++++++----- ...aultP12TrustStoreWithCredentialsProviderTest.java | 6 +++--- ...ustStoreWithCredentialsProviderWithAliasTest.java | 8 ++++---- .../java/io/quarkus/tls/DefaultPemKeyStoreTest.java | 6 +++--- .../DefaultPemKeyStoreWithMultipleAliasesTest.java | 11 ++++++----- .../io/quarkus/tls/DefaultPemTrustStoreTest.java | 6 +++--- .../DefaultPemTrustStoreWithMultipleAliasesTest.java | 11 ++++++----- .../java/io/quarkus/tls/DefaultSSLOptionsTest.java | 12 ++++++------ .../tls/JKSKeyStoreCredentialsProviderTest.java | 6 +++--- ...eyStoreCredentialsProviderWithCustomKeysTest.java | 6 +++--- .../io/quarkus/tls/JKSKeyStoreFromClassPathTest.java | 6 +++--- .../JKSKeyStoreWithAliasCredentialsProviderTest.java | 8 ++++---- .../tls/JKSKeyStoreWithMissingAliasPasswordTest.java | 11 ++++++----- ...eyStoreWithOverriddenCredentialsProviderTest.java | 6 +++--- .../java/io/quarkus/tls/JKSKeyStoreWithSniTest.java | 9 +++++---- .../tls/JKSKeyStoreWithWrongAliasPasswordTest.java | 11 ++++++----- .../tls/JKSKeyStoreWithWrongPasswordTest.java | 6 +++--- .../quarkus/tls/JKSKeyStoreWithoutPasswordTest.java | 6 +++--- .../quarkus/tls/JKSTrustStoreFromClassPathTest.java | 6 +++--- ...stStoreWithOverriddenCredentialsProviderTest.java | 6 +++--- .../tls/JKSTrustStoreWithWrongPasswordTest.java | 6 +++--- .../tls/JKSTrustStoreWithoutPasswordTest.java | 6 +++--- ...eyStoreWithMissingKeyCredentialsProviderTest.java | 6 +++--- .../KeyStoreWithSelectedCredentialsProviderTest.java | 6 +++--- .../quarkus/tls/KeyStoreWithSniAndAliasSetTest.java | 9 +++++---- .../tls/KeyStoreWithSniAndSingleAliasSetTest.java | 7 ++++--- .../tls/MissingJKSKeyStoreFromClassPathTest.java | 6 +++--- .../tls/MissingJKSKeyStoreFromFileSystemTest.java | 6 +++--- .../tls/MissingJKSTrustStoreFromClassPathTest.java | 6 +++--- .../tls/MissingP12KeyStoreFromClassPathTest.java | 6 +++--- .../tls/MissingP12KeyStoreFromFileSystemTest.java | 6 +++--- .../tls/MissingP12TrustStoreFromClassPathTest.java | 6 +++--- .../tls/MissingP12TrustStoreFromFileSystemTest.java | 6 +++--- .../test/java/io/quarkus/tls/MissingPemCertTest.java | 6 +++--- .../test/java/io/quarkus/tls/MissingPemKeyTest.java | 6 +++--- .../quarkus/tls/MissingTrustStorePemCertsTest.java | 6 +++--- .../tls/NamedJKSKeyStoreCredentialsProviderTest.java | 6 +++--- .../java/io/quarkus/tls/NamedJKSKeyStoreTest.java | 6 +++--- ...dJKSKeyStoreWithAliasCredentialsProviderTest.java | 8 ++++---- .../java/io/quarkus/tls/NamedJKSTrustStoreTest.java | 6 +++--- ...amedJKSTrustStoreWithCredentialsProviderTest.java | 6 +++--- ...ustStoreWithCredentialsProviderWithAliasTest.java | 8 ++++---- .../tls/NamedP12KeyStoreCredentialsProviderTest.java | 6 +++--- .../java/io/quarkus/tls/NamedP12KeyStoreTest.java | 6 +++--- ...dP12KeyStoreWithAliasCredentialsProviderTest.java | 8 ++++---- .../java/io/quarkus/tls/NamedP12TrustStoreTest.java | 6 +++--- ...amedP12TrustStoreWithCredentialsProviderTest.java | 6 +++--- ...ustStoreWithCredentialsProviderWithAliasTest.java | 8 ++++---- .../java/io/quarkus/tls/NamedPemKeyStoreTest.java | 6 +++--- .../java/io/quarkus/tls/NamedPemTrustStoreTest.java | 6 +++--- .../java/io/quarkus/tls/NamedSSLOptionsTest.java | 12 ++++++------ .../tls/P12KeyStoreCredentialsProviderTest.java | 6 +++--- .../io/quarkus/tls/P12KeyStoreFromClassPathTest.java | 6 +++--- .../P12KeyStoreWithAliasCredentialsProviderTest.java | 8 ++++---- .../tls/P12KeyStoreWithMissingAliasPasswordTest.java | 11 ++++++----- ...eyStoreWithOverriddenCredentialsProviderTest.java | 6 +++--- .../java/io/quarkus/tls/P12KeyStoreWithSniTest.java | 9 +++++---- .../tls/P12KeyStoreWithWrongAliasPasswordTest.java | 11 ++++++----- .../tls/P12KeyStoreWithWrongPasswordTest.java | 6 +++--- .../quarkus/tls/P12KeyStoreWithoutPasswordTest.java | 6 +++--- .../quarkus/tls/P12TrustStoreFromClassPathTest.java | 6 +++--- ...stStoreWithOverriddenCredentialsProviderTest.java | 6 +++--- .../tls/P12TrustStoreWithWrongPasswordTest.java | 6 +++--- .../tls/P12TrustStoreWithoutPasswordTest.java | 6 +++--- .../tls/PemCertOrderWithNotEnoughValueTest.java | 6 +++--- .../tls/PemCertOrderWithTooManyValueTest.java | 6 +++--- .../io/quarkus/tls/PemKeyStoreFromClassPathTest.java | 6 +++--- .../io/quarkus/tls/PemKeyStoreNaturalOrderTest.java | 11 ++++++----- .../io/quarkus/tls/PemKeyStoreUserOrderTest.java | 11 ++++++----- .../java/io/quarkus/tls/PemKeyStoreWithSniTest.java | 9 +++++---- .../quarkus/tls/PemTrustStoreFromClassPathTest.java | 6 +++--- .../test/java/io/quarkus/tls/PemWithoutKeyTest.java | 6 +++--- .../test/java/io/quarkus/tls/ReloadKeyStoreTest.java | 6 +++--- .../java/io/quarkus/tls/ReloadTrustStoreTest.java | 6 +++--- .../java/io/quarkus/tls/RuntimeRegistrationTest.java | 6 +++--- .../tls/TooManyKeyStoreConfiguredJKSAndP12Test.java | 6 +++--- .../tls/TooManyKeyStoreConfiguredPemAndJKSTest.java | 6 +++--- .../tls/TooManyKeyStoreConfiguredPemAndP12Test.java | 6 +++--- .../TooManyTrustStoreConfiguredJKSAndP12Test.java | 6 +++--- .../TooManyTrustStoreConfiguredPemAndJKSTest.java | 6 +++--- .../TooManyTrustStoreConfiguredPemAndP12Test.java | 6 +++--- .../io/quarkus/tls/TrustAllWithTrustStoreTest.java | 7 ++++--- ...TrustStoreWithMissingCredentialsProviderTest.java | 6 +++--- ...stStoreWithMissingKeyCredentialsProviderTest.java | 6 +++--- ...reWithMissingSelectedCredentialsProviderTest.java | 6 +++--- ...rustStoreWithSelectedCredentialsProviderTest.java | 6 +++--- extensions/vertx-http/deployment/pom.xml | 4 ++-- .../io/quarkus/vertx/http/DisableHttpPortTest.java | 6 +++--- .../http/DisableHttpPortWithTlsRegistryTest.java | 6 +++--- .../MainHttpServerTlsCertificateReloadTest.java | 6 +++--- ...icateReloadWithTlsRegistryAndUpdateEventTest.java | 6 +++--- ...erverTlsCertificateReloadWithTlsRegistryTest.java | 6 +++--- ...MainHttpServerTlsPKCS12CertificateReloadTest.java | 6 +++--- ...icateReloadWithTlsRegistryAndUpdateEventTest.java | 6 +++--- ...lsPKCS12CertificateReloadWithTlsRegistryTest.java | 6 +++--- ...ManagementHttpServerTlsCertificateReloadTest.java | 6 +++--- ...erverTlsCertificateReloadWithTlsRegistryTest.java | 6 +++--- .../http2/Http2RSTFloodProtectionConfigTest.java | 6 +++--- .../http/http2/Http2RSTFloodProtectionTest.java | 6 +++--- .../java/io/quarkus/vertx/http/http2/Http2Test.java | 6 +++--- .../http2/Http2WithNamedConfigTlsRegistryTest.java | 6 +++--- .../vertx/http/http2/Http2WithTlsRegistryTest.java | 6 +++--- ...AndPrimaryUsingDifferentTlsConfigurationTest.java | 6 +++--- ...ementAndPrimaryUsingSameTlsConfigurationTest.java | 6 +++--- .../ManagementWithJksAndTlsRegistryTest.java | 6 +++--- .../vertx/http/management/ManagementWithJksTest.java | 6 +++--- ...ManagementWithJksWithAliasAndTlsRegistryTest.java | 8 ++++---- .../management/ManagementWithJksWithAliasTest.java | 8 ++++---- .../ManagementWithP12AndTlsRegistryTest.java | 6 +++--- .../vertx/http/management/ManagementWithP12Test.java | 6 +++--- ...ManagementWithP12WithAliasAndTlsRegistryTest.java | 8 ++++---- .../management/ManagementWithP12WithAliasTest.java | 8 ++++---- .../ManagementWithPemAndNamedTlsRegistryTest.java | 6 +++--- .../ManagementWithPemAndTlsRegistryTest.java | 6 +++--- .../vertx/http/management/ManagementWithPemTest.java | 6 +++--- .../vertx/http/mtls/MtlsRequestBasicAuthTest.java | 6 +++--- .../MtlsRequestBasicAuthWithTlsRegistryTest.java | 6 +++--- .../io/quarkus/vertx/http/mtls/MtlsRequestTest.java | 6 +++--- .../http/mtls/MtlsRequestWithTlsRegistryTest.java | 6 +++--- .../io/quarkus/vertx/http/mtls/MtlsRequiredTest.java | 6 +++--- .../io/quarkus/vertx/http/mtls/MtlsWithP12Test.java | 6 +++--- .../http/mtls/MtlsWithP12WithTlsRegistryTest.java | 6 +++--- .../http/mtls/MtlsWithPemAndTlsRegistryTest.java | 6 +++--- .../io/quarkus/vertx/http/mtls/MtlsWithPemTest.java | 6 +++--- .../SslServerWithJKSWithSniMatchingSanDNSTest.java | 8 ++++---- .../quarkus/vertx/http/ssl/SslServerWithJksTest.java | 6 +++--- .../http/ssl/SslServerWithJksWithAliasTest.java | 8 ++++---- .../quarkus/vertx/http/ssl/SslServerWithP12Test.java | 6 +++--- .../http/ssl/SslServerWithP12WithAliasTest.java | 8 ++++---- .../SslServerWithP12WithSniMatchingSanDNSTest.java | 8 ++++---- .../quarkus/vertx/http/ssl/SslServerWithPemTest.java | 6 +++--- .../SslServerWithPemWithSniMatchingSanDNSTest.java | 8 ++++---- .../TlsServerWithJKSWithSniMatchingSanDNSTest.java | 8 ++++---- .../quarkus/vertx/http/tls/TlsServerWithJksTest.java | 6 +++--- .../http/tls/TlsServerWithJksWithAliasTest.java | 8 ++++---- .../tls/TlsServerWithMissingConfigurationTest.java | 6 +++--- .../vertx/http/tls/TlsServerWithNamedConfigTest.java | 6 +++--- .../quarkus/vertx/http/tls/TlsServerWithP12Test.java | 6 +++--- .../http/tls/TlsServerWithP12WithAliasTest.java | 8 ++++---- .../TlsServerWithP12WithSniMatchingSanDNSTest.java | 8 ++++---- .../quarkus/vertx/http/tls/TlsServerWithPemTest.java | 6 +++--- .../TlsServerWithPemWithSniMatchingSanDNSTest.java | 8 ++++---- extensions/websockets-next/deployment/pom.xml | 4 ++-- .../test/client/MtlsWithP12ClientEndpointTest.java | 6 +++--- .../next/test/client/TlsClientEndpointTest.java | 6 +++--- integration-tests/grpc-tls-p12/pom.xml | 4 ++-- .../examples/hello/HelloWorldTlsEndpointTest.java | 6 +++--- .../examples/hello/HelloWorldTlsServiceTest.java | 6 +++--- integration-tests/grpc-tls/pom.xml | 4 ++-- .../hello/HelloWorldTlsEndpointTestBase.java | 6 +++--- .../examples/hello/HelloWorldTlsServiceTestBase.java | 6 +++--- integration-tests/kafka-ssl/pom.xml | 4 ++-- .../io/quarkus/it/kafka/SslKafkaConsumerTest.java | 6 +++--- integration-tests/mailer/pom.xml | 4 ++-- .../it/mailer/MailpitFullTlsTestResource.java | 6 +++--- .../io/quarkus/it/mailer/MailpitTestResource.java | 6 +++--- integration-tests/mtls-certificates/pom.xml | 4 ++-- 216 files changed, 719 insertions(+), 699 deletions(-) diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 3f260ea64831e..d9706721a99be 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -153,6 +153,8 @@ 3.1.0 + 0.8.1 + 1.1.0 true @@ -367,9 +369,9 @@ - me.escoffier.certs - certificate-generator-junit5 - 0.5.0 + io.smallrye.certs + smallrye-certificate-generator-junit5 + ${smallrye-certificate-generator.version} test diff --git a/extensions/grpc/deployment/pom.xml b/extensions/grpc/deployment/pom.xml index c589a0130abf7..7bb95b5265906 100644 --- a/extensions/grpc/deployment/pom.xml +++ b/extensions/grpc/deployment/pom.xml @@ -102,8 +102,8 @@ test - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/HelloWorldTlsEndpointTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/HelloWorldTlsEndpointTest.java index ec0acc2e3e07b..6646e145c82c0 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/HelloWorldTlsEndpointTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/HelloWorldTlsEndpointTest.java @@ -10,9 +10,9 @@ import io.quarkus.grpc.client.tls.HelloWorldTlsEndpoint; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "grpc-client-tls", formats = Format.PEM)) class HelloWorldTlsEndpointTest { diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerTest.java index 0aa557be56e16..b4cd131337942 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerWithAliasTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerWithAliasTest.java index 5369a00581bef..492b2190a5e72 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerWithAliasTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerWithAliasTest.java @@ -13,10 +13,10 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc-alias", password = "password", formats = { Format.JKS, Format.PEM, diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerWithTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerWithTlsRegistryTest.java index 3c1f1fc1b6e29..8bc9b4e33d7dc 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerWithTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithJKSTrustStoreWithHttpServerWithTlsRegistryTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerTest.java index 458e93ccc47dd..58f87c9925b94 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerWithAliasTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerWithAliasTest.java index 08c06e944964a..edb0f5d641f9a 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerWithAliasTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerWithAliasTest.java @@ -13,10 +13,10 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc-alias", password = "password", formats = { Format.JKS, Format.PEM, diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerWithTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerWithTlsRegistryTest.java index cc0c2abb2ef35..dd5af61df1322 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerWithTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithP12TrustStoreWithHttpServerWithTlsRegistryTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithPemTrustStoreWithHttpServerTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithPemTrustStoreWithHttpServerTest.java index e8a42172f0fff..ae5068a3d41e6 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithPemTrustStoreWithHttpServerTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithPemTrustStoreWithHttpServerTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithPemTrustStoreWithHttpServerWithTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithPemTrustStoreWithHttpServerWithTlsRegistryTest.java index 640fabddcc50d..2c9e4b095fbd8 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithPemTrustStoreWithHttpServerWithTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/MtlsWithPemTrustStoreWithHttpServerWithTlsRegistryTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreAndTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreAndTlsRegistryTest.java index 438140cb83233..dedd44e9c1d14 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreAndTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreAndTlsRegistryTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreTest.java index 00adb5bbe3bc8..e876e4344c6e1 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreWithHttpServerAndTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreWithHttpServerAndTlsRegistryTest.java index 9d1c40f5ca2f0..a4877e9609802 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreWithHttpServerAndTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreWithHttpServerAndTlsRegistryTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreWithHttpServerTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreWithHttpServerTest.java index 851349cf67dc2..94e263afcb1d5 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreWithHttpServerTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithJKSTrustStoreWithHttpServerTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreAndTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreAndTlsRegistryTest.java index 24d4218cf5568..20ed86f6fa21c 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreAndTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreAndTlsRegistryTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreTest.java index c456376c9e938..fb16c85d4ec5e 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreWithHttpServerAndTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreWithHttpServerAndTlsRegistryTest.java index ddcc27846fcee..09916e4610b3e 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreWithHttpServerAndTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreWithHttpServerAndTlsRegistryTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreWithHttpServerTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreWithHttpServerTest.java index 380c04b729d26..c49c72020c7b9 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreWithHttpServerTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithP12TrustStoreWithHttpServerTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreTest.java index 64a6a2682daf1..04c671ef82e7e 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithHttpServerTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithHttpServerTest.java index ace79d838275d..ea6c79a37a181 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithHttpServerTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithHttpServerTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithHttpServerWithTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithHttpServerWithTlsRegistryTest.java index 0105cc9e513a7..1d32b8265b3b9 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithHttpServerWithTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithHttpServerWithTlsRegistryTest.java @@ -13,9 +13,9 @@ import io.grpc.examples.helloworld.HelloRequest; import io.quarkus.grpc.GrpcClient; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithTlsRegistryTest.java index 0aac854462fe3..4b8e6dd7c0002 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/client/tls/TlsWithPemTrustStoreWithTlsRegistryTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/MutinyGrpcServiceWithSSLTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/MutinyGrpcServiceWithSSLTest.java index 5b66f4da9fe3d..ab5a6a9d3c998 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/MutinyGrpcServiceWithSSLTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/MutinyGrpcServiceWithSSLTest.java @@ -32,9 +32,9 @@ import io.quarkus.grpc.server.services.MutinyHelloService; import io.quarkus.grpc.server.services.MutinyTestService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; /** * Test services exposed by the gRPC server implemented using the regular gRPC model. diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLFromClasspathTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLFromClasspathTest.java index 6509bcf88dd44..bd456db41ec2a 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLFromClasspathTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLFromClasspathTest.java @@ -30,9 +30,9 @@ import io.quarkus.grpc.server.services.HelloService; import io.quarkus.grpc.server.services.TestService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; /** * Test services exposed by the gRPC server implemented using the regular gRPC model. diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLTest.java index 3b22054f041b3..5037da52062a0 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/RegularGrpcServiceWithSSLTest.java @@ -30,9 +30,9 @@ import io.quarkus.grpc.server.services.HelloService; import io.quarkus.grpc.server.services.TestService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; /** * Test services exposed by the gRPC server implemented using the regular gRPC model. diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSAndTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSAndTlsRegistryTest.java index 67fc717e63d25..f1e565c416583 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSAndTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSAndTlsRegistryTest.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSTest.java index 36fed74b5500e..b2c57a9fd52e6 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSTest.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSWithAliasTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSWithAliasTest.java index 7f08f4a5e1a6c..a055d114de290 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSWithAliasTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingJKSWithAliasTest.java @@ -21,10 +21,10 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc-alias", password = "password", formats = { Format.JKS, Format.PEM, diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12AndTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12AndTlsRegistryTest.java index 3659a20016eab..121a6be8b8c32 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12AndTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12AndTlsRegistryTest.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12Test.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12Test.java index 57ee009e332be..d3a813b984a56 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12Test.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12Test.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12WithAliasTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12WithAliasTest.java index 4f390b305f67a..0c236df20a776 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12WithAliasTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingP12WithAliasTest.java @@ -21,10 +21,10 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc-alias", password = "password", formats = { Format.JKS, Format.PEM, diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingPemAndTlsRegistryTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingPemAndTlsRegistryTest.java index 9a019491fa743..12e46f2539122 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingPemAndTlsRegistryTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingPemAndTlsRegistryTest.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingPemTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingPemTest.java index 0b1b19cad66db..03412846a888f 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingPemTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithHttpServerUsingPemTest.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithJksKeyStoreAndAliasTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithJksKeyStoreAndAliasTest.java index 1bc3cce5a0728..3beaeae8c6c94 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithJksKeyStoreAndAliasTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithJksKeyStoreAndAliasTest.java @@ -21,10 +21,10 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc-alias", password = "password", formats = { Format.JKS, Format.PEM, diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithJksKeyStoreTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithJksKeyStoreTest.java index 0b7aa8de51aaa..6ad345df243b1 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithJksKeyStoreTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithJksKeyStoreTest.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithP12KeyStoreAndAliasTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithP12KeyStoreAndAliasTest.java index 5f9a800cf2513..b7b7c4c5268bc 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithP12KeyStoreAndAliasTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithP12KeyStoreAndAliasTest.java @@ -21,10 +21,10 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc-alias", password = "password", formats = { Format.JKS, Format.PEM, diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithP12KeyStoreTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithP12KeyStoreTest.java index 4a566a941c04a..515fa4a5064e9 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithP12KeyStoreTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithP12KeyStoreTest.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithPemKeyStoreTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithPemKeyStoreTest.java index dc19a4b22a146..735a0e139f220 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithPemKeyStoreTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/tls/TlsWithPemKeyStoreTest.java @@ -21,9 +21,9 @@ import io.netty.handler.ssl.SslContext; import io.quarkus.grpc.server.services.HelloService; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "grpc", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }, client = true) diff --git a/extensions/mailer/runtime/pom.xml b/extensions/mailer/runtime/pom.xml index 59ecb9b368ccc..fd2304e6a1034 100644 --- a/extensions/mailer/runtime/pom.xml +++ b/extensions/mailer/runtime/pom.xml @@ -70,8 +70,8 @@ test - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/FakeSmtpTestBase.java b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/FakeSmtpTestBase.java index 8a36bcfeee3cb..dc661c79e813a 100644 --- a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/FakeSmtpTestBase.java +++ b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/FakeSmtpTestBase.java @@ -14,10 +14,10 @@ import io.quarkus.tls.BaseTlsConfiguration; import io.quarkus.tls.TlsConfiguration; import io.quarkus.tls.TlsConfigurationRegistry; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.mutiny.core.Vertx; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "mailer-certs", formats = Format.PKCS12, password = "password") }) diff --git a/extensions/resteasy-reactive/rest-client/deployment/pom.xml b/extensions/resteasy-reactive/rest-client/deployment/pom.xml index 3751bd26d6a59..c64bbb0167b0a 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/pom.xml +++ b/extensions/resteasy-reactive/rest-client/deployment/pom.xml @@ -103,8 +103,8 @@ test - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/MtlsConfigFromRegistryCdiTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/MtlsConfigFromRegistryCdiTest.java index 1d48304c8e113..6a1367c546efd 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/MtlsConfigFromRegistryCdiTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/MtlsConfigFromRegistryCdiTest.java @@ -15,10 +15,10 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromPropertiesCdiTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromPropertiesCdiTest.java index 8526ad416012b..fc32c69ba8ac0 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromPropertiesCdiTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromPropertiesCdiTest.java @@ -15,10 +15,10 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "tls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromRegistryCdiTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromRegistryCdiTest.java index 09239c2e628f7..d261e27cb5958 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromRegistryCdiTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromRegistryCdiTest.java @@ -15,10 +15,10 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "tls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromRegistryManualTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromRegistryManualTest.java index 8f246f30ef55f..e45f16d545bfe 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromRegistryManualTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/tls/TlsConfigFromRegistryManualTest.java @@ -19,10 +19,10 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.tls.TlsConfiguration; import io.quarkus.tls.TlsConfigurationRegistry; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "tls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/tls-registry/deployment/pom.xml b/extensions/tls-registry/deployment/pom.xml index ec95c9987b432..35cb957fad7bb 100644 --- a/extensions/tls-registry/deployment/pom.xml +++ b/extensions/tls-registry/deployment/pom.xml @@ -64,8 +64,8 @@ - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/BuildTimeRegistrationTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/BuildTimeRegistrationTest.java index ab3ea7551b353..d76b241d48bb2 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/BuildTimeRegistrationTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/BuildTimeRegistrationTest.java @@ -18,9 +18,9 @@ import io.quarkus.builder.BuildContext; import io.quarkus.builder.BuildStep; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-registration", password = "password", formats = Format.PKCS12) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreAndTrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreAndTrustStoreTest.java index 57fcb7c37af2c..81fdf237855e4 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreAndTrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreAndTrustStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreTest.java index 8e9bf3d7d0496..8d73e1daac2e6 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreWithAliasTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreWithAliasTest.java index 8b892b6562520..89755e4664a04 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreWithAliasTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSKeyStoreWithAliasTest.java @@ -15,14 +15,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-jks", password = "password", formats = { Format.JKS }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2", password = "alias-password-2") + @Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2", password = "alias-password-2") }) }) public class DefaultJKSKeyStoreWithAliasTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreTest.java index 5f85887477850..9fe328fd547ef 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithAliasTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithAliasTest.java index 9bdbade9012b1..3405a7d4e46c2 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithAliasTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithAliasTest.java @@ -15,14 +15,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-jks", password = "password", formats = { Format.JKS }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2", password = "alias-password-2") + @Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2", password = "alias-password-2") }) }) public class DefaultJKSTrustStoreWithAliasTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderTest.java index 7287a47289cb2..eb5c6a7c3ce63 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderWithAliasTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderWithAliasTest.java index 384f3df37f556..61792fe369c09 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderWithAliasTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderWithAliasTest.java @@ -18,10 +18,10 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider-alias", password = "secret123!", formats = { Format.JKS, diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderWithCustomKeyTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderWithCustomKeyTest.java index b0aa978f07f21..c9637549240dc 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderWithCustomKeyTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultJKSTrustStoreWithCredentialsProviderWithCustomKeyTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12KeyStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12KeyStoreTest.java index bd86fb16266f4..cee5d80872a78 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12KeyStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12KeyStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12KeyStoreWithAliasTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12KeyStoreWithAliasTest.java index c3cbde4d52092..5aa7157c1fb74 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12KeyStoreWithAliasTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12KeyStoreWithAliasTest.java @@ -15,14 +15,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-p12", password = "password", formats = { Format.PKCS12 }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2", password = "alias-password-2", subjectAlternativeNames = "dns:example.com") }) + @Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2", password = "alias-password-2", subjectAlternativeNames = "dns:example.com") }) }) public class DefaultP12KeyStoreWithAliasTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreTest.java index 9fc656d02ca10..fafb3dcc3d5f2 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithAliasTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithAliasTest.java index e7ae51950d581..c74b699274707 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithAliasTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithAliasTest.java @@ -15,14 +15,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-p12", password = "password", formats = { Format.PKCS12 }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2", password = "alias-password-2", subjectAlternativeNames = "dns:example.com") }) + @Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2", password = "alias-password-2", subjectAlternativeNames = "dns:example.com") }) }) public class DefaultP12TrustStoreWithAliasTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithCredentialsProviderTest.java index e1c9aedc2a41e..e84bda9f042d9 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithCredentialsProviderWithAliasTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithCredentialsProviderWithAliasTest.java index adc5bfe49b0ed..2467a1377f18a 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithCredentialsProviderWithAliasTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultP12TrustStoreWithCredentialsProviderWithAliasTest.java @@ -18,10 +18,10 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider-alias", password = "secret123!", formats = { Format.JKS, diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemKeyStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemKeyStoreTest.java index 894eb81de785f..2ffea01276a90 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemKeyStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemKeyStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemKeyStoreWithMultipleAliasesTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemKeyStoreWithMultipleAliasesTest.java index 12b1347bb350f..749435b350076 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemKeyStoreWithMultipleAliasesTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemKeyStoreWithMultipleAliasesTest.java @@ -18,14 +18,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-pem", formats = { Format.PEM }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2") + @Alias(name = "alias1", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2") }) }) public class DefaultPemKeyStoreWithMultipleAliasesTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemTrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemTrustStoreTest.java index 5377660710ea4..848c6b4324ed0 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemTrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemTrustStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemTrustStoreWithMultipleAliasesTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemTrustStoreWithMultipleAliasesTest.java index 51a40c6dc06f1..7fc80289f77cb 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemTrustStoreWithMultipleAliasesTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultPemTrustStoreWithMultipleAliasesTest.java @@ -18,14 +18,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-pem", formats = { Format.PEM }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2") + @Alias(name = "alias1", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2") }) }) public class DefaultPemTrustStoreWithMultipleAliasesTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultSSLOptionsTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultSSLOptionsTest.java index 97f383b07816f..2a610b2c17b7e 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultSSLOptionsTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/DefaultSSLOptionsTest.java @@ -13,13 +13,13 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; -@me.escoffier.certs.junit5.Certificates(baseDir = "target/certs", certificates = { - @me.escoffier.certs.junit5.Certificate(name = "test-ssl-options", password = "password", formats = { Format.PKCS12 }) -} - -) +@Certificates(baseDir = "target/certs", certificates = { + @Certificate(name = "test-ssl-options", password = "password", formats = { Format.PKCS12 }) +}) public class DefaultSSLOptionsTest { private static final String configuration = """ diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreCredentialsProviderTest.java index 60f5ab3cc76e0..0d6b372fe5bcd 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreCredentialsProviderWithCustomKeysTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreCredentialsProviderWithCustomKeysTest.java index 462b5917405c3..ac2d46a974ca2 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreCredentialsProviderWithCustomKeysTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreCredentialsProviderWithCustomKeysTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreFromClassPathTest.java index 2c3ee3c7a8269..ed243b7418b6f 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreFromClassPathTest.java @@ -16,9 +16,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithAliasCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithAliasCredentialsProviderTest.java index 8dbd4f1da61ce..5764a636f66e3 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithAliasCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithAliasCredentialsProviderTest.java @@ -18,10 +18,10 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider-alias", password = "secret123!", formats = { Format.JKS, diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithMissingAliasPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithMissingAliasPasswordTest.java index 9129fde0bbb14..f3f946d2c6c18 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithMissingAliasPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithMissingAliasPasswordTest.java @@ -13,14 +13,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-jks", password = "password", formats = { Format.JKS }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2", password = "alias-password-2") + @Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2", password = "alias-password-2") }) }) public class JKSKeyStoreWithMissingAliasPasswordTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithOverriddenCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithOverriddenCredentialsProviderTest.java index 3b5096a855795..63b3cb3bcfba9 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithOverriddenCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithOverriddenCredentialsProviderTest.java @@ -19,9 +19,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithSniTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithSniTest.java index dece61a291786..85503fed22f20 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithSniTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithSniTest.java @@ -1,6 +1,6 @@ package io.quarkus.tls; -import static me.escoffier.certs.Format.JKS; +import static io.smallrye.certs.Format.JKS; import static org.assertj.core.api.Assertions.assertThat; import java.security.KeyStoreException; @@ -14,11 +14,12 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { - @me.escoffier.certs.junit5.Certificate(name = "test-sni-jks", password = "sni", formats = { JKS }, aliases = { + @Certificate(name = "test-sni-jks", password = "sni", formats = { JKS }, aliases = { @Alias(name = "sni-1", password = "sni", cn = "acme.org"), @Alias(name = "sni-2", password = "sni", cn = "example.com"), }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithWrongAliasPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithWrongAliasPasswordTest.java index c1fa0faac4626..9e4938421cf5c 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithWrongAliasPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithWrongAliasPasswordTest.java @@ -13,14 +13,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-jks", password = "password", formats = { Format.JKS }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2", password = "alias-password-2") + @Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2", password = "alias-password-2") }) }) public class JKSKeyStoreWithWrongAliasPasswordTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithWrongPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithWrongPasswordTest.java index 81906566e5a6f..e6e264d4bafd3 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithWrongPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithWrongPasswordTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithoutPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithoutPasswordTest.java index 60031ed20a01f..184bae7be79e0 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithoutPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSKeyStoreWithoutPasswordTest.java @@ -17,9 +17,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreFromClassPathTest.java index 77167132d38cf..95a7c1673a044 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreFromClassPathTest.java @@ -16,9 +16,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithOverriddenCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithOverriddenCredentialsProviderTest.java index 9f03fde8c9899..12d3c64b49b8f 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithOverriddenCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithOverriddenCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithWrongPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithWrongPasswordTest.java index ad67132e0ba0c..aec528f7c73ea 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithWrongPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithWrongPasswordTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithoutPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithoutPasswordTest.java index 4ec81c20c884a..246369159f20f 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithoutPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/JKSTrustStoreWithoutPasswordTest.java @@ -17,9 +17,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithMissingKeyCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithMissingKeyCredentialsProviderTest.java index 42b2e802b6afb..529918e948aa1 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithMissingKeyCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithMissingKeyCredentialsProviderTest.java @@ -16,9 +16,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSelectedCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSelectedCredentialsProviderTest.java index e881708e71568..fb32372c50dde 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSelectedCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSelectedCredentialsProviderTest.java @@ -19,9 +19,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSniAndAliasSetTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSniAndAliasSetTest.java index f203ccca8afbc..d934d1749a70f 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSniAndAliasSetTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSniAndAliasSetTest.java @@ -1,6 +1,6 @@ package io.quarkus.tls; -import static me.escoffier.certs.Format.PKCS12; +import static io.smallrye.certs.Format.PKCS12; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -13,11 +13,12 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { - @me.escoffier.certs.junit5.Certificate(name = "test-sni-p12", password = "sni", formats = { PKCS12 }, aliases = { + @Certificate(name = "test-sni-p12", password = "sni", formats = { PKCS12 }, aliases = { @Alias(name = "sni-1", password = "sni", cn = "acme.org"), @Alias(name = "sni-2", password = "sni", cn = "example.com"), }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSniAndSingleAliasSetTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSniAndSingleAliasSetTest.java index cd1bd0b7a1b9a..42884f00d672a 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSniAndSingleAliasSetTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/KeyStoreWithSniAndSingleAliasSetTest.java @@ -1,6 +1,6 @@ package io.quarkus.tls; -import static me.escoffier.certs.Format.PKCS12; +import static io.smallrye.certs.Format.PKCS12; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -13,10 +13,11 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { - @me.escoffier.certs.junit5.Certificate(name = "test-sni-single", password = "sni", formats = { PKCS12 }) + @Certificate(name = "test-sni-single", password = "sni", formats = { PKCS12 }) }) public class KeyStoreWithSniAndSingleAliasSetTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSKeyStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSKeyStoreFromClassPathTest.java index 88f368f4142ff..9b4fe70f7d65a 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSKeyStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSKeyStoreFromClassPathTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSKeyStoreFromFileSystemTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSKeyStoreFromFileSystemTest.java index 8107cc815cb00..fe787ea1a95f2 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSKeyStoreFromFileSystemTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSKeyStoreFromFileSystemTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSTrustStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSTrustStoreFromClassPathTest.java index 4ce0208cf53f2..1d1abf5accbcb 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSTrustStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingJKSTrustStoreFromClassPathTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12KeyStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12KeyStoreFromClassPathTest.java index 33c4cdcd7fed8..93048debb740c 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12KeyStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12KeyStoreFromClassPathTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12KeyStoreFromFileSystemTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12KeyStoreFromFileSystemTest.java index 732418519fa0c..0d0310cc659cd 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12KeyStoreFromFileSystemTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12KeyStoreFromFileSystemTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12TrustStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12TrustStoreFromClassPathTest.java index 2b865247d0426..a632a6b688de4 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12TrustStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12TrustStoreFromClassPathTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12TrustStoreFromFileSystemTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12TrustStoreFromFileSystemTest.java index 25b342eaf95dd..0f5446c4cb6b7 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12TrustStoreFromFileSystemTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingP12TrustStoreFromFileSystemTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingPemCertTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingPemCertTest.java index 9bb369d3cd775..bea8695e83745 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingPemCertTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingPemCertTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingPemKeyTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingPemKeyTest.java index 900f7bc16a47f..da76ef551088e 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingPemKeyTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingPemKeyTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingTrustStorePemCertsTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingTrustStorePemCertsTest.java index f5f04f822024a..e9dc4b00da23a 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingTrustStorePemCertsTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/MissingTrustStorePemCertsTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreCredentialsProviderTest.java index b9b96067dd3ad..758b5124395d9 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreTest.java index e019653b82eee..3dd7704ab08ca 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreWithAliasCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreWithAliasCredentialsProviderTest.java index c0d98ffb3a95f..d38b1d855a52c 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreWithAliasCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSKeyStoreWithAliasCredentialsProviderTest.java @@ -18,10 +18,10 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider-alias", password = "secret123!", formats = { Format.JKS, diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreTest.java index 6ae618fa3dbab..acbe36f58449a 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreWithCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreWithCredentialsProviderTest.java index 5bbcc59f7b29d..d6007e0c59817 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreWithCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreWithCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreWithCredentialsProviderWithAliasTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreWithCredentialsProviderWithAliasTest.java index eb163ab5a709d..678d042151921 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreWithCredentialsProviderWithAliasTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedJKSTrustStoreWithCredentialsProviderWithAliasTest.java @@ -18,10 +18,10 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider-alias", password = "secret123!", formats = { Format.JKS, diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreCredentialsProviderTest.java index 2d97edace8c08..9c2c20df86198 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreTest.java index f3803cdb5e9cd..187e1bd3edea5 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreWithAliasCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreWithAliasCredentialsProviderTest.java index 1bcbdf0828407..efefb5f5d6eaf 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreWithAliasCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12KeyStoreWithAliasCredentialsProviderTest.java @@ -18,10 +18,10 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider-alias", password = "secret123!", formats = { Format.JKS, diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreTest.java index 62b47544eaef3..00405e9acf32b 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreWithCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreWithCredentialsProviderTest.java index fa956cee360f9..0217defc0458d 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreWithCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreWithCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreWithCredentialsProviderWithAliasTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreWithCredentialsProviderWithAliasTest.java index ae599dd4b3d5d..1a95c893c6230 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreWithCredentialsProviderWithAliasTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedP12TrustStoreWithCredentialsProviderWithAliasTest.java @@ -18,10 +18,10 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider-alias", password = "secret123!", formats = { Format.JKS, diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedPemKeyStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedPemKeyStoreTest.java index f058b77f0458b..b259eee1211f6 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedPemKeyStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedPemKeyStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedPemTrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedPemTrustStoreTest.java index 1a27878d166ce..55d0f21893648 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedPemTrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedPemTrustStoreTest.java @@ -15,9 +15,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedSSLOptionsTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedSSLOptionsTest.java index 7509c676f2bb7..ce0425ad24fb1 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedSSLOptionsTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/NamedSSLOptionsTest.java @@ -13,14 +13,14 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.net.SSLOptions; -import me.escoffier.certs.Format; -@me.escoffier.certs.junit5.Certificates(baseDir = "target/certs", certificates = { - @me.escoffier.certs.junit5.Certificate(name = "test-ssl-options", password = "password", formats = { Format.PKCS12 }) -} - -) +@Certificates(baseDir = "target/certs", certificates = { + @Certificate(name = "test-ssl-options", password = "password", formats = { Format.PKCS12 }) +}) public class NamedSSLOptionsTest { private static final String configuration = """ diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreCredentialsProviderTest.java index 47758fc6c90a1..540aaf5a48c4b 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreFromClassPathTest.java index 89b73a3a3f76f..387a2abbad4e7 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreFromClassPathTest.java @@ -16,9 +16,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithAliasCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithAliasCredentialsProviderTest.java index e926d68252e40..437442d00f621 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithAliasCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithAliasCredentialsProviderTest.java @@ -18,10 +18,10 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider-alias", password = "secret123!", formats = { Format.JKS, diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithMissingAliasPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithMissingAliasPasswordTest.java index d21e9a74c9a81..49e35f7cc575c 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithMissingAliasPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithMissingAliasPasswordTest.java @@ -13,14 +13,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-p12", password = "password", formats = { Format.PKCS12 }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2", password = "alias-password-2", subjectAlternativeNames = "dns:example.com") }) + @Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2", password = "alias-password-2", subjectAlternativeNames = "dns:example.com") }) }) public class P12KeyStoreWithMissingAliasPasswordTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithOverriddenCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithOverriddenCredentialsProviderTest.java index a53ebbfec6a4d..41416db6f8e0c 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithOverriddenCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithOverriddenCredentialsProviderTest.java @@ -19,9 +19,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithSniTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithSniTest.java index 8ffe16ed046f6..2ac46783535c9 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithSniTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithSniTest.java @@ -1,6 +1,6 @@ package io.quarkus.tls; -import static me.escoffier.certs.Format.PKCS12; +import static io.smallrye.certs.Format.PKCS12; import static org.assertj.core.api.Assertions.assertThat; import java.security.KeyStoreException; @@ -14,11 +14,12 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { - @me.escoffier.certs.junit5.Certificate(name = "test-sni-p12", password = "sni", formats = { PKCS12 }, aliases = { + @Certificate(name = "test-sni-p12", password = "sni", formats = { PKCS12 }, aliases = { @Alias(name = "sni-1", password = "sni", cn = "acme.org"), @Alias(name = "sni-2", password = "sni", cn = "example.com"), }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithWrongAliasPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithWrongAliasPasswordTest.java index c5e5a3bac363f..8785d7ae51487 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithWrongAliasPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithWrongAliasPasswordTest.java @@ -13,14 +13,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-alias-p12", password = "password", formats = { Format.PKCS12 }, aliases = { - @me.escoffier.certs.junit5.Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "alias2", password = "alias-password-2", subjectAlternativeNames = "dns:example.com") }) + @Alias(name = "alias1", password = "alias-password", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "alias2", password = "alias-password-2", subjectAlternativeNames = "dns:example.com") }) }) public class P12KeyStoreWithWrongAliasPasswordTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithWrongPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithWrongPasswordTest.java index 0ba5959389c8d..e6353951345be 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithWrongPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithWrongPasswordTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithoutPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithoutPasswordTest.java index 3ae4dbaf7389c..09377f389bd63 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithoutPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12KeyStoreWithoutPasswordTest.java @@ -17,9 +17,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreFromClassPathTest.java index 2d5d00f0f6b37..4e21daa002a6e 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreFromClassPathTest.java @@ -16,9 +16,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithOverriddenCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithOverriddenCredentialsProviderTest.java index c9f06c3c9c264..15fd7fbba1c07 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithOverriddenCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithOverriddenCredentialsProviderTest.java @@ -18,9 +18,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithWrongPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithWrongPasswordTest.java index 2aa9dac9fd506..b323c0d50524e 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithWrongPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithWrongPasswordTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithoutPasswordTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithoutPasswordTest.java index e27ad4d653ead..6b34e296190c1 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithoutPasswordTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/P12TrustStoreWithoutPasswordTest.java @@ -17,9 +17,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemCertOrderWithNotEnoughValueTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemCertOrderWithNotEnoughValueTest.java index 6125673934a73..d7a9b41f1e355 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemCertOrderWithNotEnoughValueTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemCertOrderWithNotEnoughValueTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemCertOrderWithTooManyValueTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemCertOrderWithTooManyValueTest.java index 5c7e3cd18ca89..29ce526c75d1c 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemCertOrderWithTooManyValueTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemCertOrderWithTooManyValueTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreFromClassPathTest.java index bdf223319dfc2..35beaf5c6a5c8 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreFromClassPathTest.java @@ -14,9 +14,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreNaturalOrderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreNaturalOrderTest.java index d0cbd3c4124c1..5db3b348fab76 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreNaturalOrderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreNaturalOrderTest.java @@ -18,14 +18,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-pem-order", formats = { Format.PEM }, subjectAlternativeNames = "dns:quarkus.io", aliases = { - @me.escoffier.certs.junit5.Alias(name = "test-pem-order-alias1", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "test-pem-order-alias2", subjectAlternativeNames = "dns:example.com"), + @Alias(name = "test-pem-order-alias1", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "test-pem-order-alias2", subjectAlternativeNames = "dns:example.com"), }) }) public class PemKeyStoreNaturalOrderTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreUserOrderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreUserOrderTest.java index 59cb87a7d4dfc..85a83df7cef8e 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreUserOrderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreUserOrderTest.java @@ -18,14 +18,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-pem-order", formats = { Format.PEM }, subjectAlternativeNames = "dns:quarkus.io", aliases = { - @me.escoffier.certs.junit5.Alias(name = "test-pem-order-alias1", subjectAlternativeNames = "dns:acme.org"), - @me.escoffier.certs.junit5.Alias(name = "test-pem-order-alias2", subjectAlternativeNames = "dns:example.com"), + @Alias(name = "test-pem-order-alias1", subjectAlternativeNames = "dns:acme.org"), + @Alias(name = "test-pem-order-alias2", subjectAlternativeNames = "dns:example.com"), }) }) public class PemKeyStoreUserOrderTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreWithSniTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreWithSniTest.java index 297e3c1181883..804f3b65e28f2 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreWithSniTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemKeyStoreWithSniTest.java @@ -1,6 +1,6 @@ package io.quarkus.tls; -import static me.escoffier.certs.Format.PEM; +import static io.smallrye.certs.Format.PEM; import static org.assertj.core.api.Assertions.assertThat; import jakarta.inject.Inject; @@ -12,11 +12,12 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { - @me.escoffier.certs.junit5.Certificate(name = "test-sni-pem", formats = { PEM }, aliases = { + @Certificate(name = "test-sni-pem", formats = { PEM }, aliases = { @Alias(name = "sni-1", cn = "acme.org"), @Alias(name = "sni-2", cn = "example.com"), }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemTrustStoreFromClassPathTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemTrustStoreFromClassPathTest.java index 754a21b99c51f..8e58970c09e19 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemTrustStoreFromClassPathTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemTrustStoreFromClassPathTest.java @@ -16,9 +16,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemWithoutKeyTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemWithoutKeyTest.java index b0268b301f126..3d4c7eaaf1ed2 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemWithoutKeyTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/PemWithoutKeyTest.java @@ -13,9 +13,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/ReloadKeyStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/ReloadKeyStoreTest.java index 1f94b369e5a07..b87c3ce7f1c7f 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/ReloadKeyStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/ReloadKeyStoreTest.java @@ -20,9 +20,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-reload-A", password = "password", formats = Format.PKCS12, subjectAlternativeNames = "dns:localhost"), diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/ReloadTrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/ReloadTrustStoreTest.java index 7660ca46e4418..247a1f076a916 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/ReloadTrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/ReloadTrustStoreTest.java @@ -20,9 +20,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-reload-A", password = "password", formats = Format.PKCS12, subjectAlternativeNames = "dns:localhost"), diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/RuntimeRegistrationTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/RuntimeRegistrationTest.java index c4b5bd162f4b8..bb61432979575 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/RuntimeRegistrationTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/RuntimeRegistrationTest.java @@ -18,9 +18,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-registration", password = "password", formats = Format.PKCS12) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredJKSAndP12Test.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredJKSAndP12Test.java index 1398b582fa82d..16b21209255df 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredJKSAndP12Test.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredJKSAndP12Test.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredPemAndJKSTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredPemAndJKSTest.java index ec106278c9f26..e22c01e5ee697 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredPemAndJKSTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredPemAndJKSTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredPemAndP12Test.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredPemAndP12Test.java index ad51849127bfc..6581878478fb3 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredPemAndP12Test.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyKeyStoreConfiguredPemAndP12Test.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredJKSAndP12Test.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredJKSAndP12Test.java index 8c67987910c9b..cae9ce89bf55b 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredJKSAndP12Test.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredJKSAndP12Test.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredPemAndJKSTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredPemAndJKSTest.java index a85db2cd51722..f94cb42b50a2a 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredPemAndJKSTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredPemAndJKSTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredPemAndP12Test.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredPemAndP12Test.java index 20f2432b37f2c..3543fb6b4a7ae 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredPemAndP12Test.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TooManyTrustStoreConfiguredPemAndP12Test.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustAllWithTrustStoreTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustAllWithTrustStoreTest.java index 416e344b09a1e..b676c86560e69 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustAllWithTrustStoreTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustAllWithTrustStoreTest.java @@ -10,13 +10,14 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; /** * Verify that is trust all is set, trust store is not set. */ -@me.escoffier.certs.junit5.Certificates(baseDir = "target/certs", certificates = { +@Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-formats", password = "password", formats = { Format.JKS, Format.PEM, Format.PKCS12 }) }) public class TrustAllWithTrustStoreTest { diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingCredentialsProviderTest.java index 5b6dfe60293b7..e538f240753bd 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingCredentialsProviderTest.java @@ -10,9 +10,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingKeyCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingKeyCredentialsProviderTest.java index b4320e4732a1f..466debf478410 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingKeyCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingKeyCredentialsProviderTest.java @@ -16,9 +16,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingSelectedCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingSelectedCredentialsProviderTest.java index 8974a5a0b6e1c..d1c8ba7496642 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingSelectedCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithMissingSelectedCredentialsProviderTest.java @@ -16,9 +16,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithSelectedCredentialsProviderTest.java b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithSelectedCredentialsProviderTest.java index 8f9d7b04c5e0d..64090a23b1ee2 100644 --- a/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithSelectedCredentialsProviderTest.java +++ b/extensions/tls-registry/deployment/src/test/java/io/quarkus/tls/TrustStoreWithSelectedCredentialsProviderTest.java @@ -19,9 +19,9 @@ import io.quarkus.credentials.CredentialsProvider; import io.quarkus.test.QuarkusUnitTest; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "test-credentials-provider", password = "secret123!", formats = { Format.JKS, Format.PKCS12 }) diff --git a/extensions/vertx-http/deployment/pom.xml b/extensions/vertx-http/deployment/pom.xml index e34a75ce58be4..8d4ce7733a096 100644 --- a/extensions/vertx-http/deployment/pom.xml +++ b/extensions/vertx-http/deployment/pom.xml @@ -125,8 +125,8 @@ - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortTest.java index a8b5de8403c04..6fefb020b411c 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortTest.java @@ -19,10 +19,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortWithTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortWithTlsRegistryTest.java index 09eb2c4e5ecba..8c922107a6f63 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortWithTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/DisableHttpPortWithTlsRegistryTest.java @@ -19,10 +19,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadTest.java index 7dc36e5d33dbd..6a594f79ab916 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadTest.java @@ -27,6 +27,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.options.TlsCertificateReloader; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientOptions; @@ -35,9 +38,6 @@ import io.vertx.core.http.HttpMethod; import io.vertx.core.net.PemTrustOptions; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certificates", certificates = { @Certificate(name = "reload-A", formats = Format.PEM), diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadWithTlsRegistryAndUpdateEventTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadWithTlsRegistryAndUpdateEventTest.java index 688b0855c1e06..63a00ff7633ff 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadWithTlsRegistryAndUpdateEventTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadWithTlsRegistryAndUpdateEventTest.java @@ -28,6 +28,9 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.tls.CertificateUpdatedEvent; import io.quarkus.tls.TlsConfigurationRegistry; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientOptions; @@ -36,9 +39,6 @@ import io.vertx.core.http.HttpMethod; import io.vertx.core.net.PemTrustOptions; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certificates", certificates = { @Certificate(name = "reload-A", formats = Format.PEM), diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadWithTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadWithTlsRegistryTest.java index 00b637806cba9..7ee13f50b6740 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadWithTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadWithTlsRegistryTest.java @@ -27,6 +27,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.options.TlsCertificateReloader; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientOptions; @@ -35,9 +38,6 @@ import io.vertx.core.http.HttpMethod; import io.vertx.core.net.PemTrustOptions; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certificates", certificates = { @Certificate(name = "reload-A", formats = Format.PEM), diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java index 7f8494bb679cd..72e09ec1bd56e 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java @@ -27,6 +27,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.options.TlsCertificateReloader; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientOptions; @@ -35,9 +38,6 @@ import io.vertx.core.http.HttpMethod; import io.vertx.core.net.PfxOptions; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certificates", certificates = { @Certificate(name = "reload-A", formats = Format.PKCS12, password = "password"), diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadWithTlsRegistryAndUpdateEventTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadWithTlsRegistryAndUpdateEventTest.java index 8c178851a02cc..989bf7867ec0f 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadWithTlsRegistryAndUpdateEventTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadWithTlsRegistryAndUpdateEventTest.java @@ -28,6 +28,9 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.tls.CertificateUpdatedEvent; import io.quarkus.tls.TlsConfigurationRegistry; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientOptions; @@ -36,9 +39,6 @@ import io.vertx.core.http.HttpMethod; import io.vertx.core.net.PfxOptions; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certificates", certificates = { @Certificate(name = "reload-A", formats = Format.PKCS12, password = "password"), diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadWithTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadWithTlsRegistryTest.java index 751ac9239f4c1..be735274a21f4 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadWithTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadWithTlsRegistryTest.java @@ -27,6 +27,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.options.TlsCertificateReloader; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientOptions; @@ -35,9 +38,6 @@ import io.vertx.core.http.HttpMethod; import io.vertx.core.net.PfxOptions; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certificates", certificates = { @Certificate(name = "reload-A", formats = Format.PKCS12, password = "password"), diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/ManagementHttpServerTlsCertificateReloadTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/ManagementHttpServerTlsCertificateReloadTest.java index f4e6a47094f20..abd026c05c05b 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/ManagementHttpServerTlsCertificateReloadTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/ManagementHttpServerTlsCertificateReloadTest.java @@ -31,6 +31,9 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.runtime.options.TlsCertificateReloader; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; @@ -40,9 +43,6 @@ import io.vertx.core.http.HttpMethod; import io.vertx.core.net.PemTrustOptions; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certificates", certificates = { @Certificate(name = "reload-C", formats = Format.PEM), diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/ManagementHttpServerTlsCertificateReloadWithTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/ManagementHttpServerTlsCertificateReloadWithTlsRegistryTest.java index 6864fcfa75aa1..9c4c16206c6d1 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/ManagementHttpServerTlsCertificateReloadWithTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/ManagementHttpServerTlsCertificateReloadWithTlsRegistryTest.java @@ -32,6 +32,9 @@ import io.quarkus.tls.TlsConfigurationRegistry; import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; @@ -41,9 +44,6 @@ import io.vertx.core.http.HttpMethod; import io.vertx.core.net.PemTrustOptions; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certificates", certificates = { @Certificate(name = "reload-C", formats = Format.PEM), diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java index b141965526b81..a44d1329999fd 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionConfigTest.java @@ -22,6 +22,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.core.runtime.VertxCoreRecorder; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpClientRequest; @@ -29,9 +32,6 @@ import io.vertx.core.net.JdkSSLEngineOptions; import io.vertx.core.net.JksOptions; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; /** * Configuration of the RST flood protection (CVE-2023-44487) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java index 3e676ca2abfa0..31020ed457241 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java @@ -22,6 +22,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.core.runtime.VertxCoreRecorder; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpClientRequest; @@ -29,9 +32,6 @@ import io.vertx.core.net.JdkSSLEngineOptions; import io.vertx.core.net.JksOptions; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; /** * Reproduce CVE-2023-44487. diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2Test.java index 4cf2f94e496ea..bd729000cdaed 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2Test.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2Test.java @@ -16,6 +16,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.core.runtime.VertxCoreRecorder; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; @@ -26,9 +29,6 @@ import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2WithNamedConfigTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2WithNamedConfigTlsRegistryTest.java index 108e1932b10d9..cab362542d56b 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2WithNamedConfigTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2WithNamedConfigTlsRegistryTest.java @@ -16,6 +16,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.core.runtime.VertxCoreRecorder; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; @@ -26,9 +29,6 @@ import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2WithTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2WithTlsRegistryTest.java index 2fede3a217b46..54eafbeb13ef8 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2WithTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2WithTlsRegistryTest.java @@ -16,6 +16,9 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.core.runtime.VertxCoreRecorder; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; @@ -26,9 +29,6 @@ import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementAndPrimaryUsingDifferentTlsConfigurationTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementAndPrimaryUsingDifferentTlsConfigurationTest.java index 0954c02330ab5..b3f64d0cec1ae 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementAndPrimaryUsingDifferentTlsConfigurationTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementAndPrimaryUsingDifferentTlsConfigurationTest.java @@ -22,12 +22,12 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = { @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementAndPrimaryUsingSameTlsConfigurationTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementAndPrimaryUsingSameTlsConfigurationTest.java index e39ac3aea91a9..d5766e7121c16 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementAndPrimaryUsingSameTlsConfigurationTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementAndPrimaryUsingSameTlsConfigurationTest.java @@ -22,12 +22,12 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksAndTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksAndTlsRegistryTest.java index 2135a179679eb..95b467c140fdd 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksAndTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksAndTlsRegistryTest.java @@ -19,11 +19,11 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksTest.java index 64d1c9d094aae..26b33d6a6e4bc 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksTest.java @@ -19,11 +19,11 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksWithAliasAndTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksWithAliasAndTlsRegistryTest.java index 2aef34fedce9c..9711c9848eb71 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksWithAliasAndTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksWithAliasAndTlsRegistryTest.java @@ -19,12 +19,12 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-alias-test", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksWithAliasTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksWithAliasTest.java index 2719de1464bc4..2ca8e95be2111 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksWithAliasTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithJksWithAliasTest.java @@ -19,12 +19,12 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-alias-test", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12AndTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12AndTlsRegistryTest.java index fbbdfd6604643..c68ffc77a2765 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12AndTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12AndTlsRegistryTest.java @@ -19,11 +19,11 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12Test.java index 2dfe139a6948a..99e6c164cc33b 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12Test.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12Test.java @@ -19,11 +19,11 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12WithAliasAndTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12WithAliasAndTlsRegistryTest.java index 3f100f086b082..38da259ea1782 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12WithAliasAndTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12WithAliasAndTlsRegistryTest.java @@ -19,12 +19,12 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-alias-test", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12WithAliasTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12WithAliasTest.java index 729215a497d42..2daee6853b8ca 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12WithAliasTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithP12WithAliasTest.java @@ -19,12 +19,12 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-alias-test", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemAndNamedTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemAndNamedTlsRegistryTest.java index 04a1f66ad1ba7..e8fd92f92637e 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemAndNamedTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemAndNamedTlsRegistryTest.java @@ -19,11 +19,11 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemAndTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemAndTlsRegistryTest.java index e9e117f3e5f09..8d085efa23b37 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemAndTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemAndTlsRegistryTest.java @@ -19,11 +19,11 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemTest.java index 3c5bf89806922..9c06d87d99fe5 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/management/ManagementWithPemTest.java @@ -19,11 +19,11 @@ import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthTest.java index 303c23ab05f29..872aaaad9f9f9 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthTest.java @@ -20,10 +20,10 @@ import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.quarkus.vertx.http.security.TestTrustedIdentityProvider; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthWithTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthWithTlsRegistryTest.java index b8cdf17d64327..9b4af696f361e 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthWithTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestBasicAuthWithTlsRegistryTest.java @@ -19,10 +19,10 @@ import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.quarkus.vertx.http.security.TestTrustedIdentityProvider; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestTest.java index e8ac562acc49e..a966229fac7f2 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestTest.java @@ -16,10 +16,10 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestWithTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestWithTlsRegistryTest.java index d8caebbf0e61b..e0d867688a5b6 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestWithTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequestWithTlsRegistryTest.java @@ -16,10 +16,10 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequiredTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequiredTest.java index fbbed0c116045..06ea879e0d139 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequiredTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsRequiredTest.java @@ -16,10 +16,10 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12Test.java index c0cf63e6b0fe1..606827a0144de 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12Test.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12Test.java @@ -16,10 +16,10 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12WithTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12WithTlsRegistryTest.java index f2199f8642026..6e6532fb87923 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12WithTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithP12WithTlsRegistryTest.java @@ -16,10 +16,10 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemAndTlsRegistryTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemAndTlsRegistryTest.java index 0ed661564a74e..d85e0bb0b58ce 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemAndTlsRegistryTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemAndTlsRegistryTest.java @@ -16,10 +16,10 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemTest.java index 6230f62508515..6eed8921fc9ab 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/mtls/MtlsWithPemTest.java @@ -16,10 +16,10 @@ import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJKSWithSniMatchingSanDNSTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJKSWithSniMatchingSanDNSTest.java index 13b1a301a6c80..ac79b60ec18d3 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJKSWithSniMatchingSanDNSTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJKSWithSniMatchingSanDNSTest.java @@ -14,16 +14,16 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.Router; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-sni", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, cn = "acme.org", subjectAlternativeNames = "DNS:example.com", aliases = { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksTest.java index ffefd7fd6f979..e0d1976ab0ccc 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksTest.java @@ -15,10 +15,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksWithAliasTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksWithAliasTest.java index 33d90888a747e..26184e02f5564 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksWithAliasTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithJksWithAliasTest.java @@ -15,11 +15,11 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-alias", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12Test.java index 981c9b70cf292..47e0d0f502257 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12Test.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12Test.java @@ -15,10 +15,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12WithAliasTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12WithAliasTest.java index 6b0df8212b263..c741df1cbe289 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12WithAliasTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12WithAliasTest.java @@ -15,11 +15,11 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-alias", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12WithSniMatchingSanDNSTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12WithSniMatchingSanDNSTest.java index 10394dc9d8392..316a927b716e9 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12WithSniMatchingSanDNSTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithP12WithSniMatchingSanDNSTest.java @@ -14,16 +14,16 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.Router; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-sni", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, cn = "acme.org", subjectAlternativeNames = "DNS:example.com", aliases = { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemTest.java index a9580ab8491f5..08d94c88c4d0f 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemTest.java @@ -16,10 +16,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; /** * We also set quarkus.http.insecure-requests=disabled in order to test that server starts correctly - see diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemWithSniMatchingSanDNSTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemWithSniMatchingSanDNSTest.java index 762c0bdf14121..7a2622f53848a 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemWithSniMatchingSanDNSTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/SslServerWithPemWithSniMatchingSanDNSTest.java @@ -15,16 +15,16 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.Router; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-sni", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, cn = "acme.org", subjectAlternativeNames = "DNS:example.com", aliases = { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJKSWithSniMatchingSanDNSTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJKSWithSniMatchingSanDNSTest.java index df288706e1ab9..56dec683d3eca 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJKSWithSniMatchingSanDNSTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJKSWithSniMatchingSanDNSTest.java @@ -14,16 +14,16 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.Router; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-sni", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, cn = "acme.org", subjectAlternativeNames = "DNS:example.com", aliases = { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJksTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJksTest.java index d1d185ea0de7e..3986ea131814b 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJksTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJksTest.java @@ -15,10 +15,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJksWithAliasTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJksWithAliasTest.java index 6892f62c8f871..2dc9966b04f7e 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJksWithAliasTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithJksWithAliasTest.java @@ -15,11 +15,11 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-alias", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithMissingConfigurationTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithMissingConfigurationTest.java index 5b2548446a69f..7280e134488cd 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithMissingConfigurationTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithMissingConfigurationTest.java @@ -14,10 +14,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithNamedConfigTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithNamedConfigTest.java index cf4622820ff4d..1f5f1c3bcf3ff 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithNamedConfigTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithNamedConfigTest.java @@ -15,10 +15,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12Test.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12Test.java index 4ce427b1e7c96..f237d3b8419c1 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12Test.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12Test.java @@ -15,10 +15,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12WithAliasTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12WithAliasTest.java index 56ad889664b5f..479e83f94da01 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12WithAliasTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12WithAliasTest.java @@ -15,11 +15,11 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-alias", password = "secret", formats = { Format.JKS, Format.PKCS12, diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12WithSniMatchingSanDNSTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12WithSniMatchingSanDNSTest.java index a2de36c67d943..ed121343ba1a9 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12WithSniMatchingSanDNSTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithP12WithSniMatchingSanDNSTest.java @@ -14,16 +14,16 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.Router; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-sni", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, cn = "acme.org", subjectAlternativeNames = "DNS:example.com", aliases = { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithPemTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithPemTest.java index e3163ad17240d..01343e15cf7a7 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithPemTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithPemTest.java @@ -16,10 +16,10 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; import io.restassured.RestAssured; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.ext.web.Router; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; /** * We also set quarkus.http.insecure-requests=disabled in order to test that server starts correctly - see diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithPemWithSniMatchingSanDNSTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithPemWithSniMatchingSanDNSTest.java index 2bf2705e81961..2a41551e9c02d 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithPemWithSniMatchingSanDNSTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/tls/TlsServerWithPemWithSniMatchingSanDNSTest.java @@ -15,16 +15,16 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Alias; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.Router; import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.client.WebClientOptions; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Alias; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test-sni", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, cn = "acme.org", subjectAlternativeNames = "DNS:example.com", aliases = { diff --git a/extensions/websockets-next/deployment/pom.xml b/extensions/websockets-next/deployment/pom.xml index 7a72a09f793d2..03928d9cbb66e 100644 --- a/extensions/websockets-next/deployment/pom.xml +++ b/extensions/websockets-next/deployment/pom.xml @@ -61,8 +61,8 @@ test - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/client/MtlsWithP12ClientEndpointTest.java b/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/client/MtlsWithP12ClientEndpointTest.java index 974b29e25d63b..2a21ae663d083 100644 --- a/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/client/MtlsWithP12ClientEndpointTest.java +++ b/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/client/MtlsWithP12ClientEndpointTest.java @@ -25,9 +25,9 @@ import io.quarkus.websockets.next.WebSocketClient; import io.quarkus.websockets.next.WebSocketClientConnection; import io.quarkus.websockets.next.WebSocketConnector; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "mtls-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM }, client = true)) diff --git a/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/client/TlsClientEndpointTest.java b/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/client/TlsClientEndpointTest.java index 07ec8054d0d2d..8f5af7885b530 100644 --- a/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/client/TlsClientEndpointTest.java +++ b/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/client/TlsClientEndpointTest.java @@ -25,9 +25,9 @@ import io.quarkus.websockets.next.WebSocketClient; import io.quarkus.websockets.next.WebSocketClientConnection; import io.quarkus.websockets.next.WebSocketConnector; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = { Format.JKS, Format.PKCS12, Format.PEM })) diff --git a/integration-tests/grpc-tls-p12/pom.xml b/integration-tests/grpc-tls-p12/pom.xml index fd1ba8d02145e..0e7a121943d7e 100644 --- a/integration-tests/grpc-tls-p12/pom.xml +++ b/integration-tests/grpc-tls-p12/pom.xml @@ -78,8 +78,8 @@ - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/integration-tests/grpc-tls-p12/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsEndpointTest.java b/integration-tests/grpc-tls-p12/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsEndpointTest.java index 06d82e849a518..e76f568433c26 100644 --- a/integration-tests/grpc-tls-p12/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsEndpointTest.java +++ b/integration-tests/grpc-tls-p12/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsEndpointTest.java @@ -9,9 +9,9 @@ import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "grpc-tls", password = "wibble", formats = { Format.PKCS12, Format.PEM })) diff --git a/integration-tests/grpc-tls-p12/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsServiceTest.java b/integration-tests/grpc-tls-p12/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsServiceTest.java index 9cc4f2a75e756..711986c1d575f 100644 --- a/integration-tests/grpc-tls-p12/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsServiceTest.java +++ b/integration-tests/grpc-tls-p12/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsServiceTest.java @@ -19,6 +19,9 @@ import io.grpc.Channel; import io.quarkus.grpc.test.utils.GRPCTestUtils; import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientOptions; @@ -26,9 +29,6 @@ import io.vertx.core.net.SocketAddress; import io.vertx.grpc.client.GrpcClient; import io.vertx.grpc.client.GrpcClientChannel; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "grpc-tls", password = "wibble", formats = { Format.PKCS12, Format.PEM })) diff --git a/integration-tests/grpc-tls/pom.xml b/integration-tests/grpc-tls/pom.xml index cbb9fab63970a..16fdfe120501e 100644 --- a/integration-tests/grpc-tls/pom.xml +++ b/integration-tests/grpc-tls/pom.xml @@ -78,8 +78,8 @@ - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/integration-tests/grpc-tls/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsEndpointTestBase.java b/integration-tests/grpc-tls/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsEndpointTestBase.java index 4e42827dba5c9..f9a3552c5f36c 100644 --- a/integration-tests/grpc-tls/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsEndpointTestBase.java +++ b/integration-tests/grpc-tls/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsEndpointTestBase.java @@ -8,9 +8,9 @@ import org.junit.jupiter.api.Test; import io.restassured.RestAssured; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "grpc-tls", password = "wibble", formats = { Format.JKS, Format.PEM })) diff --git a/integration-tests/grpc-tls/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsServiceTestBase.java b/integration-tests/grpc-tls/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsServiceTestBase.java index aad97c3e81ae0..fe4694323e81a 100644 --- a/integration-tests/grpc-tls/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsServiceTestBase.java +++ b/integration-tests/grpc-tls/src/test/java/io/quarkus/grpc/examples/hello/HelloWorldTlsServiceTestBase.java @@ -13,9 +13,9 @@ import examples.MutinyGreeterGrpc; import io.grpc.Channel; import io.quarkus.grpc.test.utils.GRPCTestUtils; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(baseDir = "target/certs", certificates = @Certificate(name = "grpc-tls", password = "wibble", formats = { Format.JKS, Format.PEM })) diff --git a/integration-tests/kafka-ssl/pom.xml b/integration-tests/kafka-ssl/pom.xml index 0b912f72f02fc..05c78f2330779 100644 --- a/integration-tests/kafka-ssl/pom.xml +++ b/integration-tests/kafka-ssl/pom.xml @@ -84,8 +84,8 @@ test - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/integration-tests/kafka-ssl/src/test/java/io/quarkus/it/kafka/SslKafkaConsumerTest.java b/integration-tests/kafka-ssl/src/test/java/io/quarkus/it/kafka/SslKafkaConsumerTest.java index f2b0144bad31b..188761217d32d 100644 --- a/integration-tests/kafka-ssl/src/test/java/io/quarkus/it/kafka/SslKafkaConsumerTest.java +++ b/integration-tests/kafka-ssl/src/test/java/io/quarkus/it/kafka/SslKafkaConsumerTest.java @@ -19,9 +19,9 @@ import io.quarkus.test.common.WithTestResource; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; -import me.escoffier.certs.Format; -import me.escoffier.certs.junit5.Certificate; -import me.escoffier.certs.junit5.Certificates; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; @Certificates(certificates = { @Certificate(name = "kafka", formats = { Format.PKCS12, Format.JKS, diff --git a/integration-tests/mailer/pom.xml b/integration-tests/mailer/pom.xml index ad2305b69a2a8..47f3e76d5758e 100644 --- a/integration-tests/mailer/pom.xml +++ b/integration-tests/mailer/pom.xml @@ -67,8 +67,8 @@ - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test diff --git a/integration-tests/mailer/src/test/java/io/quarkus/it/mailer/MailpitFullTlsTestResource.java b/integration-tests/mailer/src/test/java/io/quarkus/it/mailer/MailpitFullTlsTestResource.java index a79ec25e0094d..926ea9217779e 100644 --- a/integration-tests/mailer/src/test/java/io/quarkus/it/mailer/MailpitFullTlsTestResource.java +++ b/integration-tests/mailer/src/test/java/io/quarkus/it/mailer/MailpitFullTlsTestResource.java @@ -13,9 +13,9 @@ import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; import io.restassured.RestAssured; -import me.escoffier.certs.CertificateGenerator; -import me.escoffier.certs.CertificateRequest; -import me.escoffier.certs.Format; +import io.smallrye.certs.CertificateGenerator; +import io.smallrye.certs.CertificateRequest; +import io.smallrye.certs.Format; public class MailpitFullTlsTestResource implements QuarkusTestResourceLifecycleManager { diff --git a/integration-tests/mailer/src/test/java/io/quarkus/it/mailer/MailpitTestResource.java b/integration-tests/mailer/src/test/java/io/quarkus/it/mailer/MailpitTestResource.java index aa79b904a515e..eeb6270d23a45 100644 --- a/integration-tests/mailer/src/test/java/io/quarkus/it/mailer/MailpitTestResource.java +++ b/integration-tests/mailer/src/test/java/io/quarkus/it/mailer/MailpitTestResource.java @@ -13,9 +13,9 @@ import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; import io.restassured.RestAssured; -import me.escoffier.certs.CertificateGenerator; -import me.escoffier.certs.CertificateRequest; -import me.escoffier.certs.Format; +import io.smallrye.certs.CertificateGenerator; +import io.smallrye.certs.CertificateRequest; +import io.smallrye.certs.Format; /** * Starts a MailPit container without TLS but accepting STARTTLS commands. diff --git a/integration-tests/mtls-certificates/pom.xml b/integration-tests/mtls-certificates/pom.xml index fa7befcec5bdd..5209a355df098 100644 --- a/integration-tests/mtls-certificates/pom.xml +++ b/integration-tests/mtls-certificates/pom.xml @@ -33,8 +33,8 @@ test - me.escoffier.certs - certificate-generator-junit5 + io.smallrye.certs + smallrye-certificate-generator-junit5 test From 0cb61b7493f2bf6fc9bca0c2ba11b44e7095e8d7 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Wed, 3 Jul 2024 10:55:05 -0300 Subject: [PATCH 14/94] Bump JBoss Logging to 3.0.0.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 64067b99562da..5a22297e53195 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -106,7 +106,7 @@ 2.2.21 2.2.5.Final 2.2.2.Final - 2.2.1.Final + 3.0.0.Final 2.0.0.Final 1.7.0.Final 1.0.1.Final From 21ce94b16494b128e7cad3f5ca713b793b8b92b9 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 3 Jul 2024 17:25:31 +0300 Subject: [PATCH 15/94] Polish HeaderUtil I stumbled upon this when looking at #41639 --- .../reactive/common/headers/HeaderUtil.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/headers/HeaderUtil.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/headers/HeaderUtil.java index 28d078838e5fb..15239f7aa1cc5 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/headers/HeaderUtil.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/headers/HeaderUtil.java @@ -114,8 +114,7 @@ public static URI getLocation(MultivaluedMap headers) public static MediaType getMediaType(MultivaluedMap headers) { Object first = headers.getFirst(HttpHeaders.CONTENT_TYPE); - if (first instanceof String) { - String contentType = (String) first; + if (first instanceof String contentType) { return MediaType.valueOf(contentType); } else { return (MediaType) first; @@ -150,8 +149,7 @@ public static Map getCookies(MultivaluedMap cookies = new HashMap(); for (Object obj : list) { - if (obj instanceof Cookie) { - Cookie cookie = (Cookie) obj; + if (obj instanceof Cookie cookie) { cookies.put(cookie.getName(), cookie); } else { String str = headerToString(obj); @@ -170,8 +168,7 @@ public static Map getNewCookies(MultivaluedMap cookies = new HashMap<>(); for (Object obj : list) { - if (obj instanceof NewCookie) { - NewCookie cookie = (NewCookie) obj; + if (obj instanceof NewCookie cookie) { cookies.put(cookie.getName(), cookie); } else { String str = HeaderUtil.headerToString(obj); @@ -203,7 +200,7 @@ public static String getHeaderString(MultivaluedMap he } StringBuilder sb = new StringBuilder(); for (Object s : list) { - if (sb.length() > 0) { + if (!sb.isEmpty()) { sb.append(","); } sb.append(headerToString(s)); @@ -267,13 +264,13 @@ public static List getAcceptableMediaTypes(MultivaluedMap list = new ArrayList(); + List list = new ArrayList<>(); for (Object obj : accepts) { if (obj instanceof MediaType) { list.add((MediaType) obj); continue; } - String accept = null; + String accept; if (obj instanceof String) { accept = (String) obj; } else { @@ -315,7 +312,7 @@ public static List getAcceptableLanguages(MultivaluedMap list = new ArrayList(languages.size()); + List list = new ArrayList<>(languages.size()); for (WeightedLanguage language : languages) list.add(language.getLocale()); return list; From ee2587dc5e5d86941f836106d7cb16a846571e31 Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Wed, 3 Jul 2024 12:47:48 -0500 Subject: [PATCH 16/94] Only warn about `module-info` if it exists Fixes #41669 --- .../quarkus/deployment/dev/filesystem/StaticFileManager.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/StaticFileManager.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/StaticFileManager.java index d3706c1e133e5..acfad17471237 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/StaticFileManager.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/StaticFileManager.java @@ -31,8 +31,9 @@ public Iterable getJavaSources(Iterable Date: Sun, 5 May 2024 19:06:48 +0200 Subject: [PATCH 17/94] Fix correct parsing of collections in AmazonLambdaRecorder with CollectionInputReader add test for abstractHandler add test for collection input and collection output rename tests for clarification what they are for --- ...stractInputCollectionOutputCollection.java | 20 ++++++++ ...tCollectionOutputCollectionLambdaImpl.java | 5 ++ ...lectionOutputCollectionLambdaImplTest.java | 46 +++++++++++++++++++ .../deployment/testing/GreetingLambda.java | 6 ++- .../testing/GreetingLambdaTest.java | 38 +++++++++++++++ ...InputCollectionOutputCollectionLambda.java | 24 ++++++++++ ...tCollectionOutputCollectionLambdaTest.java | 45 ++++++++++++++++++ ...aDevServicesContinuousTestingTestCase.java | 8 ++-- .../deployment/testing/LambdaHandlerET.java | 31 ------------- .../lambda/deployment/testing/Person.java | 15 ------ .../deployment/testing/model/InputPerson.java | 22 +++++++++ .../testing/model/OutputPerson.java | 20 ++++++++ .../lambda/runtime/AmazonLambdaRecorder.java | 6 +++ .../handlers/CollectionInputReader.java | 28 +++++++++++ 14 files changed, 263 insertions(+), 51 deletions(-) create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollection.java create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollectionLambdaImpl.java create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollectionLambdaImplTest.java create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/GreetingLambdaTest.java create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/InputCollectionOutputCollectionLambda.java create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/InputCollectionOutputCollectionLambdaTest.java delete mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaHandlerET.java delete mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/Person.java create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/model/InputPerson.java create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/model/OutputPerson.java create mode 100644 extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/handlers/CollectionInputReader.java diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollection.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollection.java new file mode 100644 index 0000000000000..26edbfe29cb43 --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollection.java @@ -0,0 +1,20 @@ +package io.quarkus.amazon.lambda.deployment.testing; + +import java.util.ArrayList; +import java.util.List; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import io.quarkus.amazon.lambda.deployment.testing.model.InputPerson; +import io.quarkus.amazon.lambda.deployment.testing.model.OutputPerson; + +public abstract class AbstractInputCollectionOutputCollection implements RequestHandler, List> { + + @Override + public List handleRequest(List inputPeronList, Context context) { + List personList = new ArrayList<>(); + inputPeronList.forEach(person -> personList.add(new OutputPerson(person.getName()))); + return personList; + } +} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollectionLambdaImpl.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollectionLambdaImpl.java new file mode 100644 index 0000000000000..6b8aff7356e9f --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollectionLambdaImpl.java @@ -0,0 +1,5 @@ +package io.quarkus.amazon.lambda.deployment.testing; + +public class AbstractInputCollectionOutputCollectionLambdaImpl extends AbstractInputCollectionOutputCollection { + +} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollectionLambdaImplTest.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollectionLambdaImplTest.java new file mode 100644 index 0000000000000..ce5f9843936b5 --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/AbstractInputCollectionOutputCollectionLambdaImplTest.java @@ -0,0 +1,46 @@ +package io.quarkus.amazon.lambda.deployment.testing; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.hasEntry; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.amazon.lambda.deployment.testing.model.InputPerson; +import io.quarkus.amazon.lambda.deployment.testing.model.OutputPerson; +import io.quarkus.test.QuarkusUnitTest; + +public class AbstractInputCollectionOutputCollectionLambdaImplTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap + .create(JavaArchive.class) + .addClasses(AbstractInputCollectionOutputCollectionLambdaImpl.class, AbstractInputCollectionOutputCollection.class, + InputPerson.class, OutputPerson.class)); + + @Test + void abstractRequestHandler_InputCollectionInputPerson_OutputCollectionOutputPerson() { + + List personList = new ArrayList<>(); + personList.add(new InputPerson("Chris")); + personList.add(new InputPerson("Fred")); + + given() + .body(personList) + .when() + .post() + .then() + .statusCode(200) + .body("", hasItem(hasEntry("outputname", "Chris"))) // OutputPerson serializes name with key outputname + .body("", hasItem(hasEntry("outputname", "Fred"))) + .body("", not(hasItem(hasEntry("name", "Chris")))) // make sure that there is no key name + .body("", not(hasItem(hasEntry("name", "Fred")))); + } +} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/GreetingLambda.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/GreetingLambda.java index f62a7ef7eef95..7b2ed22d57b92 100644 --- a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/GreetingLambda.java +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/GreetingLambda.java @@ -3,10 +3,12 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; -public class GreetingLambda implements RequestHandler { +import io.quarkus.amazon.lambda.deployment.testing.model.InputPerson; + +public class GreetingLambda implements RequestHandler { @Override - public String handleRequest(Person input, Context context) { + public String handleRequest(InputPerson input, Context context) { return "Hey " + input.getName(); } } diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/GreetingLambdaTest.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/GreetingLambdaTest.java new file mode 100644 index 0000000000000..899e2febbff0e --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/GreetingLambdaTest.java @@ -0,0 +1,38 @@ +package io.quarkus.amazon.lambda.deployment.testing; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.amazon.lambda.deployment.testing.model.InputPerson; +import io.quarkus.test.QuarkusUnitTest; + +class GreetingLambdaTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap + .create(JavaArchive.class) + .addClasses(GreetingLambda.class, InputPerson.class)); + + @Test + public void requestHandler_InputPerson_OutputString() throws Exception { + // you test your lambdas by invoking on http://localhost:8081 + // this works in dev mode too + + InputPerson in = new InputPerson("Stu"); + given() + .contentType("application/json") + .accept("application/json") + .body(in) + .when() + .post() + .then() + .statusCode(200) + .body(containsString("Hey Stu")); + } + +} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/InputCollectionOutputCollectionLambda.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/InputCollectionOutputCollectionLambda.java new file mode 100644 index 0000000000000..437cc4e6fcbc5 --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/InputCollectionOutputCollectionLambda.java @@ -0,0 +1,24 @@ +package io.quarkus.amazon.lambda.deployment.testing; + +import java.util.ArrayList; +import java.util.List; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import io.quarkus.amazon.lambda.deployment.testing.model.InputPerson; +import io.quarkus.amazon.lambda.deployment.testing.model.OutputPerson; + +public class InputCollectionOutputCollectionLambda implements RequestHandler, List> { + + @Override + public List handleRequest(List people, Context context) { + + List outputPeople = new ArrayList<>(); + people.stream().parallel().forEach((person) -> { + outputPeople.add(new OutputPerson(person.getName())); + }); + + return outputPeople; + } +} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/InputCollectionOutputCollectionLambdaTest.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/InputCollectionOutputCollectionLambdaTest.java new file mode 100644 index 0000000000000..907827ece866a --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/InputCollectionOutputCollectionLambdaTest.java @@ -0,0 +1,45 @@ +package io.quarkus.amazon.lambda.deployment.testing; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.hasEntry; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.amazon.lambda.deployment.testing.model.InputPerson; +import io.quarkus.amazon.lambda.deployment.testing.model.OutputPerson; +import io.quarkus.test.QuarkusUnitTest; + +public class InputCollectionOutputCollectionLambdaTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap + .create(JavaArchive.class) + .addClasses(InputCollectionOutputCollectionLambda.class, InputPerson.class, OutputPerson.class)); + + @Test + void requestHandler_InputCollectionInputPerson_OutputCollectionOutputPerson() { + + List personList = new ArrayList<>(); + personList.add(new InputPerson("Chris")); + personList.add(new InputPerson("Fred")); + + given() + .body(personList) + .when() + .post() + .then() + .statusCode(200) + .body("", hasItem(hasEntry("outputname", "Chris"))) // OutputPerson serializes name with key outputname + .body("", hasItem(hasEntry("outputname", "Fred"))) + .body("", not(hasItem(hasEntry("name", "Chris")))) // make sure that there is no key name + .body("", not(hasItem(hasEntry("name", "Fred")))); + } +} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaDevServicesContinuousTestingTestCase.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaDevServicesContinuousTestingTestCase.java index cef9a2bda6761..60b108b89b094 100644 --- a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaDevServicesContinuousTestingTestCase.java +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaDevServicesContinuousTestingTestCase.java @@ -10,6 +10,8 @@ import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.amazon.lambda.deployment.testing.model.InputPerson; +import io.quarkus.amazon.lambda.deployment.testing.model.OutputPerson; import io.quarkus.test.ContinuousTestingTestUtils; import io.quarkus.test.QuarkusDevModeTest; @@ -21,7 +23,7 @@ public class LambdaDevServicesContinuousTestingTestCase { @Override public JavaArchive get() { return ShrinkWrap.create(JavaArchive.class) - .addClasses(GreetingLambda.class, Person.class) + .addClasses(GreetingLambda.class, InputPerson.class, OutputPerson.class) .addAsResource( new StringAsset(ContinuousTestingTestUtils.appProperties( "quarkus.log.category.\"io.quarkus.amazon.lambda.runtime\".level=DEBUG")), @@ -30,7 +32,7 @@ public JavaArchive get() { }).setTestArchiveProducer(new Supplier<>() { @Override public JavaArchive get() { - return ShrinkWrap.create(JavaArchive.class).addClass(LambdaHandlerET.class); + return ShrinkWrap.create(JavaArchive.class).addClass(GreetingLambdaTest.class); } }); @@ -45,7 +47,7 @@ public void testLambda() throws Exception { result = utils.waitForNextCompletion(); Assertions.assertEquals(0, result.getTotalTestsPassed()); Assertions.assertEquals(1, result.getTotalTestsFailed()); - test.modifyTestSourceFile(LambdaHandlerET.class, s -> s.replace("Hey", "Yo")); + test.modifyTestSourceFile(GreetingLambdaTest.class, s -> s.replace("Hey", "Yo")); result = utils.waitForNextCompletion(); Assertions.assertEquals(1, result.getTotalTestsPassed()); Assertions.assertEquals(0, result.getTotalTestsFailed()); diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaHandlerET.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaHandlerET.java deleted file mode 100644 index 135b02bac0547..0000000000000 --- a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaHandlerET.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.quarkus.amazon.lambda.deployment.testing; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.CoreMatchers.containsString; - -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.QuarkusTest; - -@QuarkusTest -class LambdaHandlerET { - - @Test - public void testSimpleLambdaSuccess() throws Exception { - // you test your lambdas by invoking on http://localhost:8081 - // this works in dev mode too - - Person in = new Person(); - in.setName("Stu"); - given() - .contentType("application/json") - .accept("application/json") - .body(in) - .when() - .post() - .then() - .statusCode(200) - .body(containsString("Hey Stu")); - } - -} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/Person.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/Person.java deleted file mode 100644 index d2a4066a77dc2..0000000000000 --- a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/Person.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.quarkus.amazon.lambda.deployment.testing; - -public class Person { - - private String name; - - public String getName() { - return name; - } - - public Person setName(String name) { - this.name = name; - return this; - } -} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/model/InputPerson.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/model/InputPerson.java new file mode 100644 index 0000000000000..b74c9482d8c3c --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/model/InputPerson.java @@ -0,0 +1,22 @@ +package io.quarkus.amazon.lambda.deployment.testing.model; + +public class InputPerson { + + public InputPerson() { + } + + public InputPerson(String name) { + this.name = name; + } + + private String name; + + public String getName() { + return name; + } + + public InputPerson setName(String name) { + this.name = name; + return this; + } +} diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/model/OutputPerson.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/model/OutputPerson.java new file mode 100644 index 0000000000000..f448321ffdd5f --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/model/OutputPerson.java @@ -0,0 +1,20 @@ +package io.quarkus.amazon.lambda.deployment.testing.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class OutputPerson { + + public OutputPerson() { + } + + public OutputPerson(String name) { + this.name = name; + } + + @JsonProperty("outputname") + private String name; + + public String getName() { + return name; + } +} diff --git a/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java b/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java index f8fb6d5b76568..49dc6d9d92a2b 100644 --- a/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java +++ b/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/AmazonLambdaRecorder.java @@ -4,6 +4,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -16,6 +17,7 @@ import com.amazonaws.services.lambda.runtime.events.S3Event; import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.amazon.lambda.runtime.handlers.CollectionInputReader; import io.quarkus.amazon.lambda.runtime.handlers.S3EventInputReader; import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.runtime.LaunchMode; @@ -53,11 +55,15 @@ static void initializeHandlerClass(Class> handler ObjectMapper objectMapper = AmazonLambdaMapperRecorder.objectMapper; Method handlerMethod = discoverHandlerMethod(handlerClass); Class parameterType = handlerMethod.getParameterTypes()[0]; + if (parameterType.equals(S3Event.class)) { objectReader = new S3EventInputReader(objectMapper); + } else if (Collection.class.isAssignableFrom(parameterType)) { + objectReader = new CollectionInputReader<>(objectMapper, handlerMethod); } else { objectReader = new JacksonInputReader(objectMapper.readerFor(parameterType)); } + objectWriter = new JacksonOutputWriter(objectMapper.writerFor(handlerMethod.getReturnType())); } diff --git a/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/handlers/CollectionInputReader.java b/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/handlers/CollectionInputReader.java new file mode 100644 index 0000000000000..a698bcdbf400f --- /dev/null +++ b/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/handlers/CollectionInputReader.java @@ -0,0 +1,28 @@ +package io.quarkus.amazon.lambda.runtime.handlers; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Collection; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; + +import io.quarkus.amazon.lambda.runtime.LambdaInputReader; + +public class CollectionInputReader implements LambdaInputReader> { + final ObjectReader reader; + + public CollectionInputReader(ObjectMapper mapper, Method handler) { + Type genericParameterType = handler.getGenericParameterTypes()[0]; + JavaType constructParameterType = mapper.getTypeFactory().constructType(genericParameterType); + this.reader = mapper.readerFor(constructParameterType); + } + + @Override + public Collection readValue(InputStream is) throws IOException { + return this.reader.readValue(is); + } +} From 0ae63d59354cfca3a32b4006be3ca4dcabeb9870 Mon Sep 17 00:00:00 2001 From: vsevel Date: Wed, 3 Jul 2024 14:21:52 +0200 Subject: [PATCH 18/94] Support multiple recipe artifacts in quarkus update --- .../io/quarkus/cli/build/GradleRunner.java | 3 + .../io/quarkus/cli/build/MavenRunner.java | 3 + .../io/quarkus/cli/update/RewriteGroup.java | 4 + .../quarkus/gradle/tasks/QuarkusUpdate.java | 16 +++ .../java/io/quarkus/maven/UpdateMojo.java | 9 ++ .../devtools/commands/UpdateProject.java | 6 + .../handlers/UpdateProjectCommandHandler.java | 6 +- .../update/rewrite/QuarkusUpdateCommand.java | 4 +- .../update/rewrite/QuarkusUpdates.java | 3 +- .../rewrite/QuarkusUpdatesRepository.java | 103 +++++++++++------- 10 files changed, 116 insertions(+), 41 deletions(-) diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java index afe7c34c796ab..ee4d6eb6253ea 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java @@ -173,6 +173,9 @@ public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, Rew if (rewrite.updateRecipesVersion != null) { args.add("--updateRecipesVersion=" + rewrite.updateRecipesVersion); } + if (rewrite.additionalUpdateRecipeCoords != null) { + args.add("--additionalUpdateRecipeCoords=" + rewrite.additionalUpdateRecipeCoords); + } if (rewrite.noRewrite) { args.add("--noRewrite"); } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java index 632c68c8135ff..b495e8b78f7a4 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java @@ -176,6 +176,9 @@ public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, Rew if (rewrite.updateRecipesVersion != null) { args.add("-DupdateRecipesVersion=" + rewrite.updateRecipesVersion); } + if (rewrite.additionalUpdateRecipeCoords != null) { + args.add("-DadditionalUpdateRecipeCoords" + rewrite.additionalUpdateRecipeCoords); + } if (rewrite.dryRun) { args.add("-DrewriteDryRun"); } diff --git a/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java index 48bd2a3a94c10..2683d87365fb0 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java @@ -20,4 +20,8 @@ public class RewriteGroup { "--rewrite-plugin-version" }, description = "Use a custom OpenRewrite plugin version.") public String pluginVersion; + @CommandLine.Option(order = 4, names = { + "--additional-update-recipe-coords" }, description = "Specify an additional list of artifacts to retrieve recipes from.") + public String additionalUpdateRecipeCoords; + } diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java index 6e33b913d8d3d..8e02a5626055c 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java @@ -29,6 +29,7 @@ public abstract class QuarkusUpdate extends QuarkusPlatformTask { private String rewritePluginVersion = null; private String rewriteUpdateRecipesVersion = null; + private String rewriteAdditionalUpdateRecipeCoords = null; @Input @Optional @@ -87,6 +88,18 @@ public QuarkusUpdate setRewriteUpdateRecipesVersion(String rewriteUpdateRecipesV return this; } + @Input + @Optional + public String getRewriteAdditionalUpdateRecipeCoords() { + return rewriteAdditionalUpdateRecipeCoords; + } + + @Option(description = " The additional artifacts to retrieve recipes from.", option = "additionalUpdateRecipeCoords") + public QuarkusUpdate setRewriteAdditionalUpdateRecipeCoords(String rewriteAdditionalUpdateRecipeCoords) { + this.rewriteAdditionalUpdateRecipeCoords = rewriteAdditionalUpdateRecipeCoords; + return this; + } + @Input @Optional public String getTargetStreamId() { @@ -143,6 +156,9 @@ public void logUpdates() { if (rewriteUpdateRecipesVersion != null) { invoker.rewriteUpdateRecipesVersion(rewriteUpdateRecipesVersion); } + if (rewriteAdditionalUpdateRecipeCoords != null) { + invoker.rewriteAdditionalUpdateRecipeCoords(rewriteAdditionalUpdateRecipeCoords); + } if (rewritePluginVersion != null) { invoker.rewritePluginVersion(rewritePluginVersion); } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java index a605646dc177f..54525279dc298 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java @@ -65,6 +65,12 @@ public class UpdateMojo extends QuarkusProjectStateMojoBase { @Parameter(property = "updateRecipesVersion", required = false) private String rewriteUpdateRecipesVersion; + /** + * The list of artifacts containing rewrite recipes + */ + @Parameter(property = "additionalUpdateRecipeCoords", required = false) + private String rewriteAdditionalUpdateRecipeCoords; + /** * Target stream (e.g: 2.0) */ @@ -116,6 +122,9 @@ protected void processProjectState(QuarkusProject quarkusProject) throws MojoExe if (rewriteUpdateRecipesVersion != null) { invoker.rewriteUpdateRecipesVersion(rewriteUpdateRecipesVersion); } + if (rewriteAdditionalUpdateRecipeCoords != null) { + invoker.rewriteAdditionalUpdateRecipeCoords(rewriteAdditionalUpdateRecipeCoords); + } invoker.rewriteDryRun(rewriteDryRun); invoker.noRewrite(noRewrite); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java index 92409437a023d..edde73e81b92e 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java @@ -25,6 +25,7 @@ public class UpdateProject { public static final String TARGET_PLATFORM_VERSION = "quarkus.update-project.target-platform-version"; public static final String REWRITE_PLUGIN_VERSION = "quarkus.update-project.rewrite.plugin-version"; public static final String REWRITE_UPDATE_RECIPES_VERSION = "quarkus.update-project.rewrite.update-recipes-version"; + public static final String REWRITE_ADDITIONAL_UPDATE_RECIPE_COORDS = "quarkus.update-project.rewrite.additional-update-recipe-coords"; public static final String REWRITE_DRY_RUN = "quarkus.update-project.rewrite.dry-run"; private final QuarkusCommandInvocation invocation; @@ -64,6 +65,11 @@ public UpdateProject rewriteUpdateRecipesVersion(String rewriteUpdateRecipesVers return this; } + public UpdateProject rewriteAdditionalUpdateRecipeCoords(String rewriteAdditionalUpdateRecipeCoords) { + invocation.setValue(REWRITE_ADDITIONAL_UPDATE_RECIPE_COORDS, rewriteAdditionalUpdateRecipeCoords); + return this; + } + public UpdateProject rewriteDryRun(boolean rewriteDryRun) { invocation.setValue(REWRITE_DRY_RUN, rewriteDryRun); return this; diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java index 9dab75d417447..f8655b01a03a1 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java @@ -117,9 +117,13 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws final String updateRecipesVersion = invocation.getValue( UpdateProject.REWRITE_UPDATE_RECIPES_VERSION, QuarkusUpdatesRepository.DEFAULT_UPDATE_RECIPES_VERSION); + final String additionalUpdateRecipeCoords = invocation.getValue( + UpdateProject.REWRITE_ADDITIONAL_UPDATE_RECIPE_COORDS, + null); final FetchResult fetchResult = QuarkusUpdates.createRecipe(invocation.log(), recipe, - QuarkusProjectHelper.artifactResolver(), buildTool, updateRecipesVersion, request); + QuarkusProjectHelper.artifactResolver(), buildTool, updateRecipesVersion, + additionalUpdateRecipeCoords, request); invocation.log().info("OpenRewrite recipe generated: %s", recipe); String rewritePluginVersion = invocation.getValue(UpdateProject.REWRITE_PLUGIN_VERSION, diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateCommand.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateCommand.java index debbf1f24e58d..674bffe72d8cb 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateCommand.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdateCommand.java @@ -65,7 +65,9 @@ private static void runMavenUpdate(MessageWriter log, Path baseDir, String rewri executeCommand(baseDir, getMavenUpdateCommand(mvnBinary, rewritePluginVersion, recipesGAV, recipe, dryRun), log); // format the sources - executeCommand(baseDir, getMavenProcessSourcesCommand(mvnBinary), log); + if (!dryRun) { + executeCommand(baseDir, getMavenProcessSourcesCommand(mvnBinary), log); + } } private static void runGradleUpdate(MessageWriter log, Path baseDir, String rewritePluginVersion, String recipesGAV, diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java index 9989ca2a42a5f..ff152288495e0 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java @@ -22,11 +22,12 @@ private QuarkusUpdates() { } public static FetchResult createRecipe(MessageWriter log, Path target, MavenArtifactResolver artifactResolver, - BuildTool buildTool, String updateRecipesVersion, + BuildTool buildTool, String updateRecipesVersion, String additionalUpdateRecipeCoords, ProjectUpdateRequest request) throws IOException { final FetchResult result = QuarkusUpdatesRepository.fetchRecipes(log, artifactResolver, buildTool, updateRecipesVersion, + additionalUpdateRecipeCoords, request.currentVersion, request.targetVersion, request.projectExtensionsUpdateInfo diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java index 57198af695eee..ef00b8eb905b8 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java @@ -5,6 +5,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -41,50 +43,75 @@ private QuarkusUpdatesRepository() { public static FetchResult fetchRecipes(MessageWriter log, MavenArtifactResolver artifactResolver, BuildTool buildTool, - String recipeVersion, String currentVersion, + String recipeVersion, String additionalUpdateRecipeCoords, String currentVersion, String targetVersion, List topExtensionDependency) { - final String gav = QUARKUS_RECIPE_GA + ":" + recipeVersion; - - Map recipeDirectoryNames = new LinkedHashMap<>(); - recipeDirectoryNames.put("core", new String[] { currentVersion, targetVersion }); - for (ExtensionUpdateInfo dep : topExtensionDependency) { - recipeDirectoryNames.put( - toKey(dep), - new String[] { dep.getCurrentDep().getVersion(), dep.getRecommendedDependency().getVersion() }); + + List gavs = new ArrayList<>(); + gavs.add(QUARKUS_RECIPE_GA + ":" + recipeVersion); + if (additionalUpdateRecipeCoords != null) { + gavs.addAll(Arrays.stream(additionalUpdateRecipeCoords.split(",")).map(String::strip).toList()); } - try { - final Artifact artifact = artifactResolver.resolve(DependencyUtils.toArtifact(gav)).getArtifact(); - final ResourceLoader resourceLoader = ResourceLoaders.resolveFileResourceLoader( - artifact.getFile()); - final Map recipes = fetchUpdateRecipes(resourceLoader, "quarkus-updates", recipeDirectoryNames); - final Properties props = resourceLoader.loadResourceAsPath("quarkus-updates/", p -> { - final Properties properties = new Properties(); - final Path propPath = p.resolve("recipes.properties"); - if (Files.isRegularFile(propPath)) { - try (final InputStream inStream = Files.newInputStream(propPath)) { - properties.load(inStream); + List artifacts = new ArrayList<>(); + Map recipes = new HashMap<>(); + String propRewritePluginVersion = null; + + for (String gav : gavs) { + + Map recipeDirectoryNames = new LinkedHashMap<>(); + recipeDirectoryNames.put("core", new String[] { currentVersion, targetVersion }); + for (ExtensionUpdateInfo dep : topExtensionDependency) { + recipeDirectoryNames.put( + toKey(dep), + new String[] { dep.getCurrentDep().getVersion(), dep.getRecommendedDependency().getVersion() }); + } + + try { + final Artifact artifact = artifactResolver.resolve(DependencyUtils.toArtifact(gav)).getArtifact(); + String resolvedGAV = artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion(); + artifacts.add(resolvedGAV); + final ResourceLoader resourceLoader = ResourceLoaders.resolveFileResourceLoader( + artifact.getFile()); + Map newRecipes = fetchUpdateRecipes(resourceLoader, "quarkus-updates", recipeDirectoryNames); + recipes.putAll(newRecipes); + final Properties props = resourceLoader.loadResourceAsPath("quarkus-updates/", p -> { + final Properties properties = new Properties(); + final Path propPath = p.resolve("recipes.properties"); + if (Files.isRegularFile(propPath)) { + try (final InputStream inStream = Files.newInputStream(propPath)) { + properties.load(inStream); + } } + return properties; + }); + + final String pluginVersion = getPropRewritePluginVersion(props, buildTool); + if (propRewritePluginVersion == null) { + propRewritePluginVersion = pluginVersion; + } else if (!propRewritePluginVersion.equals(pluginVersion)) { + throw new RuntimeException( + "quarkus update artifacts require multiple rewrite plugin versions: " + propRewritePluginVersion + + " and " + pluginVersion); } - return properties; - }); - final String propRewritePluginVersion = getPropRewritePluginVersion(props, buildTool); - - log.info(String.format( - "Resolved io.quarkus:quarkus-updates-recipes:%s with %s recipe(s) to update from %s to %s (initially made for OpenRewrite %s plugin version: %s) ", - artifact.getVersion(), - recipes.size(), - currentVersion, - targetVersion, - buildTool, - propRewritePluginVersion)); - return new FetchResult(artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion(), - new ArrayList<>(recipes.values()), propRewritePluginVersion); - } catch (BootstrapMavenException e) { - throw new RuntimeException("Failed to resolve artifact: " + gav, e); - } catch (IOException e) { - throw new RuntimeException("Failed to load recipes in artifact: " + gav, e); + + log.info(String.format( + "Resolved %s with %s recipe(s) to update from %s to %s (initially made for OpenRewrite %s plugin version: %s) ", + gav, + recipes.size(), + currentVersion, + targetVersion, + buildTool, + propRewritePluginVersion)); + + } catch (BootstrapMavenException e) { + throw new RuntimeException("Failed to resolve artifact: " + gav, e); + } catch (IOException e) { + throw new RuntimeException("Failed to load recipes in artifact: " + gav, e); + } } + + return new FetchResult(String.join(",", artifacts), + new ArrayList<>(recipes.values()), propRewritePluginVersion); } private static String getPropRewritePluginVersion(Properties props, BuildTool buildTool) { From a0c7c69c609356ced161b96aab9c10b6197f8e11 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 4 Jul 2024 10:28:36 +0300 Subject: [PATCH 19/94] Don't always set the user parameter when starting container in tests Fixes: #41659 --- .../quarkus/test/common/DefaultDockerContainerLauncher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/DefaultDockerContainerLauncher.java b/test-framework/common/src/main/java/io/quarkus/test/common/DefaultDockerContainerLauncher.java index 2d01ef727fd4a..533048d795934 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/DefaultDockerContainerLauncher.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/DefaultDockerContainerLauncher.java @@ -118,7 +118,9 @@ public void start() throws IOException { args.add("-i"); // Interactive, write logs to stdout args.add("--rm"); - args.addAll(NativeImageBuildLocalContainerRunner.getVolumeAccessArguments(containerRuntime)); + if (!volumeMounts.isEmpty()) { + args.addAll(NativeImageBuildLocalContainerRunner.getVolumeAccessArguments(containerRuntime)); + } args.add("-p"); args.add(httpPort + ":" + httpPort); From 26911904df5a46e19c593481d02863345789680c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 4 Jul 2024 09:50:24 +0200 Subject: [PATCH 20/94] Escape ] in rewritten xrefs for downstream doc Supersedes #41651 --- .../generation/AssembleDownstreamDocumentation.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/src/main/java/io/quarkus/docs/generation/AssembleDownstreamDocumentation.java b/docs/src/main/java/io/quarkus/docs/generation/AssembleDownstreamDocumentation.java index 6f23fd809cd4c..2a88d1d2b106c 100755 --- a/docs/src/main/java/io/quarkus/docs/generation/AssembleDownstreamDocumentation.java +++ b/docs/src/main/java/io/quarkus/docs/generation/AssembleDownstreamDocumentation.java @@ -426,7 +426,7 @@ private static String rewriteContent(String fileName, addError(errors, fileName, "Unable to find title for: " + mr.group() + " [" + reference + "]"); title = "~~ unknown title ~~"; } - return "xref:" + trimReference(mr.group(1)) + "[" + title.trim() + "]"; + return "xref:" + trimReference(mr.group(1)) + "[" + escapeXrefTitleForReplaceAll(title) + "]"; }); content = ANGLE_BRACKETS_WITHOUT_DESCRIPTION_PATTERN.matcher(content).replaceAll(mr -> { @@ -436,11 +436,11 @@ private static String rewriteContent(String fileName, addError(errors, fileName, "Unable to find title for: " + mr.group() + " [" + reference + "]"); title = "~~ unknown title ~~"; } - return "xref:" + trimReference(mr.group(1)) + "[" + title.trim() + "]"; + return "xref:" + trimReference(mr.group(1)) + "[" + escapeXrefTitleForReplaceAll(title) + "]"; }); content = ANGLE_BRACKETS_WITH_DESCRIPTION_PATTERN.matcher(content).replaceAll(mr -> { - return "xref:" + trimReference(mr.group(1)) + "[" + mr.group(2).trim() + "]"; + return "xref:" + trimReference(mr.group(1)) + "[" + escapeXrefTitleForReplaceAll(mr.group(2)) + "]"; }); content = XREF_GUIDE_PATTERN.matcher(content).replaceAll(mr -> { @@ -466,6 +466,10 @@ private static String rewriteContent(String fileName, return content; } + private static String escapeXrefTitleForReplaceAll(String title) { + return title.trim().replace("]", "\\\\]"); + } + private static String trimReference(String reference) { reference = normalizeAdoc(reference); From 3c823620ad7428da2cf6ede4cdf3ed0a0ee3e822 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 22:01:37 +0000 Subject: [PATCH 21/94] Bump kafka3.version from 3.7.0 to 3.7.1 Bumps `kafka3.version` from 3.7.0 to 3.7.1. Updates `org.apache.kafka:kafka-clients` from 3.7.0 to 3.7.1 Updates `org.apache.kafka:kafka-streams` from 3.7.0 to 3.7.1 Updates `org.apache.kafka:kafka-streams-test-utils` from 3.7.0 to 3.7.1 Updates `org.apache.kafka:kafka_2.13` from 3.7.0 to 3.7.1 --- updated-dependencies: - dependency-name: org.apache.kafka:kafka-clients dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.kafka:kafka-streams dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.kafka:kafka-streams-test-utils dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.kafka:kafka_2.13 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 dcd8703f73d50..0edaa60df51bc 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -140,7 +140,7 @@ 3.6.0.Final 2.6.1 4.0.5 - 3.7.0 + 3.7.1 1.8.0 1.1.10.5 0.106.0 From e0cabae0abc7081ca0d8b016693e662ac085afeb Mon Sep 17 00:00:00 2001 From: Jan Hendriks Date: Thu, 4 Jul 2024 09:55:11 +0200 Subject: [PATCH 22/94] #6963 Add Qute support to iterate long numbers --- docs/src/main/asciidoc/qute-reference.adoc | 2 +- .../src/main/java/io/quarkus/qute/LoopSectionHelper.java | 5 +++++ .../src/test/java/io/quarkus/qute/LoopSectionTest.java | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc index dff500a62efb2..c5e25a21bfdf5 100644 --- a/docs/src/main/asciidoc/qute-reference.adoc +++ b/docs/src/main/asciidoc/qute-reference.adoc @@ -668,7 +668,7 @@ A section helper that defines the logic of a section can "execute" any of the bl [[loop_section]] ==== Loop Section -The loop section makes it possible to iterate over an instance of `Iterable`, `Iterator`, array, `Map` (element is a `Map.Entry`), `Stream`, `Integer` and `int` (primitive value). +The loop section makes it possible to iterate over an instance of `Iterable`, `Iterator`, array, `Map` (element is a `Map.Entry`), `Stream`, `Integer`, `Long`, `int` and `long` (primitive value). A `null` parameter value results in a no-op. This section has two flavors. diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java index a25d8b4b11932..43d7abe826191 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java @@ -15,6 +15,7 @@ import java.util.Set; import java.util.concurrent.CompletionStage; import java.util.stream.IntStream; +import java.util.stream.LongStream; import java.util.stream.Stream; import io.quarkus.qute.SectionHelperFactory.SectionInitContext; @@ -84,6 +85,8 @@ private static int extractSize(Object it) { return Array.getLength(it); } else if (it instanceof Integer) { return ((Integer) it); + } else if (it instanceof Long) { + return (((Long) it).intValue()); } else if (it instanceof Collection) { return ((Collection) it).size(); } else if (it instanceof Map) { @@ -101,6 +104,8 @@ private Iterator extractIterator(Object it) { return ((AbstractMap) it).entrySet().iterator(); } else if (it instanceof Integer) { return IntStream.rangeClosed(1, (Integer) it).iterator(); + } else if (it instanceof Long) { + return LongStream.rangeClosed(1, (Long) it).iterator(); } else if (it.getClass().isArray()) { int length = Array.getLength(it); List elements = new ArrayList<>(length); diff --git a/independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java b/independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java index e5046d20de327..5e0ccfbbdf8a2 100644 --- a/independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java +++ b/independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java @@ -111,6 +111,14 @@ public void testIntegerStream() { engine.parse("{#for i in total}{i}:{/for}").data("total", 3).render()); } + @Test + public void testLongStream() { + Engine engine = Engine.builder().addDefaults().build(); + + assertEquals("1:2:3:", + engine.parse("{#for i in total}{i}:{/for}").data("total", 3L).render()); + } + @Test public void testIterator() { Engine engine = Engine.builder().addDefaults().build(); From 10c335d2dde67a58d2fff4155de9ce62a130b55c Mon Sep 17 00:00:00 2001 From: brunobat Date: Mon, 17 Jun 2024 09:04:14 +0100 Subject: [PATCH 23/94] Set extension properties directly --- .../observability-devservices-lgtm.adoc | 79 ++++++++--------- .../ObservabilityDevServiceProcessor.java | 87 +++++++++++++------ .../DevResourceLifecycleManager.java | 10 +++ .../devresource/ExtensionsCatalog.java | 8 ++ .../devresource/lgtm/LgtmResource.java | 35 ++++++-- integration-tests/observability-lgtm/pom.xml | 17 ---- .../observability/test/LgtmLifecycleTest.java | 18 +++- .../observability/test/LgtmResourcesTest.java | 21 ++++- .../observability/test/LgtmTestBase.java | 6 +- .../test/support/DevResourcesTestProfile.java | 14 --- .../QuarkusTestResourceTestProfile.java | 14 --- .../src/test/resources/application.properties | 6 -- 12 files changed, 185 insertions(+), 130 deletions(-) create mode 100644 extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/ExtensionsCatalog.java delete mode 100644 integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/DevResourcesTestProfile.java delete mode 100644 integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QuarkusTestResourceTestProfile.java diff --git a/docs/src/main/asciidoc/observability-devservices-lgtm.adoc b/docs/src/main/asciidoc/observability-devservices-lgtm.adoc index e906607d41062..198871bbc2cc6 100644 --- a/docs/src/main/asciidoc/observability-devservices-lgtm.adoc +++ b/docs/src/main/asciidoc/observability-devservices-lgtm.adoc @@ -35,32 +35,28 @@ implementation("quarkus-observability-devservices-lgtm") === Metrics -If you're using https://micrometer.io/[MicroMeter's] Quarkiverse OTLP registry to push metrics to Grafana OTel LGTM, this is how you would define the export endpoint url; where `quarkus.otel-collector.url` is provided by the Observability Dev Services extension. +If you need metrics, add the Micrometer OTLP registry to your build file: -[source,properties] +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml ---- -# Micrometer OTLP registry -%test.quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics -%dev.quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics -%prod.quarkus.micrometer.export.otlp.url=http://localhost:4318/v1/metrics + + io.quarkiverse.micrometer.registry + quarkus-micrometer-registry-otlp + ---- -Please note that the `${quarkus.otel-collector.url}` value is generated by quarkus when it starts the Grafana OTel LGTM Dev Resource. - -Along OTel collector enpoint url, LGTM Dev Resource also provides a Grafana endpoint url - under `quarkus.grafana.url` property. - -In this case LGTM Dev Resource would be automatically started and used by Observability Dev Services. - -If you don't want all the hassle with Dev Services (e.g. lookup and re-use of existing running containers, etc) you can simply disable Dev Services and enable just Dev Resource usage: -[source,properties] +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle ---- -quarkus.observability.enabled=false -quarkus.observability.dev-resources=true +implementation("io.quarkiverse.micrometer.registry:quarkus-micrometer-registry-otlp") ---- +When using the https://micrometer.io/[MicroMeter's] Quarkiverse OTLP registry to push metrics to Grafana OTel LGTM, the `quarkus.micrometer.export.otlp.url` property is automatically set to OTel collector endpoint as seen from the outside of the docker container. + === Tracing -Just add the quarkus-opentelemetry extension to your build file: +For Tracing add the `quarkus-opentelemetry` extension to your build file: [source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] .pom.xml ---- @@ -76,34 +72,43 @@ Just add the quarkus-opentelemetry extension to your build file: implementation("io.quarkus:quarkus-opentelemetry") ---- -On the `application.properties` file, you can define: -[source,properties] ----- -# OpenTelemetry -quarkus.otel.exporter.otlp.traces.protocol=http/protobuf -%test.quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url} -%dev.quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url} -%prod.quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4318 ----- +The `quarkus.otel.exporter.otlp.traces.endpoint` property is automatically set to OTel collector endpoint as seen from the outside of the docker container. + +The `quarkus.otel.exporter.otlp.traces.protocol` is set to `http/protobuf`. + === Access Grafana Once you start your app in dev mode: include::{includes}/devtools/dev.adoc[] -You will see a message like this: +You will see a log entry like this: [source, log] ---- -Lgtm Dev Services Starting: 2024-02-20 11:15:24,540 INFO [org.tes.con.wai.str.HttpWaitStrategy] (build-32) /loving_chatelet: Waiting for 60 seconds for URL: http://localhost:61907/ (where port 61907 maps to container port 3000) +[io.qu.ob.de.ObservabilityDevServiceProcessor] (build-35) Dev Service Lgtm started, config: {grafana.endpoint=http://localhost:42797, quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:34711, otel-collector.url=localhost:34711, quarkus.micrometer.export.otlp.url=http://localhost:34711/v1/metrics, quarkus.otel.exporter.otlp.traces.protocol=http/protobuf} + ---- -Remember that Grafana is accessible in an ephemeral port, so you need to check the logs to see which port is being used. In this example, it's port 61907. +Remember that Grafana is accessible in an ephemeral port, so you need to check the logs to see which port is being used. In this example, the grafana endpoint is `grafana.endpoint=http://localhost:42797`. If you miss the message you can always check the port with this Docker command: [source, bash] ---- docker ps | grep grafana ---- + +=== Additional configuration + +This extension will configure your `quarkus-opentelemetry` and `quarkus-micrometer-registry-otlp` extensions to send data to the OTel Collector bundled with the Grafana OTel LGTM image. + +If you don't want all the hassle with Dev Services (e.g. lookup and re-use of existing running containers, etc) you can simply disable Dev Services and enable just Dev Resource usage: + +[source,properties] +---- +quarkus.observability.enabled=false +quarkus.observability.dev-resources=true +---- + === Tests And for the least 'auto-magical' usage in the tests, simply disable both (Dev Resources are already disabled by default): @@ -142,16 +147,6 @@ Use existing Quarkus MicroMeter OTLP registry implementation("io.quarkiverse.micrometer.registry:quarkus-micrometer-registry-otlp") ---- -On the test `application.properties` file, you need to define: -[source,properties] ----- -# Micrometer OTLP registry -quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics -# OpenTelemetry -quarkus.otel.exporter.otlp.traces.protocol=http/protobuf -quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url} ----- - Simply inject the Meter registry into your code -- it will periodically push metrics to Grafana LGTM's OTLP HTTP endpoint. [source, java] @@ -182,14 +177,14 @@ Where you can then check Grafana's datasource API for existing metrics data. ---- public class LgtmTestBase { - @ConfigProperty(name = "quarkus.grafana.url") - String url; // NOTE -- injected Grafana endpoint url! + @ConfigProperty(name = "grafana.endpoint") + String endpoint; // NOTE -- injected Grafana endpoint! @Test public void testTracing() { String response = RestAssured.get("/api/poke?f=100").body().asString(); System.out.println(response); - GrafanaClient client = new GrafanaClient("http://" + url, "admin", "admin"); + GrafanaClient client = new GrafanaClient(endpoint, "admin", "admin"); Awaitility.await().atMost(61, TimeUnit.SECONDS).until( client::user, u -> "admin".equals(u.login)); diff --git a/extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java b/extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java index 48c5932448d18..9061d75d6789b 100644 --- a/extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java +++ b/extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java @@ -12,8 +12,13 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; import org.jboss.logging.Logger; +import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; +import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; import io.quarkus.deployment.Feature; import io.quarkus.deployment.IsNormal; import io.quarkus.deployment.annotations.BuildProducer; @@ -28,6 +33,7 @@ import io.quarkus.deployment.console.StartupLogCompressor; import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig; import io.quarkus.deployment.logging.LoggingSetupBuildItem; +import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.devservices.common.ContainerLocator; import io.quarkus.observability.common.config.ContainerConfig; import io.quarkus.observability.common.config.ContainerConfigUtil; @@ -35,8 +41,10 @@ import io.quarkus.observability.devresource.Container; import io.quarkus.observability.devresource.DevResourceLifecycleManager; import io.quarkus.observability.devresource.DevResources; +import io.quarkus.observability.devresource.ExtensionsCatalog; import io.quarkus.observability.runtime.config.ObservabilityConfiguration; import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.metrics.MetricsFactory; @BuildSteps(onlyIfNot = IsNormal.class, onlyIf = { GlobalDevServicesConfig.Enabled.class, ObservabilityDevServiceProcessor.IsEnabled.class }) @@ -46,6 +54,7 @@ class ObservabilityDevServiceProcessor { private static final Map devServices = new ConcurrentHashMap<>(); private static final Map capturedDevServicesConfigurations = new ConcurrentHashMap<>(); private static final Map firstStart = new ConcurrentHashMap<>(); + public static final DotName OTLP_REGISTRY = DotName.createSimple("io.micrometer.registry.otlp.OtlpMeterRegistry"); public static class IsEnabled implements BooleanSupplier { ObservabilityConfiguration config; @@ -74,7 +83,10 @@ public void startContainers(LaunchModeBuildItem launchMode, CuratedApplicationShutdownBuildItem closeBuildItem, LoggingSetupBuildItem loggingSetupBuildItem, GlobalDevServicesConfig devServicesConfig, - BuildProducer services) { + BuildProducer services, + BeanArchiveIndexBuildItem indexBuildItem, + Capabilities capabilities, + Optional metricsConfiguration) { if (!configuration.enabled()) { log.infof("Observability dev services are disabled in config"); @@ -103,7 +115,11 @@ public void startContainers(LaunchModeBuildItem launchMode, // only do get, not remove, so it can be re-used DevServicesResultBuildItem.RunningDevService devService = devServices.get(devId); - ContainerConfig currentDevServicesConfiguration = dev.config(configuration); + ContainerConfig currentDevServicesConfiguration = dev.config( + configuration, + new ExtensionsCatalog( + capabilities.isPresent(Capability.OPENTELEMETRY_TRACER), + hasMicrometerOtlp(metricsConfiguration, indexBuildItem))); if (devService != null) { ContainerConfig capturedDevServicesConfiguration = capturedDevServicesConfigurations.get(devId); @@ -152,19 +168,22 @@ public void startContainers(LaunchModeBuildItem launchMode, } if (firstStart.computeIfAbsent(devId, x -> true)) { - Runnable closeTask = () -> { - DevServicesResultBuildItem.RunningDevService current = devServices.get(devId); - if (current != null) { - try { - current.close(); - } catch (Throwable t) { - log.errorf("Failed to stop %s container", devId, t); + Runnable closeTask = new Runnable() { + @Override + public void run() { + DevServicesResultBuildItem.RunningDevService current = devServices.get(devId); + if (current != null) { + try { + current.close(); + } catch (Throwable t) { + log.errorf("Failed to stop %s container", devId, t); + } } + firstStart.remove(devId); + //noinspection resource + devServices.remove(devId); + capturedDevServicesConfigurations.remove(devId); } - firstStart.remove(devId); - //noinspection resource - devServices.remove(devId); - capturedDevServicesConfigurations.remove(devId); }; closeBuildItem.addCloseTask(closeTask, true); } @@ -173,6 +192,18 @@ public void startContainers(LaunchModeBuildItem launchMode, }); } + private static boolean hasMicrometerOtlp(Optional metricsConfiguration, + BeanArchiveIndexBuildItem indexBuildItem) { + if (metricsConfiguration.isPresent() && + metricsConfiguration.get().metricsSupported(MetricsFactory.MICROMETER)) { + ClassInfo clazz = indexBuildItem.getIndex().getClassByName(OTLP_REGISTRY); + if (clazz != null) { + return true; + } + } + return false; + } + private DevServicesResultBuildItem.RunningDevService startContainer( String devId, DevResourceLifecycleManager dev, @@ -190,14 +221,17 @@ private DevServicesResultBuildItem.RunningDevService startContainer( return null; } - final Supplier defaultContainerSupplier = () -> { - Container container = dev.container(capturedDevServicesConfiguration, root); - timeout.ifPresent(container::withStartupTimeout); - Map config = dev.start(); - log.infof("Dev Service %s started, config: %s", devId, config); - return new DevServicesResultBuildItem.RunningDevService( - Feature.OBSERVABILITY.getName(), container.getContainerId(), - container.closeableCallback(capturedDevServicesConfiguration.serviceName()), config); + final Supplier defaultContainerSupplier = new Supplier() { + @Override + public DevServicesResultBuildItem.RunningDevService get() { + Container container = dev.container(capturedDevServicesConfiguration, root); + timeout.ifPresent(container::withStartupTimeout); + Map config = dev.start(); + log.infof("Dev Service %s started, config: %s", devId, config); + return new DevServicesResultBuildItem.RunningDevService( + Feature.OBSERVABILITY.getName(), container.getContainerId(), + container.closeableCallback(capturedDevServicesConfiguration.serviceName()), config); + } }; Map config = new LinkedHashMap<>(); // old config @@ -206,10 +240,13 @@ private DevServicesResultBuildItem.RunningDevService startContainer( .locateContainer( capturedDevServicesConfiguration.serviceName(), capturedDevServicesConfiguration.shared(), LaunchMode.current(), (p, ca) -> config.putAll(dev.config(p, ca.getHost(), ca.getPort()))) - .map(cid -> { - log.infof("Dev Service %s re-used, config: %s", devId, config); - return new DevServicesResultBuildItem.RunningDevService(Feature.OBSERVABILITY.getName(), cid, - null, config); + .map(new Function() { + @Override + public DevServicesResultBuildItem.RunningDevService apply(String cid) { + log.infof("Dev Service %s re-used, config: %s", devId, config); + return new DevServicesResultBuildItem.RunningDevService(Feature.OBSERVABILITY.getName(), cid, + null, config); + } }) .orElseGet(defaultContainerSupplier); } diff --git a/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/DevResourceLifecycleManager.java b/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/DevResourceLifecycleManager.java index be32e44084607..069bac13510ad 100644 --- a/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/DevResourceLifecycleManager.java +++ b/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/DevResourceLifecycleManager.java @@ -28,8 +28,18 @@ public interface DevResourceLifecycleManager extends * @param configuration main observability configuration * @return module's config */ + @Deprecated T config(ModulesConfiguration configuration); + /** + * Get resource's config from main observability configuration and extension catalog + * + * @param configuration main observability configuration + * @param catalog observability catalog. If OpenTelemetry or Micrometer are enabled. + * @return module's config + */ + T config(ModulesConfiguration configuration, ExtensionsCatalog catalog); + /** * Should we enable / start this dev resource. * e.g. we could already have actual service running diff --git a/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/ExtensionsCatalog.java b/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/ExtensionsCatalog.java new file mode 100644 index 0000000000000..00af2105c6c0b --- /dev/null +++ b/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/ExtensionsCatalog.java @@ -0,0 +1,8 @@ +package io.quarkus.observability.devresource; + +/** + * Relevant Observability extensions present. + */ +public record ExtensionsCatalog(boolean hasOpenTelemetry, + boolean hasMicrometerOtlp) { +} diff --git a/extensions/observability-devservices/testlibs/devresource-lgtm/src/main/java/io/quarkus/observability/devresource/lgtm/LgtmResource.java b/extensions/observability-devservices/testlibs/devresource-lgtm/src/main/java/io/quarkus/observability/devresource/lgtm/LgtmResource.java index 6273380d805ba..4fab76355a1ed 100644 --- a/extensions/observability-devservices/testlibs/devresource-lgtm/src/main/java/io/quarkus/observability/devresource/lgtm/LgtmResource.java +++ b/extensions/observability-devservices/testlibs/devresource-lgtm/src/main/java/io/quarkus/observability/devresource/lgtm/LgtmResource.java @@ -1,5 +1,6 @@ package io.quarkus.observability.devresource.lgtm; +import java.util.HashMap; import java.util.Map; import io.quarkus.observability.common.ContainerConstants; @@ -7,29 +8,39 @@ import io.quarkus.observability.common.config.ModulesConfiguration; import io.quarkus.observability.devresource.Container; import io.quarkus.observability.devresource.DevResourceLifecycleManager; +import io.quarkus.observability.devresource.ExtensionsCatalog; import io.quarkus.observability.devresource.testcontainers.ContainerResource; import io.quarkus.observability.testcontainers.LgtmContainer; public class LgtmResource extends ContainerResource { + private ExtensionsCatalog catalog; + @Override public LgtmConfig config(ModulesConfiguration configuration) { return configuration.lgtm(); } + @Override + public LgtmConfig config(ModulesConfiguration configuration, ExtensionsCatalog catalog) { + this.catalog = catalog; + return config(configuration); + } + @Override public Container container(LgtmConfig config, ModulesConfiguration root) { return set(new LgtmContainer(config)); } + // FIXME consolidate config methods. @Override public Map config(int privatePort, String host, int publicPort) { switch (privatePort) { case ContainerConstants.GRAFANA_PORT: - return Map.of("quarkus.grafana.url", String.format("%s:%s", host, publicPort)); + return Map.of("grafana.endpoint", String.format("http://%s:%s", host, publicPort)); case ContainerConstants.OTEL_GRPC_EXPORTER_PORT: case ContainerConstants.OTEL_HTTP_EXPORTER_PORT: - return Map.of("quarkus.otel-collector.url", String.format("%s:%s", host, publicPort)); + return Map.of("otel-collector.url", String.format("%s:%s", host, publicPort)); } return Map.of(); } @@ -42,9 +53,23 @@ protected LgtmContainer defaultContainer() { @Override public Map doStart() { String host = container.getHost(); - return Map.of( - "quarkus.grafana.url", String.format("%s:%s", host, container.getGrafanaPort()), - "quarkus.otel-collector.url", String.format("%s:%s", host, container.getOtlpPort())); + + //Set non Quarkus properties for convenience and testing. + Map containerConfigs = new HashMap<>(); + containerConfigs.put("grafana.endpoint", String.format("http://%s:%s", host, container.getGrafanaPort())); + containerConfigs.put("otel-collector.url", String.format("%s:%s", host, container.getOtlpPort())); + + // set relevant properties for Quarkus extensions directly + if (catalog != null && catalog.hasOpenTelemetry()) { + containerConfigs.put("quarkus.otel.exporter.otlp.traces.endpoint", + String.format("http://%s:%s", host, container.getOtlpPort())); + containerConfigs.put("quarkus.otel.exporter.otlp.traces.protocol", "http/protobuf"); + } + if (catalog != null && catalog.hasMicrometerOtlp()) { + containerConfigs.put("quarkus.micrometer.export.otlp.url", + String.format("http://%s:%s/v1/metrics", host, container.getOtlpPort())); + } + return containerConfigs; } @Override diff --git a/integration-tests/observability-lgtm/pom.xml b/integration-tests/observability-lgtm/pom.xml index 0abc8789677cb..ac48beae5ef5b 100644 --- a/integration-tests/observability-lgtm/pom.xml +++ b/integration-tests/observability-lgtm/pom.xml @@ -28,10 +28,6 @@ quarkus-micrometer-registry-otlp 3.2.4 - - io.quarkus - quarkus-micrometer - io.quarkus quarkus-opentelemetry @@ -88,19 +84,6 @@ - - io.quarkus - quarkus-micrometer-deployment - ${project.version} - pom - test - - - * - * - - - diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmLifecycleTest.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmLifecycleTest.java index 17a1b4488aec5..b6c476eec06e3 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmLifecycleTest.java +++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmLifecycleTest.java @@ -1,17 +1,31 @@ package io.quarkus.observability.test; +import java.util.Map; + import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import io.quarkus.observability.devresource.lgtm.LgtmResource; -import io.quarkus.observability.test.support.QuarkusTestResourceTestProfile; import io.quarkus.test.common.WithTestResource; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; import io.quarkus.test.junit.TestProfile; @QuarkusTest @WithTestResource(LgtmResource.class) -@TestProfile(QuarkusTestResourceTestProfile.class) +@TestProfile(LgtmLifecycleTest.TestResourceTestProfileOff.class) @DisabledOnOs(OS.WINDOWS) public class LgtmLifecycleTest extends LgtmTestBase { + + public static class TestResourceTestProfileOff implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of( + "quarkus.micrometer.export.otlp.url", "http://${otel-collector.url}/v1/metrics", + "quarkus.otel.exporter.otlp.traces.protocol", "http/protobuf", + "quarkus.otel.exporter.otlp.traces.endpoint", "http://${otel-collector.url}", + "quarkus.observability.dev-resources", "false", + "quarkus.observability.enabled", "false"); + } + } } diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesTest.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesTest.java index 6cd232235b38f..8383556181e5b 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesTest.java +++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmResourcesTest.java @@ -1,14 +1,31 @@ package io.quarkus.observability.test; +import java.util.Map; + import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; -import io.quarkus.observability.test.support.DevResourcesTestProfile; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; import io.quarkus.test.junit.TestProfile; +/** + * Simple case were you have to provide your own configuration + */ @QuarkusTest -@TestProfile(DevResourcesTestProfile.class) +@TestProfile(LgtmResourcesTest.DevResourcesTestProfileOnly.class) @DisabledOnOs(OS.WINDOWS) public class LgtmResourcesTest extends LgtmTestBase { + + public static class DevResourcesTestProfileOnly implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + return Map.of( + "quarkus.micrometer.export.otlp.url", "http://${otel-collector.url}/v1/metrics", + "quarkus.otel.exporter.otlp.traces.protocol", "http/protobuf", + "quarkus.otel.exporter.otlp.traces.endpoint", "http://${otel-collector.url}", + "quarkus.observability.dev-resources", "true", + "quarkus.observability.enabled", "false"); + } + } } diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java index 6223f25763584..ee49956a5a296 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java +++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestBase.java @@ -13,15 +13,15 @@ public abstract class LgtmTestBase { private final Logger log = Logger.getLogger(getClass()); - @ConfigProperty(name = "quarkus.grafana.url") - String url; + @ConfigProperty(name = "grafana.endpoint") + String endpoint; @Test public void testTracing() { log.info("Testing Grafana ..."); String response = RestAssured.get("/api/poke?f=100").body().asString(); log.info("Response: " + response); - GrafanaClient client = new GrafanaClient("http://" + url, "admin", "admin"); + GrafanaClient client = new GrafanaClient(endpoint, "admin", "admin"); Awaitility.await().atMost(61, TimeUnit.SECONDS).until( client::user, u -> "admin".equals(u.login)); diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/DevResourcesTestProfile.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/DevResourcesTestProfile.java deleted file mode 100644 index 1110a11073098..0000000000000 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/DevResourcesTestProfile.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.quarkus.observability.test.support; - -import java.util.Map; - -import io.quarkus.test.junit.QuarkusTestProfile; - -public class DevResourcesTestProfile implements QuarkusTestProfile { - @Override - public Map getConfigOverrides() { - return Map.of( - "quarkus.observability.dev-resources", "true", - "quarkus.observability.enabled", "false"); - } -} diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QuarkusTestResourceTestProfile.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QuarkusTestResourceTestProfile.java deleted file mode 100644 index b60772d61d550..0000000000000 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QuarkusTestResourceTestProfile.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.quarkus.observability.test.support; - -import java.util.Map; - -import io.quarkus.test.junit.QuarkusTestProfile; - -public class QuarkusTestResourceTestProfile implements QuarkusTestProfile { - @Override - public Map getConfigOverrides() { - return Map.of( - "quarkus.observability.dev-resources", "false", - "quarkus.observability.enabled", "false"); - } -} diff --git a/integration-tests/observability-lgtm/src/test/resources/application.properties b/integration-tests/observability-lgtm/src/test/resources/application.properties index d3f0cfca1f442..e1e468769a002 100644 --- a/integration-tests/observability-lgtm/src/test/resources/application.properties +++ b/integration-tests/observability-lgtm/src/test/resources/application.properties @@ -8,9 +8,3 @@ quarkus.log.category."io.quarkus.devservices".level=DEBUG quarkus.micrometer.export.otlp.enabled=true quarkus.micrometer.export.otlp.publish=true quarkus.micrometer.export.otlp.step=PT5S -quarkus.micrometer.export.otlp.default-registry=true -quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics - -#opentelemetry -quarkus.otel.exporter.otlp.traces.protocol=http/protobuf -quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url} From 36c168609cac2614d79b91478563f44cf229d013 Mon Sep 17 00:00:00 2001 From: Marek Skacelik Date: Thu, 4 Jul 2024 13:39:48 +0200 Subject: [PATCH 24/94] SmallRye GraphQL: added \r to PATTERN_NEWLINE_OR_TAB --- .../graphql/runtime/SmallRyeGraphQLExecutionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java index d0ffbc1901c95..7d97c18bd090c 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java @@ -309,7 +309,7 @@ private String readQueryParameter(RoutingContext ctx, String parameterName) { return null; } - private static final Pattern PATTERN_NEWLINE_OR_TAB = Pattern.compile("\\n|\\t"); + private static final Pattern PATTERN_NEWLINE_OR_TAB = Pattern.compile("\\n|\\t|\\r"); /** * Strip away unescaped tabs and line breaks from the incoming JSON document so that it can be From e5a1efab98ab6b4764362b4ed106e551f2102afe Mon Sep 17 00:00:00 2001 From: Katia Aresti Date: Thu, 4 Jul 2024 13:49:40 +0200 Subject: [PATCH 25/94] Infinispan Extension adds SASL to service providers --- .../deployment/InfinispanClientProcessor.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/InfinispanClientProcessor.java b/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/InfinispanClientProcessor.java index a52ff559827ff..47a397108eaa1 100644 --- a/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/InfinispanClientProcessor.java +++ b/extensions/infinispan-client/deployment/src/main/java/io/quarkus/infinispan/client/deployment/InfinispanClientProcessor.java @@ -303,6 +303,22 @@ InfinispanPropertiesBuildItem setup(ApplicationArchivesBuildItem applicationArch "org.infinispan.client.hotrod.impl.consistenthash.SegmentConsistentHash") .build()); + // Elytron Classes + String[] elytronClasses = new String[] { + "org.wildfly.security.sasl.plain.PlainSaslClientFactory", + "org.wildfly.security.sasl.scram.ScramSaslClientFactory", + "org.wildfly.security.credential.BearerTokenCredential", + "org.wildfly.security.credential.GSSKerberosCredential", + "org.wildfly.security.credential.KeyPairCredential", + "org.wildfly.security.credential.PasswordCredential", + "org.wildfly.security.credential.PublicKeyCredential", + "org.wildfly.security.credential.SecretKeyCredential", + "org.wildfly.security.credential.SSHCredential", + "org.wildfly.security.credential.X509CertificateChainPrivateCredential", + "org.wildfly.security.credential.X509CertificateChainPublicCredential" + }; + + reflectiveClass.produce(ReflectiveClassBuildItem.builder(elytronClasses).build()); return new InfinispanPropertiesBuildItem(propertiesMap); } From 76bde942113addc4867e919ade494ad40be5018c Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Thu, 4 Jul 2024 10:57:01 -0300 Subject: [PATCH 26/94] Use JDK17 features in LoopSectionHelper --- .../io/quarkus/qute/LoopSectionHelper.java | 89 ++++++++----------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java index 43d7abe826191..d23bdbc5f763f 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java @@ -77,20 +77,20 @@ public CompletionStage resolve(SectionResolutionContext context) { private static int extractSize(Object it) { // Note that we intentionally use "instanceof" to test interfaces as the last resort in order to mitigate the "type pollution" // See https://github.com/RedHatPerf/type-pollution-agent for more information - if (it instanceof AbstractCollection) { - return ((AbstractCollection) it).size(); - } else if (it instanceof AbstractMap) { - return ((AbstractMap) it).size(); + if (it instanceof AbstractCollection collection) { + return collection.size(); + } else if (it instanceof AbstractMap map) { + return map.size(); } else if (it.getClass().isArray()) { return Array.getLength(it); - } else if (it instanceof Integer) { - return ((Integer) it); - } else if (it instanceof Long) { - return (((Long) it).intValue()); - } else if (it instanceof Collection) { - return ((Collection) it).size(); - } else if (it instanceof Map) { - return ((Map) it).size(); + } else if (it instanceof Integer integer) { + return integer; + } else if (it instanceof Long longValue) { + return longValue.intValue(); + } else if (it instanceof Collection collection) { + return collection.size(); + } else if (it instanceof Map map) { + return map.size(); } return 10; } @@ -98,14 +98,14 @@ private static int extractSize(Object it) { private Iterator extractIterator(Object it) { // Note that we intentionally use "instanceof" to test interfaces as the last resort in order to mitigate the "type pollution" // See https://github.com/RedHatPerf/type-pollution-agent for more information - if (it instanceof AbstractCollection) { - return ((AbstractCollection) it).iterator(); - } else if (it instanceof AbstractMap) { - return ((AbstractMap) it).entrySet().iterator(); - } else if (it instanceof Integer) { - return IntStream.rangeClosed(1, (Integer) it).iterator(); - } else if (it instanceof Long) { - return LongStream.rangeClosed(1, (Long) it).iterator(); + if (it instanceof AbstractCollection col) { + return col.iterator(); + } else if (it instanceof AbstractMap map) { + return map.entrySet().iterator(); + } else if (it instanceof Integer integer) { + return IntStream.rangeClosed(1, integer).iterator(); + } else if (it instanceof Long longValue) { + return LongStream.rangeClosed(1, longValue).iterator(); } else if (it.getClass().isArray()) { int length = Array.getLength(it); List elements = new ArrayList<>(length); @@ -114,14 +114,14 @@ private Iterator extractIterator(Object it) { elements.add(Array.get(it, i)); } return elements.iterator(); - } else if (it instanceof Iterable) { - return ((Iterable) it).iterator(); - } else if (it instanceof Iterator) { - return (Iterator) it; - } else if (it instanceof Map) { - return ((Map) it).entrySet().iterator(); - } else if (it instanceof Stream) { - return ((Stream) it).sequential().iterator(); + } else if (it instanceof Iterable iterable) { + return iterable.iterator(); + } else if (it instanceof Iterator iterator) { + return iterator; + } else if (it instanceof Map map) { + return map.entrySet().iterator(); + } else if (it instanceof Stream stream) { + return stream.sequential().iterator(); } else { TemplateException.Builder builder; if (Results.isNotFound(it)) { @@ -299,28 +299,17 @@ public CompletionStage getAsync(String key) { } // Iteration metadata final int count = index + 1; - switch (key) { - case "count": - return CompletedStage.of(count); - case "index": - return CompletedStage.of(index); - case "indexParity": - return count % 2 == 0 ? EVEN : ODD; - case "hasNext": - return hasNext ? Results.TRUE : Results.FALSE; - case "isLast": - return hasNext ? Results.FALSE : Results.TRUE; - case "isFirst": - return index == 0 ? Results.TRUE : Results.FALSE; - case "isOdd": - case "odd": - return count % 2 != 0 ? Results.TRUE : Results.FALSE; - case "isEven": - case "even": - return count % 2 == 0 ? Results.TRUE : Results.FALSE; - default: - return Results.notFound(key); - } + return switch (key) { + case "count" -> CompletedStage.of(count); + case "index" -> CompletedStage.of(index); + case "indexParity" -> count % 2 == 0 ? EVEN : ODD; + case "hasNext" -> hasNext ? Results.TRUE : Results.FALSE; + case "isLast" -> hasNext ? Results.FALSE : Results.TRUE; + case "isFirst" -> index == 0 ? Results.TRUE : Results.FALSE; + case "isOdd", "odd" -> count % 2 != 0 ? Results.TRUE : Results.FALSE; + case "isEven", "even" -> count % 2 == 0 ? Results.TRUE : Results.FALSE; + default -> Results.notFound(key); + }; } @Override From 7c899fe8bc953e9fd897a74b2a9a2b735b800b3f Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 4 Jul 2024 14:44:22 +0200 Subject: [PATCH 27/94] Qute: extend the `@EngineConfiguration` support to ParserHook Co-authored-by: George Gastaldi --- docs/src/main/asciidoc/qute-reference.adoc | 2 +- .../io/quarkus/qute/deployment/Names.java | 2 + .../qute/deployment/QuteProcessor.java | 68 +++++++++++------- .../parserhook/CustomParserHookBuildTest.java | 71 +++++++++++++++++++ .../CustomParserHookRuntimeTest.java | 48 +++++++++++++ .../section/WrongTargetConstructorTest.java | 2 +- .../section/WrongTargetNestedTypeTest.java | 2 +- .../quarkus/qute/runtime/EngineProducer.java | 7 +- .../io/quarkus/qute/EngineConfiguration.java | 19 +++-- .../io/quarkus/qute/NamespaceResolver.java | 1 + .../java/io/quarkus/qute/ParserHelper.java | 5 ++ .../main/java/io/quarkus/qute/ParserHook.java | 3 + .../io/quarkus/qute/SectionHelperFactory.java | 1 + .../java/io/quarkus/qute/ValueResolver.java | 2 + 14 files changed, 192 insertions(+), 41 deletions(-) create mode 100644 extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java create mode 100644 extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookRuntimeTest.java diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc index dff500a62efb2..43687701a925f 100644 --- a/docs/src/main/asciidoc/qute-reference.adoc +++ b/docs/src/main/asciidoc/qute-reference.adoc @@ -1626,7 +1626,7 @@ public class CustomSectionFactory implements SectionHelperFactory Validate that `foo` parameter is always present; e.g. `{#custom foo='bar' /}` is ok but `{#custom /}` results in a build failure. <4> Use the injected `Service` during rendering. -The `@EngineConfiguration` annotation can be also used to register ``ValueResolver``s and ``NamespaceResolver``s. +TIP: The `@EngineConfiguration` annotation can be also used to register `ValueResolver`, `NamespaceResolver` and `ParserHook` components. [[template-locator-registration]] ==== Template Locator Registration diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/Names.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/Names.java index aeef20ef729b7..e0d2afbf4a999 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/Names.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/Names.java @@ -14,6 +14,7 @@ import io.quarkus.qute.Locate.Locates; import io.quarkus.qute.Location; import io.quarkus.qute.NamespaceResolver; +import io.quarkus.qute.ParserHook; import io.quarkus.qute.SectionHelperFactory; import io.quarkus.qute.Template; import io.quarkus.qute.TemplateEnum; @@ -52,6 +53,7 @@ final class Names { static final DotName SECTION_HELPER_FACTORY = DotName.createSimple(SectionHelperFactory.class.getName()); static final DotName VALUE_RESOLVER = DotName.createSimple(ValueResolver.class.getName()); static final DotName NAMESPACE_RESOLVER = DotName.createSimple(NamespaceResolver.class.getName()); + static final DotName PARSER_HOOK = DotName.createSimple(ParserHook.class); private Names() { } diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index f591dcfb3cf27..f0958213ff94a 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -618,22 +618,32 @@ TemplatesAnalysisBuildItem analyzeTemplates(List template } } - // Register additional section factories + // Register additional section factories and parser hooks if (engineConfigurations.isPresent()) { - Collection sectionFactories = engineConfigurations.get().getConfigurations().stream() - .filter(c -> Types.isImplementorOf(c, Names.SECTION_HELPER_FACTORY, beanArchiveIndex.getIndex())) - .collect(Collectors.toList()); // Use the deployment class loader - it can load application classes; it's non-persistent and isolated ClassLoader tccl = Thread.currentThread().getContextClassLoader(); - for (ClassInfo factoryClass : sectionFactories) { - try { - Class sectionHelperFactoryClass = tccl.loadClass(factoryClass.toString()); - SectionHelperFactory factory = (SectionHelperFactory) sectionHelperFactoryClass - .getDeclaredConstructor().newInstance(); - builder.addSectionHelper(factory); - LOGGER.debugf("SectionHelperFactory registered during template analysis: " + factoryClass); - } catch (Exception e) { - throw new IllegalStateException("Unable to instantiate SectionHelperFactory: " + factoryClass, e); + IndexView index = beanArchiveIndex.getIndex(); + + for (ClassInfo engineConfigClass : engineConfigurations.get().getConfigurations()) { + if (Types.isImplementorOf(engineConfigClass, Names.SECTION_HELPER_FACTORY, index)) { + try { + Class sectionHelperFactoryClass = tccl.loadClass(engineConfigClass.toString()); + SectionHelperFactory factory = (SectionHelperFactory) sectionHelperFactoryClass + .getDeclaredConstructor().newInstance(); + builder.addSectionHelper(factory); + LOGGER.debugf("SectionHelperFactory registered during template analysis: %s", engineConfigClass); + } catch (Exception e) { + throw new IllegalStateException("Unable to instantiate SectionHelperFactory: " + engineConfigClass, e); + } + } else if (Types.isImplementorOf(engineConfigClass, Names.PARSER_HOOK, index)) { + try { + Class parserHookClass = tccl.loadClass(engineConfigClass.toString()); + ParserHook parserHook = (ParserHook) parserHookClass.getDeclaredConstructor().newInstance(); + builder.addParserHook(parserHook); + LOGGER.debugf("ParserHook registered during template analysis: %s", engineConfigClass); + } catch (Exception e) { + throw new IllegalStateException("Unable to instantiate ParserHook: " + engineConfigClass, e); + } } } } @@ -2281,28 +2291,32 @@ void collectEngineConfigurations( for (AnnotationInstance annotation : engineConfigAnnotations) { AnnotationTarget target = annotation.target(); if (target.kind() == Kind.CLASS) { - ClassInfo targetClass = target.asClass(); + ClassInfo clazz = target.asClass(); - if (targetClass.nestingType() != NestingType.TOP_LEVEL - && (targetClass.nestingType() != NestingType.INNER || !Modifier.isStatic(targetClass.flags()))) { + if (clazz.isAbstract() + || clazz.isInterface() + || (clazz.nestingType() != NestingType.TOP_LEVEL + && (clazz.nestingType() != NestingType.INNER || !Modifier.isStatic(clazz.flags())))) { validationErrors.produce( new ValidationErrorBuildItem( new TemplateException(String.format( - "Only top-level and static nested classes may be annotated with @%s: %s", - EngineConfiguration.class.getSimpleName(), targetClass.name())))); - } else if (Types.isImplementorOf(targetClass, Names.SECTION_HELPER_FACTORY, index)) { - if (targetClass.hasNoArgsConstructor()) { - engineConfigClasses.add(targetClass); + "Only non-abstract, top-level or static nested classes may be annotated with @%s: %s", + EngineConfiguration.class.getSimpleName(), clazz.name())))); + } else if (Types.isImplementorOf(clazz, Names.SECTION_HELPER_FACTORY, index) + || Types.isImplementorOf(clazz, Names.PARSER_HOOK, index)) { + if (clazz.hasNoArgsConstructor() + && Modifier.isPublic(clazz.flags())) { + engineConfigClasses.add(clazz); } else { validationErrors.produce( new ValidationErrorBuildItem( new TemplateException(String.format( - "A class annotated with @%s that also implements io.quarkus.qute.SectionHelperFactory must declare a no-args constructor: %s", - EngineConfiguration.class.getSimpleName(), targetClass.name())))); + "A class annotated with @%s that also implements SectionHelperFactory or ParserHelper must be public and declare a no-args constructor: %s", + EngineConfiguration.class.getSimpleName(), clazz.name())))); } - } else if (Types.isImplementorOf(targetClass, Names.VALUE_RESOLVER, index) - || Types.isImplementorOf(targetClass, Names.NAMESPACE_RESOLVER, index)) { - engineConfigClasses.add(targetClass); + } else if (Types.isImplementorOf(clazz, Names.VALUE_RESOLVER, index) + || Types.isImplementorOf(clazz, Names.NAMESPACE_RESOLVER, index)) { + engineConfigClasses.add(clazz); } else { validationErrors.produce( new ValidationErrorBuildItem( @@ -2312,7 +2326,7 @@ void collectEngineConfigurations( new String[] { SectionHelperFactory.class.getName(), ValueResolver.class.getName(), NamespaceResolver.class.getName() }), - targetClass.name())))); + clazz.name())))); } } } diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java new file mode 100644 index 0000000000000..b2b7f2af94323 --- /dev/null +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java @@ -0,0 +1,71 @@ +package io.quarkus.qute.deployment.engineconfigurations.parserhook; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import jakarta.inject.Inject; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.qute.Engine; +import io.quarkus.qute.EngineConfiguration; +import io.quarkus.qute.ParserHelper; +import io.quarkus.qute.ParserHook; +import io.quarkus.qute.TemplateException; +import io.quarkus.test.QuarkusUnitTest; + +public class CustomParserHookBuildTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot( + root -> root.addClasses(CustomParserHook.class, Foo.class) + .addAsResource(new StringAsset("{foo.bar}"), "templates/foo.html")) + .assertException(t -> { + Throwable e = t; + TemplateException te = null; + while (e != null) { + if (e instanceof TemplateException) { + te = (TemplateException) e; + break; + } + e = e.getCause(); + } + assertNotNull(te); + assertTrue(te.getMessage().contains("Found incorrect expressions (1)"), te.getMessage()); + assertTrue(te.getMessage().contains("{foo.bar}"), te.getMessage()); + });; + + @Inject + Engine engine; + + @Test + public void test() { + fail(); + } + + @EngineConfiguration + public static class CustomParserHook implements ParserHook { + + @Override + public void beforeParsing(ParserHelper helper) { + if (helper.getTemplateId().contains("foo")) { + helper.addParameter("foo", Foo.class.getName()); + } + } + + } + + public static class Foo { + + // package-private method is ignored + String bar() { + return null; + } + + } + +} diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookRuntimeTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookRuntimeTest.java new file mode 100644 index 0000000000000..859389e76b0d0 --- /dev/null +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookRuntimeTest.java @@ -0,0 +1,48 @@ +package io.quarkus.qute.deployment.engineconfigurations.parserhook; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jakarta.inject.Inject; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.qute.Engine; +import io.quarkus.qute.EngineConfiguration; +import io.quarkus.qute.ParserHelper; +import io.quarkus.qute.ParserHook; +import io.quarkus.test.QuarkusUnitTest; + +public class CustomParserHookRuntimeTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot( + root -> root.addClasses(CustomParserHook.class) + .addAsResource(new StringAsset("{foo}"), "templates/foo.html")); + + @Inject + Engine engine; + + @Test + public void testParserHook() { + assertEquals("42", engine.getTemplate("foo").data("bar", 42).render()); + } + + @EngineConfiguration + public static class CustomParserHook implements ParserHook { + + @Inject + Engine engine; + + @Override + public void beforeParsing(ParserHelper helper) { + if (helper.getTemplateId().contains("foo") && engine != null) { + helper.addContentFilter(c -> "{bar}"); + } + } + + } + +} diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/section/WrongTargetConstructorTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/section/WrongTargetConstructorTest.java index 82e3feb4f4655..7a2dd4255b0af 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/section/WrongTargetConstructorTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/section/WrongTargetConstructorTest.java @@ -22,7 +22,7 @@ public class WrongTargetConstructorTest { Throwable rootCause = ExceptionUtil.getRootCause(t); if (rootCause instanceof TemplateException) { assertTrue(rootCause.getMessage().contains( - "A class annotated with @EngineConfiguration that also implements io.quarkus.qute.SectionHelperFactory must declare a no-args constructor:"), + "A class annotated with @EngineConfiguration that also implements SectionHelperFactory or ParserHelper must be public and declare a no-args constructor"), rootCause.toString()); } else { fail("No TemplateException thrown: " + t); diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/section/WrongTargetNestedTypeTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/section/WrongTargetNestedTypeTest.java index 2a8b66d6804ca..7fe1a5eae1bd4 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/section/WrongTargetNestedTypeTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/section/WrongTargetNestedTypeTest.java @@ -22,7 +22,7 @@ public class WrongTargetNestedTypeTest { Throwable rootCause = ExceptionUtil.getRootCause(t); if (rootCause instanceof TemplateException) { assertTrue(rootCause.getMessage().contains( - "Only top-level and static nested classes may be annotated with @EngineConfiguration:"), + "Only non-abstract, top-level or static nested classes may be annotated with @EngineConfiguration:"), rootCause.toString()); } else { fail("No TemplateException thrown: " + t); diff --git a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java index 5e513a98d04c5..bc3d9c95225df 100644 --- a/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java +++ b/extensions/qute/runtime/src/main/java/io/quarkus/qute/runtime/EngineProducer.java @@ -41,6 +41,7 @@ import io.quarkus.qute.Expression; import io.quarkus.qute.HtmlEscaper; import io.quarkus.qute.NamespaceResolver; +import io.quarkus.qute.ParserHook; import io.quarkus.qute.Qute; import io.quarkus.qute.ReflectionValueResolver; import io.quarkus.qute.Resolver; @@ -89,7 +90,7 @@ public EngineProducer(QuteContext context, QuteConfig config, QuteRuntimeConfig Event builderReady, Event engineReady, ContentTypes contentTypes, LaunchMode launchMode, LocalesBuildTimeConfig locales, @All List locators, @All List> sectionHelperFactories, @All List valueResolvers, - @All List namespaceResolvers) { + @All List namespaceResolvers, @All List parserHooks) { this.contentTypes = contentTypes; this.suffixes = config.suffixes; this.templateRoots = context.getTemplateRoots(); @@ -205,6 +206,10 @@ public EngineProducer(QuteContext context, QuteConfig config, QuteRuntimeConfig builder.addLocator(this::locate); registerCustomLocators(builder, locators); + // Add parser hooks + for (ParserHook parserHook : parserHooks) { + builder.addParserHook(parserHook); + } // Add a special parser hook for Qute.fmt() methods builder.addParserHook(new Qute.IndexedArgumentsParserHook()); diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/EngineConfiguration.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/EngineConfiguration.java index f103b54208d46..7457801fe8d80 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/EngineConfiguration.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/EngineConfiguration.java @@ -13,22 +13,21 @@ * * Enables registration of additional components to the preconfigured {@link Engine}. *

- * A top-level or static nested class that implements one of the supported component interfaces and is annotated with - * this - * annotation: + * A non-abstract, top-level or static nested class that implements one of the supported component interfaces and + * is annotated + * with this annotation: *

    *
  • can be used during validation of templates at build time,
  • *
  • is automatically registered at runtime (a) to the preconfigured {@link Engine} and (b) as a CDI bean.
  • *
* - * The list of supported component interfaces includes: {@link SectionHelperFactory}, {@link ValueResolver} and - * {@link NamespaceResolver}. + * The list of supported component interfaces includes: {@link SectionHelperFactory}, {@link ValueResolver}, + * {@link NamespaceResolver} and {@link ParserHook}. *

- * An annotated class that implements {@link SectionHelperFactory} must declare a no-args constructor that is used to - * instantiate the component at build time. - *

- * At runtime, a CDI bean instance is used. This means that the factory can define injection points. If no CDI scope is defined - * then {@code javax.enterprise.context.Dependent} is used. + * An annotated class that implements {@link SectionHelperFactory} or {@link ParserHook} must be public and declare a no-args + * constructor that is used to instantiate the component at build time. At runtime, a CDI bean instance is used. This means that + * the factory can declare injection points. However, these injection points are only injected at runtime. If no CDI scope is + * defined then {@code javax.enterprise.context.Dependent} is used. * * @see EngineBuilder#addSectionHelper(SectionHelperFactory) * @see EngineBuilder#addValueResolver(ValueResolver) diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/NamespaceResolver.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/NamespaceResolver.java index 04301d1546e07..16e4f1455a8e1 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/NamespaceResolver.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/NamespaceResolver.java @@ -10,6 +10,7 @@ * For example the expression {@code data:colors} declares a namespace {@code data}. * * @see EngineBuilder#addNamespaceResolver(NamespaceResolver) + * @see EngineConfiguration */ public interface NamespaceResolver extends Resolver, WithPriority { diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserHelper.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserHelper.java index d44fb05de3c85..1bf0e915a0433 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserHelper.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserHelper.java @@ -17,6 +17,11 @@ public interface ParserHelper { /** * Adds an implicit parameter declaration. This is an alternative approach to explicit parameter * declarations used directly in the templates, e.g. {@org.acme.Foo foo}. + *

+ * The type is a fully qualified class name. The package name is optional for JDK types from the {@code java.lang} + * package. Parameterized types are supported, however wildcards are always ignored - only the upper/lower bound is taken + * into account. For example, the type info {@code java.util.List} is recognized as + * {@code java.util.List list}. Type variables are not handled in a special way and should never be used. * * @param name * @param type diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserHook.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserHook.java index 8b02e424f3a68..78b1e60af4218 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserHook.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/ParserHook.java @@ -2,6 +2,9 @@ /** * This component can be used to hook into the parser logic. + * + * @see EngineBuilder#addParserHook(ParserHook) + * @see EngineConfiguration */ public interface ParserHook { diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionHelperFactory.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionHelperFactory.java index 849170227e8ec..7e81f7b583a70 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionHelperFactory.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionHelperFactory.java @@ -14,6 +14,7 @@ * Factory to create a new {@link SectionHelper} based on the {@link SectionInitContextImpl}. * * @see EngineBuilder#addSectionHelper(SectionHelperFactory) + * @see EngineConfiguration */ public interface SectionHelperFactory { diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/ValueResolver.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/ValueResolver.java index 267d4f2529db1..918b82f31f496 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/ValueResolver.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/ValueResolver.java @@ -7,6 +7,8 @@ * to resolve the data. If {@link Results#isNotFound(Object)} is returned the next available resolver is tried. * * @see EvalContext + * @see EngineBuilder#addValueResolver(ValueResolver) + * @see EngineConfiguration */ public interface ValueResolver extends Resolver, WithPriority { From 69538fc93d71bc3bfbc95a8e9e8501b0c40136f0 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 4 Jul 2024 15:04:02 +0200 Subject: [PATCH 28/94] Qute: introduce ValidationParserHookBuildItem - that can be used to hook into the parser logic during validation at build time --- .../qute/deployment/QuteProcessor.java | 11 ++- .../ValidationParserHookBuildItem.java | 30 ++++++++ .../parserhook/CustomParserHookBuildTest.java | 6 -- .../parserhook/ValidationHookTest.java | 76 +++++++++++++++++++ 4 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/ValidationParserHookBuildItem.java create mode 100644 extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/ValidationHookTest.java diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index f0958213ff94a..f40f1eeb724aa 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -591,8 +591,11 @@ private boolean isNotLocatedByCustomTemplateLocator( @BuildStep TemplatesAnalysisBuildItem analyzeTemplates(List templatePaths, - TemplateFilePathsBuildItem filePaths, List checkedTemplates, - List messageBundleMethods, List globals, QuteConfig config, + TemplateFilePathsBuildItem filePaths, + List checkedTemplates, + List messageBundleMethods, + List globals, QuteConfig config, + List validationParserHooks, Optional engineConfigurations, BeanArchiveIndexBuildItem beanArchiveIndex, BuildProducer checkedFragmentValidations) { @@ -719,6 +722,10 @@ public void beforeParsing(ParserHelper parserHelper) { parserHelper.addParameter(UserTagSectionHelper.Factory.ARGS, UserTagSectionHelper.Arguments.class.getName()); } + + for (ValidationParserHookBuildItem hook : validationParserHooks) { + hook.accept(parserHelper); + } } // If needed add params to message bundle templates diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/ValidationParserHookBuildItem.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/ValidationParserHookBuildItem.java new file mode 100644 index 0000000000000..656a17a4d0d09 --- /dev/null +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/ValidationParserHookBuildItem.java @@ -0,0 +1,30 @@ +package io.quarkus.qute.deployment; + +import java.util.Objects; +import java.util.function.Consumer; + +import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.qute.ParserHelper; + +/** + * This build item can be used to hook into the parser logic during validation at build time. + *

+ * Validation parser hooks are never used at runtime. + */ +public final class ValidationParserHookBuildItem extends MultiBuildItem { + + private final Consumer hook; + + public ValidationParserHookBuildItem(Consumer hook) { + this.hook = Objects.requireNonNull(hook); + } + + public Consumer getHook() { + return hook; + } + + public void accept(ParserHelper helper) { + hook.accept(helper); + } + +} diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java index b2b7f2af94323..62b4192bcb31c 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/CustomParserHookBuildTest.java @@ -4,13 +4,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import jakarta.inject.Inject; - import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import io.quarkus.qute.Engine; import io.quarkus.qute.EngineConfiguration; import io.quarkus.qute.ParserHelper; import io.quarkus.qute.ParserHook; @@ -39,9 +36,6 @@ public class CustomParserHookBuildTest { assertTrue(te.getMessage().contains("{foo.bar}"), te.getMessage()); });; - @Inject - Engine engine; - @Test public void test() { fail(); diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/ValidationHookTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/ValidationHookTest.java new file mode 100644 index 0000000000000..e76dc7b8c851a --- /dev/null +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/engineconfigurations/parserhook/ValidationHookTest.java @@ -0,0 +1,76 @@ +package io.quarkus.qute.deployment.engineconfigurations.parserhook; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.function.Consumer; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.builder.BuildChainBuilder; +import io.quarkus.builder.BuildContext; +import io.quarkus.builder.BuildStep; +import io.quarkus.qute.TemplateException; +import io.quarkus.qute.deployment.ValidationParserHookBuildItem; +import io.quarkus.test.QuarkusUnitTest; + +public class ValidationHookTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot( + root -> root.addClasses(Foo.class) + .addAsResource(new StringAsset("{foo.bar}"), "templates/foo.html")) + .assertException(t -> { + Throwable e = t; + TemplateException te = null; + while (e != null) { + if (e instanceof TemplateException) { + te = (TemplateException) e; + break; + } + e = e.getCause(); + } + assertNotNull(te); + assertTrue(te.getMessage().contains("Found incorrect expressions (1)"), te.getMessage()); + assertTrue(te.getMessage().contains("{foo.bar}"), te.getMessage()); + }).addBuildChainCustomizer(buildCustomizer()); + + static Consumer buildCustomizer() { + return new Consumer() { + @Override + public void accept(BuildChainBuilder builder) { + builder.addBuildStep(new BuildStep() { + @Override + public void execute(BuildContext context) { + context.produce(new ValidationParserHookBuildItem(helper -> { + if (helper.getTemplateId().contains("foo")) { + helper.addParameter("foo", Foo.class.getName()); + } + })); + } + }).produces(ValidationParserHookBuildItem.class) + .build(); + + } + }; + } + + @Test + public void test() { + fail(); + } + + public static class Foo { + + // package-private method is ignored + String bar() { + return null; + } + + } + +} From 4a0da3dcf0de0580e4070b7b89f41d4d5c80cb1e Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Tue, 2 Jul 2024 15:25:37 +0200 Subject: [PATCH 29/94] Allow to use custom quarkus udpate recipe coords --- .../io/quarkus/cli/build/GradleRunner.java | 8 ++--- .../io/quarkus/cli/build/MavenRunner.java | 8 ++--- .../io/quarkus/cli/update/RewriteGroup.java | 8 ++--- .../quarkus/gradle/tasks/QuarkusUpdate.java | 32 +++++++++---------- .../java/io/quarkus/maven/UpdateMojo.java | 23 ++++++------- .../devtools/commands/UpdateProject.java | 14 ++++---- .../handlers/UpdateProjectCommandHandler.java | 15 ++++----- .../update/rewrite/QuarkusUpdates.java | 11 ++++--- .../rewrite/QuarkusUpdatesRepository.java | 23 +++++-------- 9 files changed, 69 insertions(+), 73 deletions(-) diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java index ee4d6eb6253ea..adfe14df86ec6 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/GradleRunner.java @@ -170,11 +170,11 @@ public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, Rew if (rewrite.pluginVersion != null) { args.add("--rewritePluginVersion=" + rewrite.pluginVersion); } - if (rewrite.updateRecipesVersion != null) { - args.add("--updateRecipesVersion=" + rewrite.updateRecipesVersion); + if (rewrite.quarkusUpdateRecipes != null) { + args.add("--quarkusUpdateRecipes=" + rewrite.quarkusUpdateRecipes); } - if (rewrite.additionalUpdateRecipeCoords != null) { - args.add("--additionalUpdateRecipeCoords=" + rewrite.additionalUpdateRecipeCoords); + if (rewrite.additionalUpdateRecipes != null) { + args.add("--additionalUpdateRecipes=" + rewrite.additionalUpdateRecipes); } if (rewrite.noRewrite) { args.add("--noRewrite"); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java index b495e8b78f7a4..948feb9f3db81 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/build/MavenRunner.java @@ -173,11 +173,11 @@ public Integer updateProject(TargetQuarkusVersionGroup targetQuarkusVersion, Rew if (rewrite.pluginVersion != null) { args.add("-DrewritePluginVersion=" + rewrite.pluginVersion); } - if (rewrite.updateRecipesVersion != null) { - args.add("-DupdateRecipesVersion=" + rewrite.updateRecipesVersion); + if (rewrite.quarkusUpdateRecipes != null) { + args.add("-DquarkusUpdateRecipes=" + rewrite.quarkusUpdateRecipes); } - if (rewrite.additionalUpdateRecipeCoords != null) { - args.add("-DadditionalUpdateRecipeCoords" + rewrite.additionalUpdateRecipeCoords); + if (rewrite.additionalUpdateRecipes != null) { + args.add("-DadditionalUpdateRecipes" + rewrite.additionalUpdateRecipes); } if (rewrite.dryRun) { args.add("-DrewriteDryRun"); diff --git a/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java index 2683d87365fb0..5f709838edf4e 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/update/RewriteGroup.java @@ -13,15 +13,15 @@ public class RewriteGroup { public boolean dryRun = false; @CommandLine.Option(order = 2, names = { - "--update-recipes-version" }, description = "Use a custom io.quarkus:quarkus-update-recipes version. This artifact contains the base recipes used by this tool to update a project.") - public String updateRecipesVersion; + "--quarkus-update-recipes" }, description = "Use custom io.quarkus:quarkus-update-recipes:LATEST artifact (GAV) or just provide the version. This artifact should contain the base Quarkus update recipes to update a project.") + public String quarkusUpdateRecipes; @CommandLine.Option(order = 3, names = { "--rewrite-plugin-version" }, description = "Use a custom OpenRewrite plugin version.") public String pluginVersion; @CommandLine.Option(order = 4, names = { - "--additional-update-recipe-coords" }, description = "Specify an additional list of artifacts to retrieve recipes from.") - public String additionalUpdateRecipeCoords; + "--additional-update-recipes" }, description = "Specify a list of additional artifacts (GAV) containing rewrite recipes.") + public String additionalUpdateRecipes; } diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java index 8e02a5626055c..44a65d28f57da 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusUpdate.java @@ -28,8 +28,8 @@ public abstract class QuarkusUpdate extends QuarkusPlatformTask { private String rewritePluginVersion = null; - private String rewriteUpdateRecipesVersion = null; - private String rewriteAdditionalUpdateRecipeCoords = null; + private String rewriteQuarkusUpdateRecipes = null; + private String rewriteAdditionalUpdateRecipes = null; @Input @Optional @@ -78,25 +78,25 @@ public void setRewritePluginVersion(String rewritePluginVersion) { @Input @Optional - public String getRewriteUpdateRecipesVersion() { - return rewriteUpdateRecipesVersion; + public String getRewriteQuarkusUpdateRecipes() { + return rewriteQuarkusUpdateRecipes; } - @Option(description = " The io.quarkus:quarkus-update-recipes version. This artifact contains the base recipes used by this tool to update a project.", option = "updateRecipesVersion") - public QuarkusUpdate setRewriteUpdateRecipesVersion(String rewriteUpdateRecipesVersion) { - this.rewriteUpdateRecipesVersion = rewriteUpdateRecipesVersion; + @Option(description = "Use a custom io.quarkus:quarkus-update-recipes:LATEST artifact (GAV) or just provide the version. This artifact should contain the base Quarkus update recipes to update a project.", option = "quarkusUpdateRecipes") + public QuarkusUpdate setRewriteQuarkusUpdateRecipes(String rewriteQuarkusUpdateRecipes) { + this.rewriteQuarkusUpdateRecipes = rewriteQuarkusUpdateRecipes; return this; } @Input @Optional - public String getRewriteAdditionalUpdateRecipeCoords() { - return rewriteAdditionalUpdateRecipeCoords; + public String getRewriteAdditionalUpdateRecipes() { + return rewriteAdditionalUpdateRecipes; } - @Option(description = " The additional artifacts to retrieve recipes from.", option = "additionalUpdateRecipeCoords") - public QuarkusUpdate setRewriteAdditionalUpdateRecipeCoords(String rewriteAdditionalUpdateRecipeCoords) { - this.rewriteAdditionalUpdateRecipeCoords = rewriteAdditionalUpdateRecipeCoords; + @Option(description = "Specify a list of additional artifacts (GAV) containing rewrite recipes.", option = "additionalUpdateRecipes") + public QuarkusUpdate setRewriteAdditionalUpdateRecipes(String rewriteAdditionalUpdateRecipes) { + this.rewriteAdditionalUpdateRecipes = rewriteAdditionalUpdateRecipes; return this; } @@ -153,11 +153,11 @@ public void logUpdates() { final UpdateProject invoker = new UpdateProject(quarkusProject); invoker.targetCatalog(targetCatalog); - if (rewriteUpdateRecipesVersion != null) { - invoker.rewriteUpdateRecipesVersion(rewriteUpdateRecipesVersion); + if (rewriteQuarkusUpdateRecipes != null) { + invoker.rewriteQuarkusUpdateRecipes(rewriteQuarkusUpdateRecipes); } - if (rewriteAdditionalUpdateRecipeCoords != null) { - invoker.rewriteAdditionalUpdateRecipeCoords(rewriteAdditionalUpdateRecipeCoords); + if (rewriteAdditionalUpdateRecipes != null) { + invoker.rewriteAdditionalUpdateRecipes(rewriteAdditionalUpdateRecipes); } if (rewritePluginVersion != null) { invoker.rewritePluginVersion(rewritePluginVersion); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java index 54525279dc298..7f2043296cac5 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/UpdateMojo.java @@ -59,17 +59,18 @@ public class UpdateMojo extends QuarkusProjectStateMojoBase { private Boolean rewriteDryRun; /** - * The io.quarkus:quarkus-update-recipes version. This artifact contains the base recipes used by this tool to update a - * project. + * Use custom io.quarkus:quarkus-update-recipes:LATEST coords (GAV) or just provide the version. This artifact should + * contain the + * base Quarkus update recipes to update a project. */ - @Parameter(property = "updateRecipesVersion", required = false) - private String rewriteUpdateRecipesVersion; + @Parameter(property = "quarkusUpdateRecipes", required = false) + private String rewriteQuarkusUpdateRecipes; /** - * The list of artifacts containing rewrite recipes + * Specify a list of additional artifacts (GAV) containing rewrite recipes */ - @Parameter(property = "additionalUpdateRecipeCoords", required = false) - private String rewriteAdditionalUpdateRecipeCoords; + @Parameter(property = "additionalUpdateRecipes", required = false) + private String rewriteAdditionalUpdateRecipes; /** * Target stream (e.g: 2.0) @@ -119,11 +120,11 @@ protected void processProjectState(QuarkusProject quarkusProject) throws MojoExe if (rewritePluginVersion != null) { invoker.rewritePluginVersion(rewritePluginVersion); } - if (rewriteUpdateRecipesVersion != null) { - invoker.rewriteUpdateRecipesVersion(rewriteUpdateRecipesVersion); + if (rewriteQuarkusUpdateRecipes != null) { + invoker.rewriteQuarkusUpdateRecipes(rewriteQuarkusUpdateRecipes); } - if (rewriteAdditionalUpdateRecipeCoords != null) { - invoker.rewriteAdditionalUpdateRecipeCoords(rewriteAdditionalUpdateRecipeCoords); + if (rewriteAdditionalUpdateRecipes != null) { + invoker.rewriteAdditionalUpdateRecipes(rewriteAdditionalUpdateRecipes); } invoker.rewriteDryRun(rewriteDryRun); invoker.noRewrite(noRewrite); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java index edde73e81b92e..632924683f4d5 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/UpdateProject.java @@ -24,8 +24,8 @@ public class UpdateProject { public static final String NO_REWRITE = "quarkus.update-project.rewrite.disabled"; public static final String TARGET_PLATFORM_VERSION = "quarkus.update-project.target-platform-version"; public static final String REWRITE_PLUGIN_VERSION = "quarkus.update-project.rewrite.plugin-version"; - public static final String REWRITE_UPDATE_RECIPES_VERSION = "quarkus.update-project.rewrite.update-recipes-version"; - public static final String REWRITE_ADDITIONAL_UPDATE_RECIPE_COORDS = "quarkus.update-project.rewrite.additional-update-recipe-coords"; + public static final String REWRITE_QUARKUS_UPDATE_RECIPES = "quarkus.update-project.rewrite.quarkus-update-recipes"; + public static final String REWRITE_ADDITIONAL_UPDATE_RECIPES = "quarkus.update-project.rewrite.additional-update-recipes"; public static final String REWRITE_DRY_RUN = "quarkus.update-project.rewrite.dry-run"; private final QuarkusCommandInvocation invocation; @@ -59,14 +59,14 @@ public UpdateProject rewritePluginVersion(String rewritePluginVersion) { return this; } - public UpdateProject rewriteUpdateRecipesVersion(String rewriteUpdateRecipesVersion) { - invocation.setValue(REWRITE_UPDATE_RECIPES_VERSION, - requireNonNull(rewriteUpdateRecipesVersion, "rewriteUpdateRecipesVersion is required")); + public UpdateProject rewriteQuarkusUpdateRecipes(String rewriteQuarkusUpdateRecipes) { + invocation.setValue(REWRITE_QUARKUS_UPDATE_RECIPES, + requireNonNull(rewriteQuarkusUpdateRecipes, "rewriteQuarkusUpdateRecipes is required")); return this; } - public UpdateProject rewriteAdditionalUpdateRecipeCoords(String rewriteAdditionalUpdateRecipeCoords) { - invocation.setValue(REWRITE_ADDITIONAL_UPDATE_RECIPE_COORDS, rewriteAdditionalUpdateRecipeCoords); + public UpdateProject rewriteAdditionalUpdateRecipes(String rewriteAdditionalUpdateRecipes) { + invocation.setValue(REWRITE_ADDITIONAL_UPDATE_RECIPES, rewriteAdditionalUpdateRecipes); return this; } diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java index f8655b01a03a1..67a4a6cd56982 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/commands/handlers/UpdateProjectCommandHandler.java @@ -114,16 +114,15 @@ public QuarkusCommandOutcome execute(QuarkusCommandInvocation invocation) throws Path recipe = null; try { recipe = Files.createTempFile("quarkus-project-recipe-", ".yaml"); - final String updateRecipesVersion = invocation.getValue( - UpdateProject.REWRITE_UPDATE_RECIPES_VERSION, + final String quarkusUpdateRecipes = invocation.getValue( + UpdateProject.REWRITE_QUARKUS_UPDATE_RECIPES, QuarkusUpdatesRepository.DEFAULT_UPDATE_RECIPES_VERSION); - final String additionalUpdateRecipeCoords = invocation.getValue( - UpdateProject.REWRITE_ADDITIONAL_UPDATE_RECIPE_COORDS, + final String additionalUpdateRecipes = invocation.getValue( + UpdateProject.REWRITE_ADDITIONAL_UPDATE_RECIPES, null); - final FetchResult fetchResult = QuarkusUpdates.createRecipe(invocation.log(), - recipe, - QuarkusProjectHelper.artifactResolver(), buildTool, updateRecipesVersion, - additionalUpdateRecipeCoords, request); + final FetchResult fetchResult = QuarkusUpdates.createRecipe(invocation.log(), recipe, + QuarkusProjectHelper.artifactResolver(), buildTool, quarkusUpdateRecipes, + additionalUpdateRecipes, request); invocation.log().info("OpenRewrite recipe generated: %s", recipe); String rewritePluginVersion = invocation.getValue(UpdateProject.REWRITE_PLUGIN_VERSION, diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java index ff152288495e0..802755b30be3a 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdates.java @@ -22,12 +22,15 @@ private QuarkusUpdates() { } public static FetchResult createRecipe(MessageWriter log, Path target, MavenArtifactResolver artifactResolver, - BuildTool buildTool, String updateRecipesVersion, String additionalUpdateRecipeCoords, + BuildTool buildTool, String quarkusUpdateRecipes, String additionalUpdateRecipes, ProjectUpdateRequest request) throws IOException { - final FetchResult result = QuarkusUpdatesRepository.fetchRecipes(log, artifactResolver, buildTool, - updateRecipesVersion, - additionalUpdateRecipeCoords, + final FetchResult result = QuarkusUpdatesRepository.fetchRecipes( + log, + artifactResolver, + buildTool, + quarkusUpdateRecipes, + additionalUpdateRecipes, request.currentVersion, request.targetVersion, request.projectExtensionsUpdateInfo diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java index ef00b8eb905b8..9ced7ecc63129 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/update/rewrite/QuarkusUpdatesRepository.java @@ -4,15 +4,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Properties; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -33,7 +25,7 @@ public final class QuarkusUpdatesRepository { private QuarkusUpdatesRepository() { } - private static final String QUARKUS_RECIPE_GA = "io.quarkus:quarkus-update-recipes"; + private static final String QUARKUS_UPDATE_RECIPES_GA = "io.quarkus:quarkus-update-recipes"; public static final String DEFAULT_UPDATE_RECIPES_VERSION = "LATEST"; public static final String DEFAULT_MAVEN_REWRITE_PLUGIN_VERSION = "4.46.0"; @@ -42,14 +34,15 @@ private QuarkusUpdatesRepository() { public static final String PROP_REWRITE_GRADLE_PLUGIN_VERSION = "rewrite-gradle-plugin-version"; public static FetchResult fetchRecipes(MessageWriter log, MavenArtifactResolver artifactResolver, - BuildTool buildTool, - String recipeVersion, String additionalUpdateRecipeCoords, String currentVersion, + BuildTool buildTool, String quarkusUpdateRecipes, String additionalUpdateRecipes, String currentVersion, String targetVersion, List topExtensionDependency) { List gavs = new ArrayList<>(); - gavs.add(QUARKUS_RECIPE_GA + ":" + recipeVersion); - if (additionalUpdateRecipeCoords != null) { - gavs.addAll(Arrays.stream(additionalUpdateRecipeCoords.split(",")).map(String::strip).toList()); + + gavs.add(quarkusUpdateRecipes.contains(":") ? quarkusUpdateRecipes + : QUARKUS_UPDATE_RECIPES_GA + ":" + quarkusUpdateRecipes); + if (additionalUpdateRecipes != null) { + gavs.addAll(Arrays.stream(additionalUpdateRecipes.split(",")).map(String::strip).toList()); } List artifacts = new ArrayList<>(); From 6272732e4cbaf768fcf1658eec415ae1a1e949c0 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 12 Jun 2024 19:31:28 +0200 Subject: [PATCH 30/94] Provide infrastructure to debug QuarkusClassLoader lifecyle You can log the constructor and close() calls by enabling debug logging for category `io.quarkus.bootstrap.classloading.QuarkusClassLoader.lifecycle`. You can log late accesses to closed class loaders by passing `-Dquarkus-log-access-to-closed-class-loaders` to your build command. --- .../classloading/QuarkusClassLoader.java | 71 +++++++++++++++++-- 1 file changed, 65 insertions(+), 6 deletions(-) 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 fdac726f9b609..016dbdd191b44 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 @@ -35,6 +35,14 @@ */ public class QuarkusClassLoader extends ClassLoader implements Closeable { private static final Logger log = Logger.getLogger(QuarkusClassLoader.class); + private static final Logger lifecycleLog = Logger.getLogger(QuarkusClassLoader.class.getName() + ".lifecycle"); + private static final boolean LOG_ACCESS_TO_CLOSED_CLASS_LOADERS = Boolean + .getBoolean("quarkus-log-access-to-closed-class-loaders"); + + private static final byte STATUS_OPEN = 1; + private static final byte STATUS_CLOSING = 0; + private static final byte STATUS_CLOSED = -1; + protected static final String META_INF_SERVICES = "META-INF/services/"; protected static final String JAVA = "java."; @@ -103,7 +111,7 @@ public static boolean isResourcePresentAtRuntime(String resourcePath) { private final List classLoaderEventListeners; /** - * The element that holds resettable in-memory classses. + * The element that holds resettable in-memory classes. *

* A reset occurs when new transformers and in-memory classes are added to a ClassLoader. It happens after each * start in dev mode, however in general the reset resources will be the same. There are some cases where this is @@ -131,7 +139,7 @@ public static boolean isResourcePresentAtRuntime(String resourcePath) { PLATFORM_CLASS_LOADER = cl; } - private boolean closed; + private volatile byte status; private volatile boolean driverLoaded; private QuarkusClassLoader(Builder builder) { @@ -139,6 +147,7 @@ private QuarkusClassLoader(Builder builder) { // stacktraces become very ugly if we do that. super(builder.parent); this.name = builder.name; + this.status = STATUS_OPEN; this.elements = builder.elements; this.bannedElements = builder.bannedElements; this.parentFirstElements = builder.parentFirstElements; @@ -151,6 +160,10 @@ private QuarkusClassLoader(Builder builder) { this.classLoaderEventListeners = builder.classLoaderEventListeners.isEmpty() ? Collections.emptyList() : builder.classLoaderEventListeners; setDefaultAssertionStatus(builder.assertionsEnabled); + + if (lifecycleLog.isDebugEnabled()) { + lifecycleLog.debugf(new RuntimeException("Created to log a stacktrace"), "Creating class loader %s", this); + } } public static Builder builder(String name, ClassLoader parent, boolean parentFirst) { @@ -171,6 +184,8 @@ private String sanitizeName(String name) { * Returns true if the supplied class is a class that would be loaded parent-first */ public boolean isParentFirst(String name) { + ensureOpen(); + if (name.startsWith(JAVA)) { return true; } @@ -198,6 +213,8 @@ private boolean parentFirst(String name, ClassLoaderState state) { } public void reset(Map generatedResources, Map transformedClasses) { + ensureOpen(); + if (resettableElement == null) { throw new IllegalStateException("Classloader is not resettable"); } @@ -210,10 +227,14 @@ public void reset(Map generatedResources, Map tr @Override public Enumeration getResources(String unsanitisedName) throws IOException { + ensureOpen(); + return getResources(unsanitisedName, false); } public Enumeration getResources(String unsanitisedName, boolean parentAlreadyFoundResources) throws IOException { + ensureOpen(); + for (ClassLoaderEventListener l : classLoaderEventListeners) { l.enumeratingResourceURLs(unsanitisedName, this.name); } @@ -244,7 +265,7 @@ public Enumeration getResources(String unsanitisedName, boolean parentAlrea } } //TODO: in theory resources could have been added in dev mode - //but I don't thing this really matters for this code path + //but I don't think this really matters for this code path Set resources = new LinkedHashSet<>(); ClassPathElement[] providers = state.loadableResources.get(name); if (providers != null) { @@ -356,6 +377,8 @@ private ClassLoaderState getState() { @Override public URL getResource(String unsanitisedName) { + ensureOpen(); + for (ClassLoaderEventListener l : classLoaderEventListeners) { l.gettingURLFromResource(unsanitisedName, this.name); } @@ -404,6 +427,8 @@ public URL getResource(String unsanitisedName) { @Override public InputStream getResourceAsStream(String unsanitisedName) { + ensureOpen(); + for (ClassLoaderEventListener l : classLoaderEventListeners) { l.openResourceStream(unsanitisedName, this.name); } @@ -455,6 +480,8 @@ public InputStream getResourceAsStream(String unsanitisedName) { */ @Override protected Class findClass(String moduleName, String name) { + ensureOpen(); + try { return loadClass(name, false); } catch (ClassNotFoundException e) { @@ -463,21 +490,29 @@ protected Class findClass(String moduleName, String name) { } protected URL findResource(String name) { + ensureOpen(); + return getResource(name); } @Override protected Enumeration findResources(String name) throws IOException { + ensureOpen(); + return getResources(name); } @Override public Class loadClass(String name) throws ClassNotFoundException { + ensureOpen(); + return loadClass(name, false); } @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + ensureOpen(); + for (ClassLoaderEventListener l : classLoaderEventListeners) { l.loadClass(name, this.name); } @@ -574,10 +609,14 @@ private String getPackageNameFromClassName(String className) { } public List getElementsWithResource(String name) { + ensureOpen(); + return getElementsWithResource(name, false); } public List getElementsWithResource(String name, boolean localOnly) { + ensureOpen(); + List ret = new ArrayList<>(); if (parent instanceof QuarkusClassLoader && !localOnly) { ret.addAll(((QuarkusClassLoader) parent).getElementsWithResource(name)); @@ -591,6 +630,8 @@ public List getElementsWithResource(String name, boolean local } public List getLocalClassNames() { + ensureOpen(); + List ret = new ArrayList<>(); for (String name : getState().loadableResources.keySet()) { if (name.endsWith(".class")) { @@ -602,10 +643,14 @@ public List getLocalClassNames() { } public Class visibleDefineClass(String name, byte[] b, int off, int len) throws ClassFormatError { + ensureOpen(); + return super.defineClass(name, b, off, len); } public void addCloseTask(Runnable task) { + ensureOpen(); + synchronized (closeTasks) { closeTasks.add(task); } @@ -614,11 +659,16 @@ public void addCloseTask(Runnable task) { @Override public void close() { synchronized (this) { - if (closed) { + if (status < STATUS_OPEN) { return; } - closed = true; + status = STATUS_CLOSING; } + + if (lifecycleLog.isDebugEnabled()) { + lifecycleLog.debugf(new RuntimeException("Created to log a stacktrace"), "Closing class loader %s", this); + } + List tasks; synchronized (closeTasks) { tasks = new ArrayList<>(closeTasks); @@ -664,10 +714,19 @@ public void close() { } ResourceBundle.clearCache(this); + status = STATUS_CLOSED; } public boolean isClosed() { - return closed; + return status < STATUS_OPEN; + } + + private void ensureOpen() { + if (LOG_ACCESS_TO_CLOSED_CLASS_LOADERS && status == STATUS_CLOSED) { + // we do not use a logger as it might require some class loading + System.out.println("Class loader " + this + " has been closed and may not be accessed anymore"); + Thread.dumpStack(); + } } @Override From 603ea200827559d6f256b32a033bf9e9c644eaee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jul 2024 21:54:51 +0000 Subject: [PATCH 31/94] Bump de.flapdoodle.embed:de.flapdoodle.embed.mongo from 4.14.0 to 4.15.0 Bumps [de.flapdoodle.embed:de.flapdoodle.embed.mongo](https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo) from 4.14.0 to 4.15.0. - [Commits](https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo/compare/de.flapdoodle.embed.mongo-4.14.0...de.flapdoodle.embed.mongo-4.15.0) --- updated-dependencies: - dependency-name: de.flapdoodle.embed:de.flapdoodle.embed.mongo 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 94292763f2b46..f74ecdada89b9 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -171,7 +171,7 @@ 0.34.1 3.26.1 0.3.0 - 4.14.0 + 4.15.0 6.1.SP2 3.2.SP2 6.2 From 948dd53a5d3e5813c2f23e87ae23a8281fa63ccb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jul 2024 22:00:45 +0000 Subject: [PATCH 32/94] Bump elasticsearch-opensource-components.version from 8.14.1 to 8.14.2 Bumps `elasticsearch-opensource-components.version` from 8.14.1 to 8.14.2. Updates `org.elasticsearch.client:elasticsearch-rest-client` from 8.14.1 to 8.14.2 - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.14.1...v8.14.2) Updates `co.elastic.clients:elasticsearch-java` from 8.14.1 to 8.14.2 - [Release notes](https://github.com/elastic/elasticsearch-java/releases) - [Changelog](https://github.com/elastic/elasticsearch-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch-java/compare/v8.14.1...v8.14.2) Updates `org.elasticsearch.client:elasticsearch-rest-client-sniffer` from 8.14.1 to 8.14.2 - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.14.1...v8.14.2) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: co.elastic.clients:elasticsearch-java dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.elasticsearch.client:elasticsearch-rest-client-sniffer 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 94292763f2b46..4746ba8894a84 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -102,7 +102,7 @@ 7.0.1.Final 2.4 8.0.0.Final - 8.14.1 + 8.14.2 2.2.21 2.2.5.Final 2.2.2.Final From 2a567df32c844a076eeb876f65a43756413b457f Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 5 Jul 2024 12:23:56 +0300 Subject: [PATCH 33/94] Fix native issue with @Providers when only the REST Client exists When the server part of Quarkus REST is not included, prior to this change, user's @Provider classes were not registered for reflection Relates to: https://github.com/quarkiverse/quarkus-langchain4j/issues/722 --- .../reactive/deployment/RestClientReactiveProcessor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java b/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java index 07d3e0cebed69..3ccb72cce6088 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/main/java/io/quarkus/rest/client/reactive/deployment/RestClientReactiveProcessor.java @@ -311,6 +311,10 @@ void registerProvidersFromAnnotations(CombinedIndexBuildItem indexBuildItem, int.class), constructor.getThis(), constructor.loadClassFromTCCL(providerDotName.toString()), constructor.load(priority)); + + // when the server is not included, providers are not automatically registered for reflection, + // so we need to always do it for the client to be on the safe side + reflectiveClassesProducer.produce(ReflectiveClassBuildItem.builder(providerDotName.toString()).build()); } } From b56aecb150f4b3b3f3adabc27906d11f199d70e9 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 5 Jul 2024 13:52:15 +0300 Subject: [PATCH 34/94] Cancel Uni responses in Quarkus REST when the connection is closed Relates to: #41705 --- .../server/test/CancelableUniTest.java | 96 +++++++++++++++++++ .../server/handlers/UniResponseHandler.java | 27 +++++- 2 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CancelableUniTest.java diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CancelableUniTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CancelableUniTest.java new file mode 100644 index 0000000000000..3b1f003566ff7 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CancelableUniTest.java @@ -0,0 +1,96 @@ +package io.quarkus.resteasy.reactive.server.test; + +import static io.restassured.RestAssured.when; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.jupiter.api.Assertions.*; + +import java.net.URL; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.mutiny.Uni; +import io.vertx.core.Vertx; +import io.vertx.ext.web.client.WebClient; + +public class CancelableUniTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClasses(Resource.class)); + + @BeforeEach + void setUp() { + Resource.COUNT.set(0); + } + + @Inject + Vertx vertx; + + @TestHTTPResource + URL url; + + @Test + public void testNormal() { + when().get("test") + .then() + .statusCode(200) + .body(equalTo("Hello, world")); + } + + @Test + public void testCancel() { + WebClient client = WebClient.create(vertx); + + client.get(url.getPort(), url.getHost(), "/test").send(); + + try { + // make sure we did make the proper request + await().atMost(Duration.ofSeconds(2)).untilAtomic(Resource.COUNT, equalTo(1)); + + // this will effectively cancel the request + client.close(); + + // make sure we wait until the request could have completed + Thread.sleep(7_000); + + // if the count did not increase, it means that Uni was cancelled + assertEquals(1, Resource.COUNT.get()); + } catch (InterruptedException ignored) { + + } finally { + try { + client.close(); + } catch (Exception ignored) { + + } + } + + } + + @Path("test") + public static class Resource { + + public static final AtomicInteger COUNT = new AtomicInteger(0); + + @GET + @Produces(MediaType.TEXT_PLAIN) + public Uni hello() { + COUNT.incrementAndGet(); + return Uni.createFrom().item("Hello, world").onItem().delayIt().by(Duration.ofSeconds(5)).onItem().invoke( + COUNT::incrementAndGet); + } + } +} diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java index 027bb09290b7a..8e983e0e51b40 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java @@ -1,33 +1,52 @@ package org.jboss.resteasy.reactive.server.handlers; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.subscription.Cancellable; public class UniResponseHandler implements ServerRestHandler { @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { // FIXME: handle Response with entity being a Uni - if (requestContext.getResult() instanceof Uni) { - Uni result = (Uni) requestContext.getResult(); + if (requestContext.getResult() instanceof Uni result) { requestContext.suspend(); - result.subscribe().with(new Consumer() { + AtomicBoolean done = new AtomicBoolean(); + Cancellable cancellable = result.subscribe().with(new Consumer() { @Override public void accept(Object v) { + done.set(true); requestContext.setResult(v); requestContext.resume(); } - }, new Consumer() { + }, new Consumer<>() { @Override public void accept(Throwable t) { + done.set(true); requestContext.resume(t, true); } }); + + requestContext.serverResponse().addCloseHandler(new Runnable() { + @Override + public void run() { + if (!done.get()) { + cancellable.cancel(); + try { + // get rid of everything related to the request since the connection has already gone away + requestContext.close(); + } catch (Exception ignored) { + + } + } + } + }); } } } From 7ae7c6b66bb54cef0dba687c1fe7e2a7bf895817 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 5 Jul 2024 14:25:27 +0300 Subject: [PATCH 35/94] Cancel suspend functions in Quarkus REST when the connection is closed Closes: #41705 --- .../kotlin/CoroutineInvocationHandler.kt | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/extensions/resteasy-reactive/rest-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt b/extensions/resteasy-reactive/rest-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt index 61027d0a21235..fa7fc5a8ec64a 100644 --- a/extensions/resteasy-reactive/rest-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt +++ b/extensions/resteasy-reactive/rest-kotlin/runtime/src/main/kotlin/org/jboss/resteasy/reactive/server/runtime/kotlin/CoroutineInvocationHandler.kt @@ -1,6 +1,7 @@ package org.jboss.resteasy.reactive.server.runtime.kotlin import io.vertx.core.Vertx +import java.util.concurrent.atomic.AtomicBoolean import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -35,24 +36,50 @@ class CoroutineInvocationHandler( logger.trace("Handling request with dispatcher {}", dispatcher) requestContext.suspend() - coroutineScope.launch(context = dispatcher) { - // ensure the proper CL is not lost in dev-mode - Thread.currentThread().contextClassLoader = originalTCCL - try { - val result = - invoker.invokeCoroutine( - requestContext.endpointInstance, - requestContext.parameters - ) - if (result != Unit) { - requestContext.result = result + val done = AtomicBoolean() + var canceled = AtomicBoolean() + + val job = + coroutineScope.launch(context = dispatcher) { + // ensure the proper CL is not lost in dev-mode + Thread.currentThread().contextClassLoader = originalTCCL + try { + val result = + invoker.invokeCoroutine( + requestContext.endpointInstance, + requestContext.parameters + ) + done.set(true) + if (result != Unit) { + requestContext.result = result + } + requestContext.resume() + } catch (t: Throwable) { + done.set(true) + + if (canceled.get()) { + try { + // get rid of everything related to the request since the connection has + // already gone away + requestContext.close() + } catch (ignored: Exception) {} + } else { + // passing true since the target doesn't change and we want response filters + // to + // be able to know what the resource method was + requestContext.handleException(t, true) + requestContext.resume() + } } - } catch (t: Throwable) { - // passing true since the target doesn't change and we want response filters to be - // able to know what the resource method was - requestContext.handleException(t, true) } - requestContext.resume() + + requestContext.serverResponse().addCloseHandler { + if (!done.get()) { + try { + canceled.set(true) + job.cancel(null) + } catch (ignored: Exception) {} + } } } } From 3f1fa034606dc91cd72d386d3ad3ef1e70beb0fe Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 5 Jul 2024 14:41:21 +0300 Subject: [PATCH 36/94] Cancel CompletionStage in Quarkus REST when the connection is closed Relates to: #41705 --- .../test/CancelableCompletionStageTest.java | 106 ++++++++++++++++++ .../CompletionStageResponseHandler.java | 37 +++++- 2 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CancelableCompletionStageTest.java diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CancelableCompletionStageTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CancelableCompletionStageTest.java new file mode 100644 index 0000000000000..286cb45d56877 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/CancelableCompletionStageTest.java @@ -0,0 +1,106 @@ +package io.quarkus.resteasy.reactive.server.test; + +import static io.restassured.RestAssured.when; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URL; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.vertx.core.Vertx; +import io.vertx.ext.web.client.WebClient; + +public class CancelableCompletionStageTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClasses(Resource.class)); + + @BeforeEach + void setUp() { + Resource.COUNT.set(0); + } + + @Inject + Vertx vertx; + + @TestHTTPResource + URL url; + + @Test + public void testNormal() { + when().get("test") + .then() + .statusCode(200) + .body(equalTo("Hello, world")); + } + + @Test + public void testCancel() { + WebClient client = WebClient.create(vertx); + + client.get(url.getPort(), url.getHost(), "/test").send(); + + try { + // make sure we did make the proper request + await().atMost(Duration.ofSeconds(2)).untilAtomic(Resource.COUNT, equalTo(1)); + + // this will effectively cancel the request + client.close(); + + // make sure we wait until the request could have completed + Thread.sleep(7_000); + + // if the count did not increase, it means that Uni was cancelled + assertEquals(1, Resource.COUNT.get()); + } catch (InterruptedException ignored) { + + } finally { + try { + client.close(); + } catch (Exception ignored) { + + } + } + + } + + @Path("test") + public static class Resource { + + public static final AtomicInteger COUNT = new AtomicInteger(0); + + @GET + @Produces(MediaType.TEXT_PLAIN) + public CompletionStage hello() { + COUNT.incrementAndGet(); + return CompletableFuture.supplyAsync( + new Supplier<>() { + @Override + public String get() { + COUNT.incrementAndGet(); + return "Hello, world"; + } + }, + CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS)); + } + } +} diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/CompletionStageResponseHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/CompletionStageResponseHandler.java index 502fece9d492b..757b519eeaef6 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/CompletionStageResponseHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/CompletionStageResponseHandler.java @@ -1,6 +1,8 @@ package org.jboss.resteasy.reactive.server.handlers; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.concurrent.atomic.AtomicBoolean; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; @@ -10,19 +12,42 @@ public class CompletionStageResponseHandler implements ServerRestHandler { @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { // FIXME: handle Response with entity being a CompletionStage - if (requestContext.getResult() instanceof CompletionStage) { - CompletionStage result = (CompletionStage) requestContext.getResult(); + if (requestContext.getResult() instanceof CompletionStage result) { requestContext.suspend(); + AtomicBoolean done = new AtomicBoolean(); + AtomicBoolean canceled = new AtomicBoolean(); result.handle((v, t) -> { - if (t != null) { - requestContext.handleException(t, true); + done.set(true); + if (canceled.get()) { + try { + // get rid of everything related to the request since the connection has already gone away + requestContext.close(); + } catch (Exception ignored) { + + } } else { - requestContext.setResult(v); + if (t != null) { + requestContext.handleException(t, true); + } else { + requestContext.setResult(v); + } + requestContext.resume(); } - requestContext.resume(); return null; }); + + requestContext.serverResponse().addCloseHandler(new Runnable() { + @Override + public void run() { + if (!done.get()) { + if (result instanceof CompletableFuture cf) { + canceled.set(true); + cf.cancel(true); + } + } + } + }); } } } From da8d7a94d0dcf612c4cbe9b9c1a901c712626ddf Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Thu, 13 Jun 2024 15:54:57 +0200 Subject: [PATCH 37/94] Add TLS Command Implementation This commit introduces two new CLI commands to enhance TLS management: - Generate and install a Quarkus Development CA. - Create certificates, either signed with the generated CA or unsigned. As the generated certificates are generated in ``$project-directory/.certs`, this directory is added to the generated .gitignore. The CLI uses the new alias supported added in https://github.com/quarkusio/quarkus/pull/40580, and thus the commands are behind: `quarkus tls` --- bom/application/pom.xml | 7 + build-parent/pom.xml | 2 - .../main/asciidoc/tls-registry-reference.adoc | 190 ++++++++++++++++++ extensions/tls-registry/cli/pom.xml | 86 ++++++++ .../java/io/quarkus/tls/cli/Constants.java | 16 ++ .../io/quarkus/tls/cli/GenerateCACommand.java | 100 +++++++++ .../tls/cli/GenerateCertificateCommand.java | 167 +++++++++++++++ .../java/io/quarkus/tls/cli/TlsCommand.java | 26 +++ .../tls/cli/SelfSignedGenerationTest.java | 32 +++ extensions/tls-registry/pom.xml | 1 + .../resources/META-INF/quarkus-extension.yaml | 2 + .../quarkus/project/quarkus/base/..gitignore | 2 + pom.xml | 3 + 13 files changed, 632 insertions(+), 2 deletions(-) create mode 100644 extensions/tls-registry/cli/pom.xml create mode 100644 extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/Constants.java create mode 100644 extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/GenerateCACommand.java create mode 100644 extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/GenerateCertificateCommand.java create mode 100644 extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/TlsCommand.java create mode 100644 extensions/tls-registry/cli/src/test/java/io/quarkus/tls/cli/SelfSignedGenerationTest.java diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 6b77ab0497e75..6b3c0bb5ac2a4 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -288,6 +288,12 @@ ${brotli4j.version} + + io.smallrye.certs + smallrye-certificate-generator + ${smallrye-certificate-generator.version} + + com.fasterxml.jackson @@ -6269,6 +6275,7 @@ ${picocli.version} + org.hdrhistogram diff --git a/build-parent/pom.xml b/build-parent/pom.xml index d9706721a99be..f1807bfca2832 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -153,8 +153,6 @@ 3.1.0 - 0.8.1 - 1.1.0 true diff --git a/docs/src/main/asciidoc/tls-registry-reference.adoc b/docs/src/main/asciidoc/tls-registry-reference.adoc index 94cd3a6babf99..2d69218b8dfa8 100644 --- a/docs/src/main/asciidoc/tls-registry-reference.adoc +++ b/docs/src/main/asciidoc/tls-registry-reference.adoc @@ -693,3 +693,193 @@ To handle the renewal, you can use the periodic reloading mechanism: %prod.quarkus.http.insecure-requests=disabled ---- +== Quarkus CLI commands and development CA (Certificate Authority) + +The TLS registry provides CLI commands to generate a development CA and trusted certificates. +This avoids having to use self-signed certificates locally. + +[source, shell] +---- +> quarkus tls +Install and Manage TLS development certificates +Usage: tls [COMMAND] +Commands: + generate-quarkus-ca Generate Quarkus Dev CA certificate and private key. + generate-certificate Generate a TLS certificate with the Quarkus Dev CA if + available. +---- + +In most cases, you generate the Quarkus Development CA once, and then generate certificates signed by this CA. +The Quarkus Development CA is a Certificate Authority that can be used to sign certificates locally. +It is only valid for development purposes and only trusted on the local machine. +The generated CA is located in `$HOME/.quarkus/quarkus-dev-root-ca.pem`, and installed in the system trust store. + +=== CA, signed vs. self-signed certificates + +When developing with TLS, you can use two types of certificates: + + - a self-signed certificate: the certificate is signed by the same entity that uses it. It is not trusted by default. It's generally what we use when we don't have a CA, or don't want to dig too much into TLS. This is not a production setup, and may be used only for development. +- a signed certificate: the certificate is signed by a Certificate Authority (CA). The CA is a trusted entity that signs the certificate. The certificate is trusted by default. This is what we use in production. + +We could use self-signed certificate when running application locally, but it's not always convenient. +Typically, browsers will not trust the certificate, and you will have to import it manually. +`curl`, `wget`, `httpie` and other tools will also not trust the certificate. + +To avoid this, we can use a development CA to sign the certificates, and install it into the system trust store. +Thus, every certificate signed by this CA will be trusted by the system. + +Quarkus makes it easy to generate a development CA and certificates signed by this CA. + +=== Generate a development CA + +The development CA is a Certificate Authority that can be used to sign certificates locally. +Note that the generated CA is only valid for development purposes, and only trusted on the local machine. + +To generate a development CA, use the following command: + +[source, shell] +---- +quarkus tls generate-ca-certificate --install --renew --truststore +---- + +`--install` installs the CA in the system trust store. +Windows, Mac and Linux (Fedora and Ubuntu) are supported. +However, depending on your browser, you may need to import the generated CA manually. +Refer to the browser documentation for more information. +The generated CA is located in `$HOME/.quarkus/quarkus-dev-root-ca.pem`. + +WARNING: When installing the certificate, your system may ask for your password to install the certificate in the system trust store, or ask for confirmation in a dialog (on Windows). + +IMPORTANT: On Windows, makes sure you run from an elevated terminal (run as administrator) to install the CA in the system trust store. + +`--renew` renews the CA if it already exists. +When this option is used, you need to re-generate the certificates that were signed by the CA, as the private key is changed. +Note that if the CA expires, it will automatically be renewed (without passing `--renew`). + +`--truststore` also generates a PKCS12 trust store containing the CA certificate. + +=== Generate a trusted (signed) certificate + +Once you have installed the Quarkus Development CA, you can generate a trusted certificate. +It will be signed by the Quarkus Development CA, and so trusted by your system. + +[source, shell] +---- +quarkus tls generate-certificate --name my-cert +---- + +This generates a certificate signed by the Quarkus Development CA, and so if properly installed / imported, will be trusted by your system. + +The certificate is stored in `./.certs/`. +Two files are generated: + +- `$NAME-keystore.p12` - contains the private key and the certificate. It's password protected. +- `$NAME-truststore.p12` - contains the CA certificate, that you can used as trust store (for test, for instance). + +More options are available: + +[source, shell] +---- +Usage: tls generate-certificate [-hrV] [-c=] [-d=] + -n= [-p=] +Generate a TLS certificate with the Quarkus Dev CA if available. + -c, --cn= The common name of the certificate. Default is 'localhost' + -d, --directory= + The directory in which the certificates will be created. + Default is `.certs` + -n, --name= Name of the certificate. It will be used as file name and + alias in the keystore + -p, --password= + The password of the keystore. Default is 'password' + -r, --renew Whether existing certificates will need to be replaced +---- + +When generating the certificate, a `.env` file is also generated making the Quarkus dev mode aware of these certificates. +So, then, if you run your application in dev mode, it will use these certificates: + +[source, shell] +---- +./mvnw quarkus:dev +... +INFO [io.quarkus] (Quarkus Main Thread) demo 1.0.0-SNAPSHOT on JVM (powered by Quarkus 999-SNAPSHOT) started in 1.286s. Listening on: http://localhost:8080 and https://localhost:8443 +---- + +Now, you can open the Dev UI using HTTPS: `https://localhost:8443/q/dev`, or issue a request using `curl`: + +[source, shell] +---- +curl https://localhost:8443/hello +Hello from Quarkus REST% +---- + +IMPORTANT: If the Quarkus Development CA is not installed, a self-signed certificate is generated. + + +=== Generating a self-signed certificate + +Even if the Quarkus Development CA is installed, you can generate a self-signed certificate: + +[source, shell] +---- +quarkus tls generate-certificate --name my-cert --self-signed +---- + +This generates a self-signed certificate, not signed by the Quarkus Development CA. + +=== Uninstalling the Quarkus Development CA + +Uninstalling the Quarkus Development CA from your system depends on your OS. + +==== Deleting the CA certificate on Windows + +To delete the CA certificate on Windows, use the following commands from a Powershell terminal with administrator rights: + +[source, shell] +---- +# First, we need to identify the serial number of the CA certificate +> certutil -store -user Root +root "Trusted Root Certification Authorities" +================ Certificate 0 ================ +Serial Number: 019036d564c8 +Issuer: O=Quarkus, CN=quarkus-dev-root-ca # <-That's the CA, copy the Serial Number (the line above) +NotBefore: 6/19/2024 11:07 AM +NotAfter: 6/20/2025 11:07 AM +Subject: C=Cloud, S=world, L=home, OU=Quarkus Dev, O=Quarkus Dev, CN=quarkus-dev-root-ca +Signature matches Public Key +Non-root Certificate uses same Public Key as Issuer +Cert Hash(sha1): 3679bc95b613a2112a3d3256fe8321b6eccce720 +No key provider information +Cannot find the certificate and private key for decryption. +CertUtil: -store command completed successfully. + +> certutil -delstore -user -v Root $Serial_Number +---- + +Replace `$Serial_Number` with the serial number of the CA certificate. + +==== Deleting the CA certificate on Linux + +On Fedora, you can use the following command: + +[source, shell] +---- +sudo rm /etc/pki/ca-trust/source/anchors/quarkus-dev-root-ca.pem +sudo update-ca-trust +---- + +On Ubuntu, you can use the following command: + +[source, shell] +---- +sudo rm /usr/local/share/ca-certificates/quarkus-dev-root-ca.pem +sudo update-ca-certificates +---- + +==== Deleting the CA certificate on Mac + +On Mac, you can use the following command: + +[source, shell] +---- +sudo security -v remove-trusted-cert -d /Users/clement/.quarkus/quarkus-dev-root-ca.pem +---- diff --git a/extensions/tls-registry/cli/pom.xml b/extensions/tls-registry/cli/pom.xml new file mode 100644 index 0000000000000..0d4c342a3b5a3 --- /dev/null +++ b/extensions/tls-registry/cli/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + io.quarkus + quarkus-tls-registry-parent + 999-SNAPSHOT + + + quarkus-tls-registry-cli + Quarkus - TLS Registry - CLI + + + io.quarkus.tls.cli.TlsCommand + + + + + info.picocli + picocli + + + + io.smallrye.certs + smallrye-certificate-generator + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -parameters + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + jar-with-dependencies + + ${project.artifactId}-${project.version} + false + + + true + ${main.class} + + + + + + + make-assembly + package + + single + + + + + + + + \ No newline at end of file diff --git a/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/Constants.java b/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/Constants.java new file mode 100644 index 0000000000000..96821d8e3deaa --- /dev/null +++ b/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/Constants.java @@ -0,0 +1,16 @@ +package io.quarkus.tls.cli; + +import java.io.File; + +public interface Constants { + + String CA_FILE_NAME = "quarkus-dev-root-ca.pem"; + String PK_FILE_NAME = "quarkus-dev-root-key.pem"; + String KEYSTORE_FILE_NAME = "quarkus-dev-keystore.p12"; + + File BASE_DIR = new File(System.getenv("HOME"), ".quarkus"); + + File CA_FILE = new File(BASE_DIR, CA_FILE_NAME); + File PK_FILE = new File(BASE_DIR, PK_FILE_NAME); + File KEYSTORE_FILE = new File(BASE_DIR, KEYSTORE_FILE_NAME); +} diff --git a/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/GenerateCACommand.java b/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/GenerateCACommand.java new file mode 100644 index 0000000000000..eb612b66ca029 --- /dev/null +++ b/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/GenerateCACommand.java @@ -0,0 +1,100 @@ +package io.quarkus.tls.cli; + +import static io.quarkus.tls.cli.Constants.CA_FILE; +import static io.quarkus.tls.cli.Constants.KEYSTORE_FILE; +import static io.quarkus.tls.cli.Constants.PK_FILE; +import static java.lang.System.Logger.Level.INFO; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.concurrent.Callable; + +import io.smallrye.certs.ca.CaGenerator; +import picocli.CommandLine; + +@CommandLine.Command(name = "generate-quarkus-ca", mixinStandardHelpOptions = true, description = "Generate Quarkus Dev CA certificate and private key.") +public class GenerateCACommand implements Callable { + + @CommandLine.Option(names = { "-i", + "--install" }, description = "Install the generated CA into the system keychain.", defaultValue = "false") + boolean install; + + @CommandLine.Option(names = { "-t", + "--truststore" }, description = "Generate a PKCS12 (`.p12`) truststore containing the generated CA.", defaultValue = "false") + boolean truststore; + + @CommandLine.Option(names = { "-r", + "--renew" }, description = "Update certificate if already created.", defaultValue = "false") + boolean renew; + + static System.Logger LOGGER = System.getLogger("generate-quarkus-ca"); + + @Override + public Integer call() throws Exception { + LOGGER.log(INFO, "đŸ”¥ Generating Quarkus Dev CA certificate..."); + if (!Constants.BASE_DIR.exists()) { + Constants.BASE_DIR.mkdirs(); + } + + if (CA_FILE.exists() && !renew) { + if (!hasExpired()) { + LOGGER.log(INFO, + "✅ Quarkus Dev CA certificate already exists and has not yet expired. Use --renew to update."); + return 0; + } + } + + String username = System.getProperty("user.name", ""); + CaGenerator generator = new CaGenerator(CA_FILE, PK_FILE, KEYSTORE_FILE, "quarkus"); + generator + .generate("quarkus-dev-root-ca", "Quarkus Development (" + username + ")", "Quarkus Development", + "home", "world", "universe"); + if (install) { + LOGGER.log(INFO, "đŸ”¥ Installing the CA certificate in the system truststore..."); + generator.installToSystem(); + } + + if (truststore) { + LOGGER.log(INFO, "đŸ”¥ Generating p12 truststore..."); + File ts = new File("quarkus-ca-truststore.p12"); + generator.generateTrustStore(ts); + LOGGER.log(INFO, "✅ Truststore generated successfully."); + } + + LOGGER.log(INFO, "✅ Quarkus Development CA generated and installed"); + + return 0; + } + + private boolean hasExpired() throws Exception { + var cert = getCertificateFromPKCS12(); + try { + cert.checkValidity(); + } catch (Exception e) { + LOGGER.log(INFO, "đŸ”¥ Certificate has expired. Renewing..."); + return true; + } + return false; + } + + private static X509Certificate getCertificateFromPKCS12() + throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { + try (FileInputStream fis = new FileInputStream(KEYSTORE_FILE)) { + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(fis, "quarkus".toCharArray()); + Certificate cert = keystore.getCertificate(CaGenerator.KEYSTORE_CERT_ENTRY); + if (cert == null) { + throw new KeyStoreException("No certificate found with alias: " + CaGenerator.KEYSTORE_CERT_ENTRY); + } + return (X509Certificate) cert; + } + } + +} diff --git a/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/GenerateCertificateCommand.java b/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/GenerateCertificateCommand.java new file mode 100644 index 0000000000000..f570fd8345939 --- /dev/null +++ b/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/GenerateCertificateCommand.java @@ -0,0 +1,167 @@ +package io.quarkus.tls.cli; + +import static io.quarkus.tls.cli.Constants.CA_FILE; +import static io.quarkus.tls.cli.Constants.PK_FILE; +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.System.Logger.Level.INFO; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.time.Duration; +import java.util.concurrent.Callable; + +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; + +import io.smallrye.certs.CertificateGenerator; +import io.smallrye.certs.CertificateRequest; +import io.smallrye.certs.Format; +import io.smallrye.common.os.OS; +import picocli.CommandLine; + +@CommandLine.Command(name = "generate-certificate", mixinStandardHelpOptions = true, description = "Generate a TLS certificate with the Quarkus Dev CA if available.") +public class GenerateCertificateCommand implements Callable { + + @CommandLine.Option(names = { "-n", + "--name" }, description = "Name of the certificate. It will be used as file name and alias in the keystore", required = true) + String name; + + @CommandLine.Option(names = { "-p", + "--password" }, description = "The password of the keystore. Default is 'password'", defaultValue = "password", required = false) + String password; + + @CommandLine.Option(names = { "-c", + "--cn" }, description = "The common name of the certificate. Default is 'localhost'", defaultValue = "localhost", required = false) + String cn; + + @CommandLine.Option(names = { "-d", + "--directory" }, description = "The directory in which the certificates will be created. Default is `.certs`", defaultValue = ".certs") + String directory; + + @CommandLine.Option(names = { "-r", + "--renew" }, description = "Whether existing certificates will need to be replaced", defaultValue = "false") + boolean renew; + + @CommandLine.Option(names = { + "--self-signed" }, description = "Generate a self-signed certificate", defaultValue = "false", hidden = true) + boolean selfSigned; + + static { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + + static System.Logger LOGGER = System.getLogger("generate-quarkus-ca"); + + @Override + public Integer call() throws Exception { + LOGGER.log(INFO, "\uD83D\uDD0E Looking for the Quarkus Dev CA certificate..."); + + if (!CA_FILE.exists() || !PK_FILE.exists() || selfSigned) { + LOGGER.log(INFO, "\uD83C\uDFB2 Quarkus Dev CA certificate not found. Generating a self-signed certificate..."); + generateSelfSignedCertificate(); + return 0; + } + + LOGGER.log(INFO, "\uD83D\uDCDC Quarkus Dev CA certificate found at {0}", CA_FILE.getAbsolutePath()); + X509Certificate caCert = loadRootCertificate(CA_FILE); + PrivateKey caPrivateKey = loadPrivateKey(); + + createSignedCertificate(caCert, caPrivateKey); + + LOGGER.log(INFO, "✅ Signed Certificate generated successfully and exported into `{0}-keystore.p12`", name); + printConfig(new File(directory, name + "-keystore.p12").getAbsolutePath(), password); + + return 0; + } + + private void generateSelfSignedCertificate() throws Exception { + File out = new File(directory); + if (!out.exists()) { + out.mkdirs(); + } + new CertificateGenerator(out.toPath(), renew).generate(new CertificateRequest() + .withName(name) + .withCN(cn) + .withPassword(password) + .withDuration(Duration.ofDays(365)) + .withFormat(Format.PKCS12)); + LOGGER.log(INFO, "✅ Self-signed certificate generated successfully and exported into `{0}-keystore.p12`", name); + printConfig(new File(directory, name + "-keystore.p12").getAbsolutePath(), password); + + } + + private void printConfig(String path, String password) { + if (OS.WINDOWS.isCurrent()) { + path = path.replace("\\", "\\\\"); + } + + // .env format + String env = String.format(""" + _DEV_QUARKUS_TLS_KEY_STORE_P12_PATH=%s + _DEV_QUARKUS_TLS_KEY_STORE_P12_PASSWORD=%s + """, path, password); + + var dotEnvFile = new File(".env"); + try (var writer = new FileWriter(dotEnvFile, dotEnvFile.isFile())) { + writer.write(env); + } catch (IOException e) { + LOGGER.log(ERROR, "Failed to write to .env file", e); + } + + LOGGER.log(INFO, """ + ✅ Required configuration added to the `.env` file: + %dev.quarkus.tls.key-store.p12.path={0} + %dev.quarkus.tls.key-store.p12.password={1} + """, path, password); + } + + private X509Certificate loadRootCertificate(File ca) throws Exception { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + try (FileInputStream fis = new FileInputStream(ca)) { + return (X509Certificate) cf.generateCertificate(fis); + } + } + + private PrivateKey loadPrivateKey() throws Exception { + try (BufferedReader reader = new BufferedReader(new FileReader(Constants.PK_FILE)); + PEMParser pemParser = new PEMParser(reader)) { + Object obj = pemParser.readObject(); + if (obj instanceof KeyPair) { + return ((KeyPair) obj).getPrivate(); + } else if (obj instanceof PrivateKeyInfo) { + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + return converter.getPrivateKey(((PrivateKeyInfo) obj)); + } else { + throw new IllegalStateException( + "The file " + Constants.PK_FILE.getAbsolutePath() + " does not contain a private key " + + obj.getClass().getName()); + } + } + } + + private void createSignedCertificate(X509Certificate issuerCert, + PrivateKey issuerPrivateKey) throws Exception { + File out = new File(directory); + if (!out.exists()) { + out.mkdirs(); + } + new CertificateGenerator(out.toPath(), renew).generate(new CertificateRequest() + .withName(name) + .withCN(cn) + .withPassword(password) + .withDuration(Duration.ofDays(365)) + .withFormat(Format.PKCS12) + .signedWith(issuerCert, issuerPrivateKey)); + + } +} diff --git a/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/TlsCommand.java b/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/TlsCommand.java new file mode 100644 index 0000000000000..e002c66b2249d --- /dev/null +++ b/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/TlsCommand.java @@ -0,0 +1,26 @@ +package io.quarkus.tls.cli; + +import java.util.concurrent.Callable; + +import picocli.CommandLine; + +@CommandLine.Command(name = "tls", sortOptions = false, header = "Install and Manage TLS development certificates", subcommands = { + GenerateCACommand.class, + GenerateCertificateCommand.class, +}) +public class TlsCommand implements Callable { + + @CommandLine.Spec + protected CommandLine.Model.CommandSpec spec; + + @Override + public Integer call() { + spec.commandLine().usage(System.out); + return 0; + } + + public static void main(String[] args) { + int exitCode = new CommandLine(new TlsCommand()).execute(args); + System.exit(exitCode); + } +} diff --git a/extensions/tls-registry/cli/src/test/java/io/quarkus/tls/cli/SelfSignedGenerationTest.java b/extensions/tls-registry/cli/src/test/java/io/quarkus/tls/cli/SelfSignedGenerationTest.java new file mode 100644 index 0000000000000..c3a5b23aef4e4 --- /dev/null +++ b/extensions/tls-registry/cli/src/test/java/io/quarkus/tls/cli/SelfSignedGenerationTest.java @@ -0,0 +1,32 @@ +package io.quarkus.tls.cli; + +import java.io.File; +import java.io.FileInputStream; +import java.security.KeyStore; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class SelfSignedGenerationTest { + + @Test + public void testSelfSignedGeneration() throws Exception { + GenerateCertificateCommand command = new GenerateCertificateCommand(); + command.name = "test"; + command.renew = true; + command.selfSigned = true; + command.directory = "target"; + command.password = "password"; + command.call(); + + File file = new File("target/test-keystore.p12"); + Assertions.assertTrue(file.exists()); + + KeyStore ks = KeyStore.getInstance("PKCS12"); + try (FileInputStream fis = new FileInputStream(file)) { + ks.load(fis, "password".toCharArray()); + Assertions.assertNotNull(ks.getCertificate("test")); + Assertions.assertNotNull(ks.getKey("test", "password".toCharArray())); + } + } +} diff --git a/extensions/tls-registry/pom.xml b/extensions/tls-registry/pom.xml index 0b2e443bc9f6e..3a808afcbaea6 100644 --- a/extensions/tls-registry/pom.xml +++ b/extensions/tls-registry/pom.xml @@ -17,6 +17,7 @@ deployment runtime + cli diff --git a/extensions/tls-registry/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/tls-registry/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 81db75cb09125..43b04fda93333 100644 --- a/extensions/tls-registry/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/tls-registry/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -8,3 +8,5 @@ metadata: unlisted: true config: - "quarkus.tls." + cli-plugins: + - "tls: ${project.groupId}:quarkus-tls-registry-cli:${project.version}" diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/base/..gitignore b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/base/..gitignore index af4390a115714..ab558b7afeed7 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/base/..gitignore +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/project/quarkus/base/..gitignore @@ -33,3 +33,5 @@ nb-configuration.xml # Plugin directory /.quarkus/cli/plugins/ +# TLS Certificates +.certs/ diff --git a/pom.xml b/pom.xml index 737abec09f9b8..c0299e314e8a8 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,9 @@ ${protoc.version} 2.41.0 + + 0.8.1 + 7.8.0 From 9e8032ce171d587abbb66711b870fbbbc4a98b94 Mon Sep 17 00:00:00 2001 From: Holly Cummins Date: Fri, 5 Jul 2024 16:56:43 +0100 Subject: [PATCH 38/94] Tidy comments which are no longer applicable --- .../src/test/java/io/quarkus/it/main/MethodSourceTest.java | 2 -- .../test/java/io/quarkus/it/main/ParameterResolverTest.java | 3 --- .../src/test/java/io/quarkus/it/extension/ParamsTest.java | 3 --- 3 files changed, 8 deletions(-) diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/MethodSourceTest.java b/integration-tests/main/src/test/java/io/quarkus/it/main/MethodSourceTest.java index f17579c86b365..c9836ec311839 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/MethodSourceTest.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/MethodSourceTest.java @@ -33,12 +33,10 @@ public void testParameterResolver(UnusedBean.DummyInput dummyInput, Matcher provideDummyInput() { return List.of( Arguments.of( - // note: List.of(...) or Arrays.asList() fails on Java 16 due to: https://github.com/x-stream/xstream/issues/253 new UnusedBean.DummyInput("whatever", new UnusedBean.NestedDummyInput(new ArrayList<>(List.of(1, 2, 3)))), CoreMatchers.is("whatever/6")), Arguments.of( - // note: Collections.emptyList() fails on Java 16 due to: https://github.com/x-stream/xstream/issues/253 new UnusedBean.DummyInput("hi", new UnusedBean.NestedDummyInput(new ArrayList<>())), CoreMatchers.is("hi/0"))); } diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/ParameterResolverTest.java b/integration-tests/main/src/test/java/io/quarkus/it/main/ParameterResolverTest.java index 949e5be3f72e7..eeb168fac0518 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/ParameterResolverTest.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/ParameterResolverTest.java @@ -61,7 +61,6 @@ public boolean supportsParameter(ParameterContext parameterContext, ExtensionCon @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { - // note: List.of(...) or Arrays.asList() fails on Java 16 due to: https://github.com/x-stream/xstream/issues/253 return new UnusedBean.DummyInput("whatever", new UnusedBean.NestedDummyInput(new ArrayList<>(List.of(1, 2, 3)))); } } @@ -78,7 +77,6 @@ public boolean supportsParameter(ParameterContext parameterContext, ExtensionCon public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { return (Supplier) () -> new UnusedBean.DummyInput("fromSupplier", - // note: Collections.emptyList() fails on Java 16 due to: https://github.com/x-stream/xstream/issues/253 new UnusedBean.NestedDummyInput(new ArrayList<>())); } } @@ -109,7 +107,6 @@ public boolean supportsParameter(ParameterContext parameterContext, ExtensionCon @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { - // note: List.of(...) or Arrays.asList() fails on Java 16 due to: https://github.com/x-stream/xstream/issues/253 return new ArrayList<>(List.of(new NonSerializable("foo"), new NonSerializable("bar"))); } } diff --git a/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/ParamsTest.java b/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/ParamsTest.java index f799a7183d5fa..391c0c5e2fa3b 100644 --- a/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/ParamsTest.java +++ b/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/ParamsTest.java @@ -19,9 +19,6 @@ /** * Exercise {@link ParameterizedTest @ParameterizedTest}s. * - *

- * This test will run into x-stream/xstream#253 on Java 16 and - * newer if the custom XStream converters are not used */ @QuarkusTest public class ParamsTest { From dca21394fb812f6423cc82a67fce67516e8d8b83 Mon Sep 17 00:00:00 2001 From: Katia Aresti Date: Fri, 5 Jul 2024 20:13:42 +0200 Subject: [PATCH 39/94] Update Infinispan Cache Implementation to propagate Vert.x context correctly (#298) --- .../runtime/InfinispanCacheImpl.java | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/extensions/infinispan-cache/runtime/src/main/java/io/quarkus/cache/infinispan/runtime/InfinispanCacheImpl.java b/extensions/infinispan-cache/runtime/src/main/java/io/quarkus/cache/infinispan/runtime/InfinispanCacheImpl.java index a92f6f4e70fd7..cc0ea14720eb6 100644 --- a/extensions/infinispan-cache/runtime/src/main/java/io/quarkus/cache/infinispan/runtime/InfinispanCacheImpl.java +++ b/extensions/infinispan-cache/runtime/src/main/java/io/quarkus/cache/infinispan/runtime/InfinispanCacheImpl.java @@ -5,6 +5,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; import java.util.concurrent.Flow; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -24,6 +25,10 @@ import io.quarkus.infinispan.client.runtime.InfinispanClientUtil; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; +import io.vertx.core.Context; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.impl.ContextInternal; /** * This class is an internal Quarkus cache implementation using Infinispan. @@ -111,6 +116,8 @@ public Uni get(K key, Function valueLoader) { @Override public Uni getAsync(K key, Function> valueLoader) { + Context context = Vertx.currentContext(); + return Uni.createFrom().completionStage(CompletionStages.handleAndCompose(remoteCache.getAsync(key), (v1, ex1) -> { if (ex1 != null) { return CompletableFuture.failedFuture(ex1); @@ -145,7 +152,49 @@ public Uni getAsync(K key, Function> valueLoader) { } }); return resultAsync; - })); + })).emitOn(new Executor() { + // We need make sure we go back to the original context when the cache value is computed. + // Otherwise, we would always emit on the context having computed the value, which could + // break the duplicated context isolation. + @Override + public void execute(Runnable command) { + Context ctx = Vertx.currentContext(); + if (context == null) { + // We didn't capture a context + if (ctx == null) { + // We are not on a context => we can execute immediately. + command.run(); + } else { + // We are on a context. + // We cannot continue on the current context as we may share a duplicated context. + // We need a new one. Note that duplicate() does not duplicate the duplicated context, + // but the root context. + ((ContextInternal) ctx).duplicate() + .runOnContext(new Handler() { + @Override + public void handle(Void ignored) { + command.run(); + } + }); + } + } else { + // We captured a context. + if (ctx == context) { + // We are on the same context => we can execute immediately + command.run(); + } else { + // 1) We are not on a context (ctx == null) => we need to switch to the captured context. + // 2) We are on a different context (ctx != null) => we need to switch to the captured context. + context.runOnContext(new Handler() { + @Override + public void handle(Void ignored) { + command.run(); + } + }); + } + } + } + }); } @Override From 0e2918e2188a7c4527a2ced261fed3a2ebb867c9 Mon Sep 17 00:00:00 2001 From: Dmitry Kryukov Date: Fri, 5 Jul 2024 21:50:38 +0300 Subject: [PATCH 40/94] BugFix fixed comparison of field with itself --- .../infinispan/client/runtime/InfinispanDevServicesConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanDevServicesConfig.java b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanDevServicesConfig.java index 1a2a55c4a637e..5296d468953d6 100644 --- a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanDevServicesConfig.java +++ b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/InfinispanDevServicesConfig.java @@ -148,7 +148,7 @@ public boolean equals(Object o) { Objects.equals(shared, that.shared) && Objects.equals(serviceName, that.serviceName) && Objects.equals(imageName, that.imageName) && - Objects.equals(artifacts, this.artifacts) && + Objects.equals(artifacts, that.artifacts) && Objects.equals(containerEnv, that.containerEnv); } From 415194952831e40d28972ee8625f44515b550215 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:38:23 +0000 Subject: [PATCH 41/94] Bump io.rest-assured:rest-assured from 5.4.0 to 5.5.0 Bumps [io.rest-assured:rest-assured](https://github.com/rest-assured/rest-assured) from 5.4.0 to 5.5.0. - [Changelog](https://github.com/rest-assured/rest-assured/blob/master/changelog.txt) - [Commits](https://github.com/rest-assured/rest-assured/compare/rest-assured-5.4.0...rest-assured-5.5.0) --- updated-dependencies: - dependency-name: io.rest-assured:rest-assured dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- independent-projects/resteasy-reactive/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 99311b77c867c..6db49da5075cf 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -59,7 +59,7 @@ 2.6.1 2.5.0 4.5.8 - 5.4.0 + 5.5.0 1.0.0.Final 2.17.1 2.6.0 diff --git a/pom.xml b/pom.xml index 737abec09f9b8..6fe5b5015bca7 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ in the BOM as well as other POMs (build-parent/pom.xml, docs/pom.xml, ...) --> 0.8.12 6.13.1 - 5.4.0 + 5.5.0 6.5.2.Final 4.13.0 1.14.15 From 49be072316bcff5fa2072b4e15066865dea76bb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 22:08:52 +0000 Subject: [PATCH 42/94] Bump com.fasterxml.jackson:jackson-bom from 2.17.1 to 2.17.2 Bumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.17.1 to 2.17.2. - [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.17.1...jackson-bom-2.17.2) --- updated-dependencies: - dependency-name: com.fasterxml.jackson:jackson-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- independent-projects/extension-maven-plugin/pom.xml | 2 +- independent-projects/resteasy-reactive/pom.xml | 2 +- independent-projects/tools/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 6b77ab0497e75..b7950356af21c 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -92,7 +92,7 @@ 23.1.2 1.8.0 - 2.17.1 + 2.17.2 1.0.0.Final 3.14.0 1.17.0 diff --git a/independent-projects/extension-maven-plugin/pom.xml b/independent-projects/extension-maven-plugin/pom.xml index 28a3a7f1c779b..970ee26725813 100644 --- a/independent-projects/extension-maven-plugin/pom.xml +++ b/independent-projects/extension-maven-plugin/pom.xml @@ -38,7 +38,7 @@ 11 11 3.9.8 - 2.17.1 + 2.17.2 1.5.2 5.10.3 diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 99311b77c867c..29a8cfad71cfe 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -61,7 +61,7 @@ 4.5.8 5.4.0 1.0.0.Final - 2.17.1 + 2.17.2 2.6.0 3.0.2 3.0.3 diff --git a/independent-projects/tools/pom.xml b/independent-projects/tools/pom.xml index 78c0540cb9e25..fdb667652e510 100644 --- a/independent-projects/tools/pom.xml +++ b/independent-projects/tools/pom.xml @@ -49,7 +49,7 @@ 3.26.0 - 2.17.1 + 2.17.2 4.1.0 5.10.3 1.26.2 From 2ba9f520eb6f3e317556082306bfa87eabd4ce6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Sat, 6 Jul 2024 10:22:02 +0200 Subject: [PATCH 43/94] Fix REST concurrent modif. ex. for abstract res --- .../server/test/resource/basic/InheritanceTest.java | 13 +++++++++++++ .../InheritanceAbstractParentImplResource.java | 10 ++++++++++ .../resource/InheritanceAbstractParentResource.java | 12 ++++++++++++ .../processor/scanning/ResteasyReactiveScanner.java | 3 ++- 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/InheritanceAbstractParentImplResource.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/InheritanceAbstractParentResource.java diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/InheritanceTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/InheritanceTest.java index cda6508920086..2e7b03c59807b 100644 --- a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/InheritanceTest.java +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/InheritanceTest.java @@ -16,6 +16,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.InheritanceAbstractParentImplResource; +import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.InheritanceAbstractParentResource; import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.InheritanceParentResource; import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.InheritanceParentResourceImpl; import io.quarkus.resteasy.reactive.server.test.simple.PortProviderUtil; @@ -39,6 +41,8 @@ public class InheritanceTest { public JavaArchive get() { JavaArchive war = ShrinkWrap.create(JavaArchive.class); war.addClass(InheritanceParentResource.class); + war.addClass(InheritanceAbstractParentResource.class); + war.addClass(InheritanceAbstractParentImplResource.class); war.addClasses(PortProviderUtil.class, InheritanceParentResourceImpl.class); return war; } @@ -67,4 +71,13 @@ public void Test1() throws Exception { Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); Assertions.assertEquals(response.readEntity(String.class), "First"); } + + @Test + public void testAbstractParent() { + Builder builder = client.target(generateURL("/inheritance-abstract-parent-test")).request(); + builder.header("Accept", "text/plain"); + Response response = builder.get(); + Assertions.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + Assertions.assertEquals(response.readEntity(String.class), "works"); + } } diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/InheritanceAbstractParentImplResource.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/InheritanceAbstractParentImplResource.java new file mode 100644 index 0000000000000..4b740f37a38fc --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/InheritanceAbstractParentImplResource.java @@ -0,0 +1,10 @@ +package io.quarkus.resteasy.reactive.server.test.resource.basic.resource; + +public class InheritanceAbstractParentImplResource extends InheritanceAbstractParentResource { + + @Override + public String get() { + return "works"; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/InheritanceAbstractParentResource.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/InheritanceAbstractParentResource.java new file mode 100644 index 0000000000000..26560c777eb26 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/resource/basic/resource/InheritanceAbstractParentResource.java @@ -0,0 +1,12 @@ +package io.quarkus.resteasy.reactive.server.test.resource.basic.resource; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/inheritance-abstract-parent-test") +public abstract class InheritanceAbstractParentResource { + + @GET + public abstract String get(); + +} diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java index 7de89735072ba..e97bacc60b9a9 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/scanning/ResteasyReactiveScanner.java @@ -263,7 +263,8 @@ public static ResourceScanningResult scanResources( } // handle abstract classes - scannedResources.values().stream().filter(ClassInfo::isAbstract).forEach(abstractScannedResource -> { + var abstractClasses = scannedResources.values().stream().filter(ClassInfo::isAbstract).toList(); + abstractClasses.forEach(abstractScannedResource -> { Collection allSubclasses = index.getAllKnownSubclasses(abstractScannedResource.name()); if (allSubclasses.size() != 1) { return; // don't do anything with this case as it's not evident how it's supposed to be handled From c59049cbb470ef4c34935b193a0b7a8fd704a350 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sat, 6 Jul 2024 11:37:00 +0200 Subject: [PATCH 44/94] Clear the VertxMDC ThreadLocal when shutting down Vert.x --- .../java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java index e0adae3bef1d2..d7e20c81b4a95 100644 --- a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java +++ b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/VertxCoreRecorder.java @@ -451,6 +451,7 @@ public void handle(AsyncResult ar) { Thread.currentThread().interrupt(); throw new IllegalStateException("Exception when closing Vert.x instance", e); } + VertxMDC.INSTANCE.clear(); LateBoundMDCProvider.setMDCProviderDelegate(null); vertx = null; } From 38d999744f3802721d9d9b03cf3a2b176e58954d Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sat, 6 Jul 2024 11:25:27 +0200 Subject: [PATCH 45/94] Clear com.sun.naming.internal.ResourceManager#propertiesCache This is only effective if the class is made accessible, which we make in our code. I noticed this a few times in my heap dumps and I noticed this was also handled in some other frameworks (until the switch to Java 9 that made the --add-opens mandatory). I think dealing with it in our codebase is a good idea from a sustainability point of view. --- build-parent/pom.xml | 3 +- extensions/jaxb/deployment/pom.xml | 10 ++---- .../main/java/io/quarkus/test/ClearCache.java | 34 +++++++++++++++---- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/build-parent/pom.xml b/build-parent/pom.xml index d9706721a99be..62b4681b16f1d 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -440,7 +440,8 @@ - ${jacoco.agent.argLine} -Xmx1500m -XX:MaxMetaspaceSize=1500m -Djava.io.tmpdir="${project.build.directory}" ${surefire.argLine.additional} + + ${jacoco.agent.argLine} -Xmx1500m -XX:MaxMetaspaceSize=1500m -Djava.io.tmpdir="${project.build.directory}" ${surefire.argLine.additional} --add-opens java.naming/com.sun.naming.internal=ALL-UNNAMED MAVEN_OPTS diff --git a/extensions/jaxb/deployment/pom.xml b/extensions/jaxb/deployment/pom.xml index a496418bac757..e4cf5cd173d01 100644 --- a/extensions/jaxb/deployment/pom.xml +++ b/extensions/jaxb/deployment/pom.xml @@ -11,6 +11,9 @@ quarkus-jaxb-deployment Quarkus - JAXB - Deployment + + -Duser.language=en + @@ -52,13 +55,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - - -Duser.language=en - - diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/ClearCache.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/ClearCache.java index 409b5e930b9f5..179ef1d5e5295 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/ClearCache.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/ClearCache.java @@ -13,17 +13,26 @@ public class ClearCache { public static void clearCaches() { clearAnnotationCache(); + clearResourceManagerPropertiesCache(); clearBeansIntrospectorCache(); } private static void clearAnnotationCache() { + clearMap(AnnotationUtils.class, "repeatableAnnotationContainerCache"); + } + + /** + * This will only be effective if the tests are launched with --add-opens java.naming/com.sun.naming.internal=ALL-UNNAMED, + * which is the case in the Quarkus codebase where memory usage is actually an issue. + *

+ * While not mandatory, this actually helps so enabling it only in the Quarkus codebase has actual value. + */ + private static void clearResourceManagerPropertiesCache() { try { - Field f = AnnotationUtils.class.getDeclaredField("repeatableAnnotationContainerCache"); - f.setAccessible(true); - ((Map) (f.get(null))).clear(); - } catch (NoSuchFieldException | IllegalAccessException e) { - //ignore - log.debug("Failed to clear annotation cache", e); + clearMap(Class.forName("com.sun.naming.internal.ResourceManager"), "propertiesCache"); + } catch (ClassNotFoundException e) { + // ignore + log.debug("Unable to load com.sun.naming.internal.ResourceManager", e); } } @@ -31,8 +40,19 @@ private static void clearBeansIntrospectorCache() { try { Introspector.flushCaches(); } catch (Exception e) { - //ignore + // ignore log.debug("Failed to clear java.beans.Introspector cache", e); } } + + private static void clearMap(Class clazz, String mapField) { + try { + Field f = clazz.getDeclaredField(mapField); + f.setAccessible(true); + ((Map) (f.get(null))).clear(); + } catch (Exception e) { + // ignore + log.debugf(e, "Failed to clear cache for %s#%s cache", clazz.getName(), mapField); + } + } } From 72d8bf6840b5aaf30392a69c03e460c2670b9c96 Mon Sep 17 00:00:00 2001 From: harlequin516 Date: Sat, 6 Jul 2024 13:45:18 -0700 Subject: [PATCH 46/94] Update telemetry-micrometer.adoc added missing word --- docs/src/main/asciidoc/telemetry-micrometer.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/telemetry-micrometer.adoc b/docs/src/main/asciidoc/telemetry-micrometer.adoc index c986d7b46c186..a61966fa16d76 100644 --- a/docs/src/main/asciidoc/telemetry-micrometer.adoc +++ b/docs/src/main/asciidoc/telemetry-micrometer.adoc @@ -179,7 +179,7 @@ Given the following declaration of a timer: `registry.timer("http.server.request === Define dimensions for aggregation Metrics, single numerical measurements, often have additional data captured with them. This ancillary data is used to group or aggregate metrics for analysis. -The Micrometer API refers to this dimensional data as tags, but you may it referred to as "labels" or "attributes" in other documentation sources. +The Micrometer API refers to this dimensional data as tags, but you may see it referred to as "labels" or "attributes" in other documentation sources. Micrometer is built primariliy for backend monitoring systems that support dimensional data (metric names that are enchriched with key/value pairs). For heirarchical systems that only support a flat metric name, Micrometer will flatten the set of key/value pairs (sorted by key) and add them to the name. From 17dccdd91aca94091c27070e79f032e754c72548 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Sun, 7 Jul 2024 16:46:57 +0200 Subject: [PATCH 47/94] Enhance JBang Documentation with Configuration and Qute Template The existing configuration section was not functional due to issue #30087. This commit addresses this by documenting alternative methods using `application.properties` or `application.yaml`, as recommended in issue #41739. Additionally, integrating Qute templates was not straightforward. This commit adds a dedicated section to the JBang documentation, providing instructions on how to include and use Qute templates. --- docs/src/main/asciidoc/scripting.adoc | 42 ++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/docs/src/main/asciidoc/scripting.adoc b/docs/src/main/asciidoc/scripting.adoc index a57f94b7702f4..c2ea4bddbee9c 100644 --- a/docs/src/main/asciidoc/scripting.adoc +++ b/docs/src/main/asciidoc/scripting.adoc @@ -375,18 +375,29 @@ With that in place running `jbang quarkusapp.java` will log and render as expect == Configuring Application -You can use `//Q:CONFIG =` to set up static configuration for your application. +To configure the application you can use the `application.properties` file as usual, but you need to _add_ it to the script: -I.e. if you wanted to add the `smallrye-openapi` and `swagger-ui` extensions and have the Swagger UI always show up you would add the following: +[source,java] +---- +//FILES application.properties -[source,java,subs=attributes+] +// ... +@ConfigProperty(name = "prefix", defaultValue = "WG -") +String prefix; +---- + +This will make the `application.properties` file available to the script, and process the configuration as usual. + +You can also use the `application.yaml` file. +For this, you need to _add_ it to the `application.yaml` file to the script and include the `quarkus-config-yaml` dependency: + +[source,java] ---- -//DEPS io.quarkus:quarkus-smallrye-openapi:{quarkus-version} -//DEPS io.quarkus:quarkus-swagger-ui:{quarkus-version} -//Q:CONFIG quarkus.swagger-ui.always-include=true +//DEPS io.quarkus:quarkus-config-yaml +//FILES application.yaml ---- -Now during build the `quarkus.swagger-ui.always-include` will be generated into the resulting jar and `http://0.0.0.0:8080/q/swagger-ui` will be available when run. +NOTE: The path to the `application.properties` and `application.yaml` files are relative to the script file. == Running as a native application @@ -440,6 +451,23 @@ __ ____ __ _____ ___ __ ____ ______ 2023-03-22 09:38:45,450 INFO [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx] ---- +=== Using Qute + +You can use the xref:./qute.adoc[Qute templating engine] in your JBang script by adding the `quarkus-qute` dependency. +You also need to include the `templates` directory in the script: +[source,java] +---- +//DEPS io.quarkus:quarkus-qute +//FILES templates/=templates/* + +// ... + + @Inject + Template template; // Locate and load the `templates/template.html` file +---- + +If your `templates` directory includes sub-directories, use `templates/=templates/**/*` instead. + === Conclusion If you want to get started with Quarkus or write something quickly, Quarkus Scripting with jbang lets you do that. No Maven, no Gradle - just a Java file. In this guide we outlined the very basics on using Quarkus with JBang; if you want to learn more about what JBang can do, go see https://jbang.dev. From ff4b32bb0d8a1df0ccf1cc86a1e1ae9a840c5b0f Mon Sep 17 00:00:00 2001 From: mariofusco Date: Mon, 3 Jun 2024 17:22:07 +0200 Subject: [PATCH 48/94] Replace read/write lock in JarResource to avoid virtual threads pinning --- .../bootstrap/runner/JarFileReference.java | 172 ++++++++++++++++++ .../quarkus/bootstrap/runner/JarResource.java | 141 +++++--------- .../bootstrap/runner/RunnerClassLoader.java | 93 ++++++---- .../runner/VirtualThreadSupport.java | 52 ++++++ 4 files changed, 330 insertions(+), 128 deletions(-) create mode 100644 independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarFileReference.java create mode 100644 independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/VirtualThreadSupport.java diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarFileReference.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarFileReference.java new file mode 100644 index 0000000000000..d798f20830900 --- /dev/null +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarFileReference.java @@ -0,0 +1,172 @@ +package io.quarkus.bootstrap.runner; + +import static io.quarkus.bootstrap.runner.VirtualThreadSupport.isVirtualThread; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.jar.JarFile; + +import io.smallrye.common.io.jar.JarFiles; + +public class JarFileReference { + // Guarded by an atomic reader counter that emulate the behaviour of a read/write lock. + // To enable virtual threads compatibility and avoid pinning it is not possible to use an explicit read/write lock + // because the jarFile access may happen inside a native call (for example triggered by the RunnerClassLoader) + // and then it is necessary to avoid blocking on it. + private final JarFile jarFile; + + // The referenceCounter - 1 represents the number of effective readers (#aqcuire - #release), while the first + // reference is used to determine if a close has been required. + // The JarFileReference is created as already acquired and that's why the referenceCounter starts from 2 + private final AtomicInteger referenceCounter = new AtomicInteger(2); + + private JarFileReference(JarFile jarFile) { + this.jarFile = jarFile; + } + + /** + * Increase the readers counter of the jarFile. + * + * @return true if the acquiring succeeded: it's now safe to access and use the inner jarFile. + * false if the jar reference is going to be closed and then no longer usable. + */ + private boolean acquire() { + while (true) { + int count = referenceCounter.get(); + if (count == 0) { + return false; + } + if (referenceCounter.compareAndSet(count, count + 1)) { + return true; + } + } + } + + /** + * Decrease the readers counter of the jarFile. + * If the counter drops to 0 and a release has been requested also closes the jarFile. + * + * @return true if the release also closes the underlying jarFile. + */ + private boolean release(JarResource jarResource) { + while (true) { + int count = referenceCounter.get(); + if (count <= 0) { + throw new IllegalStateException( + "The reference counter cannot be negative, found: " + (referenceCounter.get() - 1)); + } + if (referenceCounter.compareAndSet(count, count - 1)) { + if (count == 1) { + try { + jarFile.close(); + } catch (IOException e) { + // ignore + } finally { + jarResource.jarFileReference.set(null); + } + return true; + } + return false; + } + } + } + + /** + * Ask to close this reference. + * If there are no readers currently accessing the jarFile also close it, otherwise defer the closing when the last reader + * will leave. + */ + void close(JarResource jarResource) { + release(jarResource); + } + + @FunctionalInterface + interface JarFileConsumer { + T apply(JarFile jarFile, Path jarPath, String resource); + } + + static T withJarFile(JarResource jarResource, String resource, JarFileConsumer fileConsumer) { + + // Happy path: the jar reference already exists and it's ready to be used + final var localJarFileRefFuture = jarResource.jarFileReference.get(); + if (localJarFileRefFuture != null && localJarFileRefFuture.isDone()) { + JarFileReference jarFileReference = localJarFileRefFuture.join(); + if (jarFileReference.acquire()) { + return consumeSharedJarFile(jarFileReference, jarResource, resource, fileConsumer); + } + } + + // There's no valid jar reference, so load a new one + + // Platform threads can load the jarfile asynchronously and eventually blocking till not ready + // to avoid loading the same jarfile multiple times in parallel + if (!isVirtualThread()) { + // It's ok to eventually block on a join() here since we're sure this is used only by platform thread + return consumeSharedJarFile(asyncLoadAcquiredJarFile(jarResource).join(), jarResource, resource, fileConsumer); + } + + // Virtual threads needs to load the jarfile synchronously to avoid blocking. This means that eventually + // multiple threads could load the same jarfile in parallel and this duplication has to be reconciled + final var newJarFileRef = syncLoadAcquiredJarFile(jarResource); + if (jarResource.jarFileReference.compareAndSet(localJarFileRefFuture, newJarFileRef) || + jarResource.jarFileReference.compareAndSet(null, newJarFileRef)) { + // The new file reference has been successfully published and can be used by the current and other threads + // The join() cannot be blocking here since the CompletableFuture has been created already completed + return consumeSharedJarFile(newJarFileRef.join(), jarResource, resource, fileConsumer); + } + + // The newly created file reference hasn't been published, so it can be used exclusively by the current virtual thread + return consumeUnsharedJarFile(newJarFileRef, jarResource, resource, fileConsumer); + } + + private static T consumeSharedJarFile(JarFileReference jarFileReference, + JarResource jarResource, String resource, JarFileConsumer fileConsumer) { + try { + return fileConsumer.apply(jarFileReference.jarFile, jarResource.jarPath, resource); + } finally { + jarFileReference.release(jarResource); + } + } + + private static T consumeUnsharedJarFile(CompletableFuture jarFileReferenceFuture, + JarResource jarResource, String resource, JarFileConsumer fileConsumer) { + JarFileReference jarFileReference = jarFileReferenceFuture.join(); + try { + return fileConsumer.apply(jarFileReference.jarFile, jarResource.jarPath, resource); + } finally { + boolean closed = jarFileReference.release(jarResource); + assert !closed; + // Check one last time if the file reference can be published and reused by other threads, otherwise close it + if (!jarResource.jarFileReference.compareAndSet(null, jarFileReferenceFuture)) { + closed = jarFileReference.release(jarResource); + assert closed; + } + } + } + + private static CompletableFuture syncLoadAcquiredJarFile(JarResource jarResource) { + try { + return CompletableFuture.completedFuture(new JarFileReference(JarFiles.create(jarResource.jarPath.toFile()))); + } catch (IOException e) { + throw new RuntimeException("Failed to open " + jarResource.jarPath, e); + } + } + + private static CompletableFuture asyncLoadAcquiredJarFile(JarResource jarResource) { + CompletableFuture newJarFileRef = new CompletableFuture<>(); + do { + if (jarResource.jarFileReference.compareAndSet(null, newJarFileRef)) { + try { + newJarFileRef.complete(new JarFileReference(JarFiles.create(jarResource.jarPath.toFile()))); + return newJarFileRef; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + newJarFileRef = jarResource.jarFileReference.get(); + } while (newJarFileRef == null || !newJarFileRef.join().acquire()); + return newJarFileRef; + } +} diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarResource.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarResource.java index 8b4da096a891d..60f337e2f3755 100644 --- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarResource.java +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarResource.java @@ -11,44 +11,28 @@ import java.security.ProtectionDomain; import java.security.cert.Certificate; import java.util.Objects; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import io.smallrye.common.io.jar.JarEntries; -import io.smallrye.common.io.jar.JarFiles; /** * A jar resource */ public class JarResource implements ClassLoadingResource { - private final ManifestInfo manifestInfo; - private final Path jarPath; - - private final Lock readLock; - private final Lock writeLock; - private volatile ProtectionDomain protectionDomain; + private final ManifestInfo manifestInfo; - //Guarded by the read/write lock; open/close operations on the JarFile require the exclusive lock, - //while using an existing open reference can use the shared lock. - //If a lock is acquired, and as long as it's owned, we ensure that the zipFile reference - //points to an open JarFile instance, and read operations are valid. - //To close the jar, the exclusive lock must be owned, and reference will be set to null before releasing it. - //Likewise, opening a JarFile requires the exclusive lock. - private volatile JarFile zipFile; + final Path jarPath; + final AtomicReference> jarFileReference = new AtomicReference<>(); public JarResource(ManifestInfo manifestInfo, Path jarPath) { this.manifestInfo = manifestInfo; this.jarPath = jarPath; - final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - this.readLock = readWriteLock.readLock(); - this.writeLock = readWriteLock.writeLock(); } @Override @@ -69,38 +53,48 @@ public void init() { @Override public byte[] getResourceData(String resource) { - final ZipFile zipFile = readLockAcquireAndGetJarReference(); - try { - ZipEntry entry = zipFile.getEntry(resource); + return JarFileReference.withJarFile(this, resource, JarResourceDataProvider.INSTANCE); + } + + private static class JarResourceDataProvider implements JarFileReference.JarFileConsumer { + private static final JarResourceDataProvider INSTANCE = new JarResourceDataProvider(); + + @Override + public byte[] apply(JarFile jarFile, Path path, String res) { + ZipEntry entry = jarFile.getEntry(res); if (entry == null) { return null; } - try (InputStream is = zipFile.getInputStream(entry)) { + try (InputStream is = jarFile.getInputStream(entry)) { byte[] data = new byte[(int) entry.getSize()]; int pos = 0; int rem = data.length; while (rem > 0) { int read = is.read(data, pos, rem); if (read == -1) { - throw new RuntimeException("Failed to read all data for " + resource); + throw new RuntimeException("Failed to read all data for " + res); } pos += read; rem -= read; } return data; } catch (IOException e) { - throw new RuntimeException("Failed to read zip entry " + resource, e); + throw new RuntimeException("Failed to read zip entry " + res, e); } - } finally { - readLock.unlock(); } } @Override public URL getResourceURL(String resource) { - final JarFile jarFile = readLockAcquireAndGetJarReference(); - try { - JarEntry entry = jarFile.getJarEntry(resource); + return JarFileReference.withJarFile(this, resource, JarResourceURLProvider.INSTANCE); + } + + private static class JarResourceURLProvider implements JarFileReference.JarFileConsumer { + private static final JarResourceURLProvider INSTANCE = new JarResourceURLProvider(); + + @Override + public URL apply(JarFile jarFile, Path path, String res) { + JarEntry entry = jarFile.getJarEntry(res); if (entry == null) { return null; } @@ -110,15 +104,7 @@ public URL getResourceURL(String resource) { if (realName.endsWith("/")) { realName = realName.substring(0, realName.length() - 1); } - final URI jarUri = jarPath.toUri(); - // first create a URI which includes both the jar file path and the relative resource name - // and then invoke a toURL on it. The URI reconstruction allows for any encoding to be done - // for the "path" which includes the "realName" - var ssp = new StringBuilder(jarUri.getPath().length() + realName.length() + 2); - ssp.append(jarUri.getPath()); - ssp.append("!/"); - ssp.append(realName); - final URL resUrl = new URI(jarUri.getScheme(), ssp.toString(), null).toURL(); + final URL resUrl = getUrl(path, realName); // wrap it up into a "jar" protocol URL //horrible hack to deal with '?' characters in the URL //seems to be the only way, the URI constructor just does not let you handle them in a sane way @@ -136,8 +122,18 @@ public URL getResourceURL(String resource) { } catch (MalformedURLException | URISyntaxException e) { throw new RuntimeException(e); } - } finally { - readLock.unlock(); + } + + private static URL getUrl(Path jarPath, String realName) throws MalformedURLException, URISyntaxException { + final URI jarUri = jarPath.toUri(); + // first create a URI which includes both the jar file path and the relative resource name + // and then invoke a toURL on it. The URI reconstruction allows for any encoding to be done + // for the "path" which includes the "realName" + var ssp = new StringBuilder(jarUri.getPath().length() + realName.length() + 2); + ssp.append(jarUri.getPath()); + ssp.append("!/"); + ssp.append(realName); + return new URI(jarUri.getScheme(), ssp.toString(), null).toURL(); } } @@ -151,60 +147,15 @@ public ProtectionDomain getProtectionDomain() { return protectionDomain; } - private JarFile readLockAcquireAndGetJarReference() { - while (true) { - readLock.lock(); - final JarFile zipFileLocal = this.zipFile; - if (zipFileLocal != null) { - //Expected fast path: returns a reference to the open JarFile while owning the readLock - return zipFileLocal; - } else { - //This Lock implementation doesn't allow upgrading a readLock to a writeLock, so release it - //as we're going to need the WriteLock. - readLock.unlock(); - //trigger the JarFile being (re)opened. - ensureJarFileIsOpen(); - //Now since we no longer own any lock, we need to try again to obtain the readLock - //and check for the reference still being valid. - //This exposes us to a race with closing the just-opened JarFile; - //however this should be extremely rare, so we can trust we won't loop much; - //A counter doesn't seem necessary, as in fact we know that methods close() - //and resetInternalCaches() are invoked each at most once, which limits the amount - //of loops here in practice. - } - } - } - - private void ensureJarFileIsOpen() { - writeLock.lock(); - try { - if (this.zipFile == null) { - try { - this.zipFile = JarFiles.create(jarPath.toFile()); - } catch (IOException e) { - throw new RuntimeException("Failed to open " + jarPath, e); - } - } - } finally { - writeLock.unlock(); - } - } - @Override public void close() { - writeLock.lock(); - try { - final JarFile zipFileLocal = this.zipFile; - if (zipFileLocal != null) { - try { - this.zipFile = null; - zipFileLocal.close(); - } catch (Throwable e) { - //ignore - } - } - } finally { - writeLock.unlock(); + var futureRef = jarFileReference.get(); + if (futureRef != null) { + // The jarfile has been already used and it's going to be removed from the cache, + // so the future must be already completed + var ref = futureRef.getNow(null); + assert (ref != null); + ref.close(this); } } diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java index 7917d17b851f0..2ff5b966de87a 100644 --- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java @@ -28,6 +28,10 @@ */ public final class RunnerClassLoader extends ClassLoader { + static { + registerAsParallelCapable(); + } + /** * A map of resources by dir name. Root dir/default package is represented by the empty string */ @@ -103,18 +107,55 @@ public Class loadClass(String name, boolean resolve) throws ClassNotFoundExce continue; } definePackage(packageName, resources); - try { - return defineClass(name, data, 0, data.length, resource.getProtectionDomain()); - } catch (LinkageError e) { - loaded = findLoadedClass(name); - if (loaded != null) { - return loaded; + return defineClass(name, data, resource); + } + } + return getParent().loadClass(name); + } + + private void definePackage(String pkgName, ClassLoadingResource[] resources) { + if ((pkgName != null) && getDefinedPackage(pkgName) == null) { + for (ClassLoadingResource classPathElement : resources) { + ManifestInfo mf = classPathElement.getManifestInfo(); + if (mf != null) { + try { + definePackage(pkgName, mf.getSpecTitle(), + mf.getSpecVersion(), + mf.getSpecVendor(), + mf.getImplTitle(), + mf.getImplVersion(), + mf.getImplVendor(), null); + } catch (IllegalArgumentException e) { + var loaded = getDefinedPackage(pkgName); + if (loaded == null) { + throw e; + } } + return; + } + } + try { + definePackage(pkgName, null, null, null, null, null, null, null); + } catch (IllegalArgumentException e) { + var loaded = getDefinedPackage(pkgName); + if (loaded == null) { throw e; } } } - return getParent().loadClass(name); + } + + private Class defineClass(String name, byte[] data, ClassLoadingResource resource) { + Class loaded; + try { + return defineClass(name, data, 0, data.length, resource.getProtectionDomain()); + } catch (LinkageError e) { + loaded = findLoadedClass(name); + if (loaded != null) { + return loaded; + } + throw e; + } } private void accessingResource(final ClassLoadingResource resource) { @@ -131,25 +172,33 @@ private void accessingResource(final ClassLoadingResource resource) { //it's already on the head of the cache: nothing to be done. return; } + for (int i = 1; i < currentlyBufferedResources.length; i++) { final ClassLoadingResource currentI = currentlyBufferedResources[i]; if (currentI == resource || currentI == null) { //it was already cached, or we found an empty slot: bubble it up by one position to give it a boost - final ClassLoadingResource previous = currentlyBufferedResources[i - 1]; - currentlyBufferedResources[i - 1] = resource; - currentlyBufferedResources[i] = previous; + bubbleUpCachedResource(resource, i); return; } } + // else, we drop one element from the cache, // and inserting the latest resource on the tail: toEvict = currentlyBufferedResources[currentlyBufferedResources.length - 1]; - currentlyBufferedResources[currentlyBufferedResources.length - 1] = resource; + bubbleUpCachedResource(resource, currentlyBufferedResources.length - 1); } + // Finally, release the cache for the dropped element: toEvict.resetInternalCaches(); } + private void bubbleUpCachedResource(ClassLoadingResource resource, int i) { + for (int j = i; j > 0; j--) { + currentlyBufferedResources[j] = currentlyBufferedResources[j - 1]; + } + currentlyBufferedResources[0] = resource; + } + @Override protected URL findResource(String name) { name = sanitizeName(name); @@ -221,28 +270,6 @@ protected Enumeration findResources(String name) { return Collections.enumeration(urls); } - private void definePackage(String pkgName, ClassLoadingResource[] resources) { - if ((pkgName != null) && getPackage(pkgName) == null) { - synchronized (getClassLoadingLock(pkgName)) { - if (getPackage(pkgName) == null) { - for (ClassLoadingResource classPathElement : resources) { - ManifestInfo mf = classPathElement.getManifestInfo(); - if (mf != null) { - definePackage(pkgName, mf.getSpecTitle(), - mf.getSpecVersion(), - mf.getSpecVendor(), - mf.getImplTitle(), - mf.getImplVersion(), - mf.getImplVendor(), null); - return; - } - } - definePackage(pkgName, null, null, null, null, null, null, null); - } - } - } - } - private String getPackageNameFromClassName(String className) { final int index = className.lastIndexOf('.'); if (index == -1) { diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/VirtualThreadSupport.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/VirtualThreadSupport.java new file mode 100644 index 0000000000000..5d6f03a51a3ab --- /dev/null +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/VirtualThreadSupport.java @@ -0,0 +1,52 @@ +package io.quarkus.bootstrap.runner; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class VirtualThreadSupport { + + private static final int MAJOR_JAVA_VERSION = majorVersionFromJavaSpecificationVersion(); + + private static final MethodHandle virtualMh = MAJOR_JAVA_VERSION >= 21 ? findVirtualMH() : null; + + private static MethodHandle findVirtualMH() { + try { + return MethodHandles.publicLookup().findVirtual(Thread.class, "isVirtual", + MethodType.methodType(boolean.class)); + } catch (Exception e) { + return null; + } + } + + static boolean isVirtualThread() { + if (virtualMh == null) { + return false; + } + try { + return (boolean) virtualMh.invokeExact(Thread.currentThread()); + } catch (Throwable t) { + return false; + } + } + + static int majorVersionFromJavaSpecificationVersion() { + return majorVersion(System.getProperty("java.specification.version", "17")); + } + + static int majorVersion(String javaSpecVersion) { + String[] components = javaSpecVersion.split("\\."); + int[] version = new int[components.length]; + + for (int i = 0; i < components.length; ++i) { + version[i] = Integer.parseInt(components[i]); + } + + if (version[0] == 1) { + assert version[1] >= 6; + return version[1]; + } else { + return version[0]; + } + } +} From 5707e14ffe38ce3a5c2e6571bbf368b618b2dc5e Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 8 Jul 2024 15:39:37 +0300 Subject: [PATCH 49/94] Fix bug in AsyncResponseImpl#register Fixes: #41732 --- .../jboss/resteasy/reactive/server/jaxrs/AsyncResponseImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/AsyncResponseImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/AsyncResponseImpl.java index 23f4964be8ea0..1e307e0349cbb 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/AsyncResponseImpl.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/AsyncResponseImpl.java @@ -153,7 +153,7 @@ public Map, Collection>> register(Class callback, Class. Objects.requireNonNull(callback); Objects.requireNonNull(callbacks); Map, Collection>> ret = new HashMap<>(); - ret.put(callback.getClass(), register(callback)); + ret.put(callback, register(callback)); for (Class cb : callbacks) { ret.put(cb, register(cb)); } From 30656e91e4b685010d2c86e8c5c6bb2608eefd6f Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 8 Jul 2024 15:46:07 +0300 Subject: [PATCH 50/94] Fix * handling of debug configuration in VertxCoreProcessor --- .../io/quarkus/vertx/core/deployment/VertxCoreProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java index d376eb44e13ef..ab09839b74d15 100644 --- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java +++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java @@ -369,7 +369,7 @@ private Filter createDebuggerFilter() { debugPort = Integer.parseInt(m.group(2)); String host = m.group(1); if (host.equals("*")) { - host.equals("localhost"); + host = "localhost"; } bindAddress = InetAddress.getByName(host); } From 57c50701c40cb4bc19a11f95daa519cd04520e6b Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 8 Jul 2024 17:16:30 +0300 Subject: [PATCH 51/94] Suppress unused warning in RestClientMetricsFilter --- .../micrometer/runtime/binder/RestClientMetricsFilter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/RestClientMetricsFilter.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/RestClientMetricsFilter.java index d8169b045f894..a5de53f0846ef 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/RestClientMetricsFilter.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/RestClientMetricsFilter.java @@ -21,6 +21,7 @@ */ @Unremovable @Provider +@SuppressWarnings("unused") // this is used by io.quarkus.micrometer.deployment.binder.HttpBinderProcessor public class RestClientMetricsFilter implements ClientRequestFilter, ClientResponseFilter { private final static String REQUEST_METRIC_PROPERTY = "restClientMetrics"; From 6c87887923bd855bab7824ecb5c0d87933296598 Mon Sep 17 00:00:00 2001 From: brunobat Date: Thu, 15 Feb 2024 14:57:53 +0000 Subject: [PATCH 52/94] Add OpenTelemetry Metrics and refactor quarkus-opentelemetry and related tests to work with it. OTel Metrics off by default --- .../deployment/OpenTelemetryProcessor.java | 26 +- .../exporter/otlp/OtlpExporterProcessor.java | 61 ++- .../deployment/metric/MetricProcessor.java | 101 +++++ .../deployment/tracing/TracerEnabled.java | 13 +- .../OpenTelemetryContinuousTestingTest.java | 4 +- .../OpenTelemetryDestroyerTest.java | 7 +- .../deployment/OpenTelemetryDevModeTest.java | 3 +- ...enTelemetryDevServicesDatasourcesTest.java | 13 +- .../OpenTelemetryDisabledSdkTest.java | 2 +- .../deployment/OpenTelemetryMDCTest.java | 9 +- .../deployment/OpenTelemetryResourceTest.java | 22 +- .../common/InMemoryMetricExporter.java | 209 ++++++++++ .../InMemoryMetricExporterProvider.java | 19 + .../exporter/otlp/OtlpExporterConfigTest.java | 4 +- ...ava => OtlpTraceExporterDisabledTest.java} | 18 +- .../GraphQLOpenTelemetryTest.java | 1 + .../GrpcOpenInstrumentationDisabledTest.java | 7 +- .../GrpcOpenTelemetryTest.java | 7 +- .../RestClientOpenTelemetryTest.java | 7 +- .../VertxClientOpenTelemetryTest.java | 7 +- .../VertxOpenTelemetryForwardedTest.java | 7 +- .../VertxOpenTelemetryXForwardedTest.java | 7 +- .../AddingSpanAttributesInterceptorTest.java | 2 +- .../interceptor/WithSpanInterceptorTest.java | 2 +- .../WithSpanLegacyInterceptorTest.java | 2 +- .../deployment/metrics/GaugeCdiTest.java | 85 ++++ .../metrics/MetricsDisabledTest.java | 30 ++ .../deployment/metrics/MetricsTest.java | 385 ++++++++++++++++++ ...emetryTextMapPropagatorCustomizerTest.java | 4 +- .../NonAppEndpointsDisabledTest.java | 4 +- ...nAppEndpointsDisabledWithRootPathTest.java | 4 +- ...dpointsEnabledLegacyConfigurationTest.java | 4 +- .../NonAppEndpointsEnabledTest.java | 4 +- .../NonAppEndpointsEqualRootPath.java | 4 +- .../OpenTelemetryCustomSamplerBeanTest.java | 4 +- .../OpenTelemetryHttpCDILegacyTest.java | 6 +- .../OpenTelemetryHttpCDITest.java | 6 +- .../OpenTelemetryIdGeneratorTest.java | 2 +- ...etryJdbcInstrumentationValidationTest.java | 3 +- .../OpenTelemetryReactiveRoutesTest.java | 4 +- .../OpenTelemetrySamplerBeanTest.java | 4 +- .../OpenTelemetrySamplerConfigTest.java | 4 +- .../OpenTelemetrySpanSecurityEventsTest.java | 3 +- .../{ => traces}/TracerRouterUT.java | 2 +- ...metrics.ConfigurableMetricExporterProvider | 1 + .../resources/application-default.properties | 6 +- .../application-no-metrics.properties | 8 + .../resource-config/application.properties | 5 +- .../opentelemetry/OpenTelemetryDestroyer.java | 3 +- ...uredOpenTelemetrySdkBuilderCustomizer.java | 57 ++- .../runtime/config/build/ExporterType.java | 4 +- .../config/build/MetricsBuildConfig.java | 28 ++ .../runtime/config/build/OTelBuildConfig.java | 7 +- .../config/runtime/ExemplarsFilterType.java | 23 ++ .../config/runtime/MetricsRuntimeConfig.java | 20 + .../config/runtime/OTelRuntimeConfig.java | 5 + .../runtime/exporter/OtlpExporterConfig.java | 134 ++++++ .../exporter/OtlpExporterMetricsConfig.java | 33 ++ .../exporter/OtlpExporterRuntimeConfig.java | 6 +- .../exporter/OtlpExporterTracesConfig.java | 124 +----- .../exporter/otlp/OTelExporterRecorder.java | 201 +++++++-- .../exporter/otlp/OTelExporterUtil.java | 6 +- .../exporter/otlp/VertxHttpExporter.java | 305 -------------- ...p_internal_OtlpMetricExporterProvider.java | 9 + .../otlp/metrics/NoopMetricExporter.java | 37 ++ .../otlp/metrics/VertxGrpcMetricExporter.java | 60 +++ .../metrics/VertxHttpMetricsExporter.java | 60 +++ .../VertxGrpcSender.java} | 254 ++++++------ .../exporter/otlp/sender/VertxHttpSender.java | 307 ++++++++++++++ .../LateBoundBatchSpanProcessor.java | 2 +- ...RemoveableLateBoundBatchSpanProcessor.java | 2 +- .../otlp/tracing/VertxGrpcSpanExporter.java | 34 ++ .../otlp/tracing/VertxHttpSpanExporter.java | 34 ++ .../runtime/graal/Substitutions.java | 18 - .../runtime/metrics/cdi/MetricsProducer.java | 29 ++ .../spi/MetricsExporterCDIProvider.java | 38 ++ ...metrics.ConfigurableMetricExporterProvider | 1 + ...boss.resteasy.spi.concurrent.ThreadContext | 2 +- .../otlp/OtlpExporterProviderTest.java | 77 ++++ .../vertx/exporter/HelloResource.java | 9 + .../src/main/resources/application.properties | 2 + .../vertx/exporter/AbstractExporterTest.java | 58 ++- .../opentelemetry/vertx/exporter/Metrics.java | 19 + .../OtelCollectorLifecycleManager.java | 35 +- .../src/test/resources/otel-config.yaml | 6 +- .../it/opentelemetry/ExporterResource.java | 46 ++- .../it/opentelemetry/SimpleResource.java | 19 + .../src/main/resources/application.properties | 2 + .../quarkus/it/opentelemetry/MetricsIT.java | 7 + .../quarkus/it/opentelemetry/MetricsTest.java | 71 ++++ .../it/opentelemetry/StaticResourceTest.java | 11 +- .../{OpenTelemetryIT.java => TracingIT.java} | 2 +- ...penTelemetryTest.java => TracingTest.java} | 13 +- .../it/opentelemetry/tck/SpanBeanTest.java | 68 ++++ 94 files changed, 2704 insertions(+), 726 deletions(-) create mode 100644 extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/metric/MetricProcessor.java create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryMetricExporter.java create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryMetricExporterProvider.java rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/{OtlpExporterDisabledTest.java => OtlpTraceExporterDisabledTest.java} (57%) create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/GaugeCdiTest.java create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/MetricsDisabledTest.java create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/MetricsTest.java rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => propagation}/OpenTelemetryTextMapPropagatorCustomizerTest.java (96%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/NonAppEndpointsDisabledTest.java (92%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/NonAppEndpointsDisabledWithRootPathTest.java (93%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/NonAppEndpointsEnabledLegacyConfigurationTest.java (93%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/NonAppEndpointsEnabledTest.java (93%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/NonAppEndpointsEqualRootPath.java (93%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetryCustomSamplerBeanTest.java (96%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetryHttpCDILegacyTest.java (94%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetryHttpCDITest.java (94%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetryIdGeneratorTest.java (98%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetryJdbcInstrumentationValidationTest.java (93%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetryReactiveRoutesTest.java (95%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetrySamplerBeanTest.java (92%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetrySamplerConfigTest.java (92%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/OpenTelemetrySpanSecurityEventsTest.java (97%) rename extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/{ => traces}/TracerRouterUT.java (89%) create mode 100644 extensions/opentelemetry/deployment/src/test/resources/META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider create mode 100644 extensions/opentelemetry/deployment/src/test/resources/resource-config/application-no-metrics.properties create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/MetricsBuildConfig.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/ExemplarsFilterType.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/MetricsRuntimeConfig.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfig.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterMetricsConfig.java delete mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/VertxHttpExporter.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/graal/Target_io_opentelemetry_exporter_otlp_internal_OtlpMetricExporterProvider.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/NoopMetricExporter.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/VertxGrpcMetricExporter.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/VertxHttpMetricsExporter.java rename extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/{VertxGrpcExporter.java => sender/VertxGrpcSender.java} (74%) create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/sender/VertxHttpSender.java rename extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/{ => tracing}/LateBoundBatchSpanProcessor.java (97%) rename extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/{ => tracing}/RemoveableLateBoundBatchSpanProcessor.java (90%) create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/VertxGrpcSpanExporter.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/VertxHttpSpanExporter.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/metrics/cdi/MetricsProducer.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/metrics/spi/MetricsExporterCDIProvider.java create mode 100644 extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider create mode 100644 integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/Metrics.java create mode 100644 integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsIT.java create mode 100644 integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsTest.java rename integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/{OpenTelemetryIT.java => TracingIT.java} (93%) rename integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/{OpenTelemetryTest.java => TracingTest.java} (99%) create mode 100644 integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/tck/SpanBeanTest.java diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java index 350a89459e648..ab745941977be 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java @@ -27,12 +27,14 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.exporter.otlp.internal.OtlpMetricExporterProvider; import io.opentelemetry.exporter.otlp.internal.OtlpSpanExporterProvider; import io.opentelemetry.instrumentation.annotations.AddingSpanAttributes; import io.opentelemetry.instrumentation.annotations.WithSpan; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurablePropagatorProvider; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider; import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; @@ -105,6 +107,7 @@ AdditionalBeanBuildItem ensureProducerIsRetained() { AutoConfiguredOpenTelemetrySdkBuilderCustomizer.ResourceCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.SamplerCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracerProviderCustomizer.class, + AutoConfiguredOpenTelemetrySdkBuilderCustomizer.MetricProviderCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TextMapPropagatorCustomizers.class) .build(); } @@ -144,11 +147,12 @@ void handleServices(OTelBuildConfig config, BuildProducer removedResources, BuildProducer runtimeReinitialized) throws IOException { - List spanExporterProviders = ServiceUtil.classNamesNamedIn( + final List spanExporterProviders = ServiceUtil.classNamesNamedIn( Thread.currentThread().getContextClassLoader(), SPI_ROOT + ConfigurableSpanExporterProvider.class.getName()) .stream() - .filter(p -> !OtlpSpanExporterProvider.class.getName().equals(p)).collect(toList()); // filter out OtlpSpanExporterProvider since it depends on OkHttp + .filter(p -> !OtlpSpanExporterProvider.class.getName().equals(p)) + .collect(toList()); // filter out OtlpSpanExporterProvider since it depends on OkHttp if (!spanExporterProviders.isEmpty()) { services.produce( new ServiceProviderBuildItem(ConfigurableSpanExporterProvider.class.getName(), spanExporterProviders)); @@ -160,8 +164,26 @@ void handleServices(OTelBuildConfig config, Set.of("META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider"))); } + final List metricExporterProviders = ServiceUtil.classNamesNamedIn( + Thread.currentThread().getContextClassLoader(), + SPI_ROOT + ConfigurableMetricExporterProvider.class.getName()) + .stream() + .filter(p -> !OtlpMetricExporterProvider.class.getName().equals(p)) + .collect(toList()); // filter out OtlpMetricExporterProvider since it depends on OkHttp + if (!metricExporterProviders.isEmpty()) { + services.produce( + new ServiceProviderBuildItem(ConfigurableMetricExporterProvider.class.getName(), metricExporterProviders)); + } + if (config.metrics().exporter().stream().noneMatch(ExporterType.Constants.OTLP_VALUE::equals)) { + removedResources.produce(new RemovedResourceBuildItem( + ArtifactKey.fromString("io.opentelemetry:opentelemetry-exporter-otlp"), + Set.of("META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider"))); + } + runtimeReinitialized.produce( new RuntimeReinitializedClassBuildItem("io.opentelemetry.sdk.autoconfigure.TracerProviderConfiguration")); + runtimeReinitialized.produce( + new RuntimeReinitializedClassBuildItem("io.opentelemetry.sdk.autoconfigure.MeterProviderConfiguration")); services.produce(ServiceProviderBuildItem.allProvidersFromClassPath( ConfigurableSamplerProvider.class.getName())); diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterProcessor.java index 4365478d1183c..1b6eac1ebadd4 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterProcessor.java @@ -13,8 +13,10 @@ import org.jboss.jandex.ParameterizedType; import org.jboss.jandex.Type; +import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.deployment.annotations.*; import io.quarkus.deployment.annotations.Record; @@ -22,16 +24,18 @@ import io.quarkus.opentelemetry.runtime.config.build.exporter.OtlpExporterBuildConfig; import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig; import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig; -import io.quarkus.opentelemetry.runtime.exporter.otlp.LateBoundBatchSpanProcessor; import io.quarkus.opentelemetry.runtime.exporter.otlp.OTelExporterRecorder; +import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundBatchSpanProcessor; import io.quarkus.tls.TlsConfigurationRegistry; import io.quarkus.tls.TlsRegistryBuildItem; import io.quarkus.vertx.core.deployment.CoreVertxBuildItem; -@BuildSteps(onlyIf = OtlpExporterProcessor.OtlpExporterEnabled.class) +@BuildSteps public class OtlpExporterProcessor { - static class OtlpExporterEnabled implements BooleanSupplier { + private static final DotName METRIC_EXPORTER = DotName.createSimple(MetricExporter.class.getName()); + + static class OtlpTracingExporterEnabled implements BooleanSupplier { OtlpExporterBuildConfig exportBuildConfig; OTelBuildConfig otelBuildConfig; @@ -43,8 +47,20 @@ public boolean getAsBoolean() { } } + static class OtlpMetricsExporterEnabled implements BooleanSupplier { + OtlpExporterBuildConfig exportBuildConfig; + OTelBuildConfig otelBuildConfig; + + public boolean getAsBoolean() { + return otelBuildConfig.enabled() && + otelBuildConfig.metrics().enabled().orElse(Boolean.TRUE) && + otelBuildConfig.metrics().exporter().contains(CDI_VALUE) && + exportBuildConfig.enabled(); + } + } + @SuppressWarnings("deprecation") - @BuildStep + @BuildStep(onlyIf = OtlpExporterProcessor.OtlpTracingExporterEnabled.class) @Record(ExecutionTime.RUNTIME_INIT) @Consume(TlsRegistryBuildItem.class) void createBatchSpanProcessor(OTelExporterRecorder recorder, @@ -70,4 +86,41 @@ void createBatchSpanProcessor(OTelExporterRecorder recorder, vertxBuildItem.getVertx())) .done()); } + + @BuildStep(onlyIf = OtlpMetricsExporterEnabled.class) + @Record(ExecutionTime.RUNTIME_INIT) + @Consume(TlsRegistryBuildItem.class) + void createMetricsExporterProcessor( + BeanDiscoveryFinishedBuildItem beanDiscovery, + OTelExporterRecorder recorder, + List externalOtelExporterBuildItem, + OTelRuntimeConfig otelRuntimeConfig, + OtlpExporterRuntimeConfig exporterRuntimeConfig, + CoreVertxBuildItem vertxBuildItem, + BuildProducer syntheticBeanBuildItemBuildProducer) { + + if (!externalOtelExporterBuildItem.isEmpty()) { + // if there is an external exporter, we don't want to create the default one. + // External exporter also use synthetic beans. However, synthetic beans don't show in the BeanDiscoveryFinishedBuildItem + return; + } + + if (!beanDiscovery.beanStream().withBeanType(METRIC_EXPORTER).isEmpty()) { + // if there is a MetricExporter bean impl around, we don't want to create the default one + return; + } + + syntheticBeanBuildItemBuildProducer.produce(SyntheticBeanBuildItem + .configure(MetricExporter.class) + .types(MetricExporter.class) + .setRuntimeInit() + .scope(Singleton.class) + .unremovable() + .addInjectionPoint(ParameterizedType.create(DotName.createSimple(Instance.class), + new Type[] { ClassType.create(DotName.createSimple(MetricExporter.class.getName())) }, null)) + .addInjectionPoint(ClassType.create(DotName.createSimple(TlsConfigurationRegistry.class))) + .createWith(recorder.createMetricExporter(otelRuntimeConfig, exporterRuntimeConfig, + vertxBuildItem.getVertx())) + .done()); + } } diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/metric/MetricProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/metric/MetricProcessor.java new file mode 100644 index 0000000000000..e4af5ce8cfeae --- /dev/null +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/metric/MetricProcessor.java @@ -0,0 +1,101 @@ +package io.quarkus.opentelemetry.deployment.metric; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.function.BooleanSupplier; +import java.util.function.Function; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; +import org.jboss.jandex.FieldInfo; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.MethodInfo; + +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.MetricReader; +import io.quarkus.arc.deployment.AdditionalBeanBuildItem; +import io.quarkus.arc.deployment.UnremovableBeanBuildItem; +import io.quarkus.arc.processor.DotNames; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig; +import io.quarkus.opentelemetry.runtime.metrics.cdi.MetricsProducer; + +@BuildSteps(onlyIf = MetricProcessor.MetricEnabled.class) +public class MetricProcessor { + private static final DotName METRIC_EXPORTER = DotName.createSimple(MetricExporter.class.getName()); + private static final DotName METRIC_READER = DotName.createSimple(MetricReader.class.getName()); + private static final DotName METRIC_PROCESSOR = DotName.createSimple(MetricProcessor.class.getName()); + + @BuildStep + UnremovableBeanBuildItem ensureProducersAreRetained( + CombinedIndexBuildItem indexBuildItem, + BuildProducer additionalBeans) { + + additionalBeans.produce(AdditionalBeanBuildItem.builder() + .setUnremovable() + .addBeanClass(MetricsProducer.class) + .build()); + + IndexView index = indexBuildItem.getIndex(); + + // Find all known SpanExporters and SpanProcessors + Collection knownClasses = new HashSet<>(); + knownClasses.add(METRIC_EXPORTER.toString()); + index.getAllKnownImplementors(METRIC_EXPORTER) + .forEach(classInfo -> knownClasses.add(classInfo.name().toString())); + + knownClasses.add(METRIC_READER.toString()); + index.getAllKnownImplementors(METRIC_READER) + .forEach(classInfo -> knownClasses.add(classInfo.name().toString())); + + knownClasses.add(METRIC_PROCESSOR.toString()); + index.getAllKnownImplementors(METRIC_PROCESSOR) + .forEach(classInfo -> knownClasses.add(classInfo.name().toString())); + + Set retainProducers = new HashSet<>(); + + for (AnnotationInstance annotation : index.getAnnotations(DotNames.PRODUCES)) { + AnnotationTarget target = annotation.target(); + switch (target.kind()) { + case METHOD: + MethodInfo method = target.asMethod(); + String returnType = method.returnType().name().toString(); + if (knownClasses.contains(returnType)) { + retainProducers.add(method.declaringClass().name().toString()); + } + break; + case FIELD: + FieldInfo field = target.asField(); + String fieldType = field.type().name().toString(); + if (knownClasses.contains(fieldType)) { + retainProducers.add(field.declaringClass().name().toString()); + } + break; + default: + break; + } + } + + return new UnremovableBeanBuildItem(new UnremovableBeanBuildItem.BeanClassNamesExclusion(retainProducers)); + } + + public static class MetricEnabled implements BooleanSupplier { + OTelBuildConfig otelBuildConfig; + + public boolean getAsBoolean() { + return otelBuildConfig.metrics().enabled() + .map(new Function() { + @Override + public Boolean apply(Boolean enabled) { + return otelBuildConfig.enabled() && enabled; + } + }) + .orElseGet(() -> otelBuildConfig.enabled()); + } + } +} diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/TracerEnabled.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/TracerEnabled.java index 96478719d5f3d..5102a51329bbc 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/TracerEnabled.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/tracing/TracerEnabled.java @@ -9,12 +9,13 @@ public class TracerEnabled implements BooleanSupplier { OTelBuildConfig otelConfig; public boolean getAsBoolean() { - return otelConfig.traces().enabled().map(new Function() { - @Override - public Boolean apply(Boolean tracerEnabled) { - return otelConfig.enabled() && tracerEnabled; - } - }) + return otelConfig.traces().enabled() + .map(new Function() { + @Override + public Boolean apply(Boolean tracerEnabled) { + return otelConfig.enabled() && tracerEnabled; + } + }) .orElseGet(() -> otelConfig.enabled()); } } diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryContinuousTestingTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryContinuousTestingTest.java index a1fb1c731deec..bc6bf553f91d2 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryContinuousTestingTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryContinuousTestingTest.java @@ -10,6 +10,7 @@ import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; import io.quarkus.opentelemetry.deployment.common.TracerRouter; +import io.quarkus.opentelemetry.deployment.traces.TracerRouterUT; import io.quarkus.test.ContinuousTestingTestUtils; import io.quarkus.test.ContinuousTestingTestUtils.TestStatus; import io.quarkus.test.QuarkusDevModeTest; @@ -23,7 +24,8 @@ public class OpenTelemetryContinuousTestingTest { .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") .add(new StringAsset(ContinuousTestingTestUtils.appProperties( - "quarkus.otel.traces.exporter=test-span-exporter")), + "quarkus.otel.traces.exporter=test-span-exporter", + "quarkus.otel.metrics.exporter=none")), "application.properties")) .setTestArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .addClass(TracerRouterUT.class)); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDestroyerTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDestroyerTest.java index d0fe61b90b23d..b8b72ee86db39 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDestroyerTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDestroyerTest.java @@ -26,8 +26,11 @@ public class OpenTelemetryDestroyerTest { .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") .add(new StringAsset( - "quarkus.otel.traces.exporter=test-span-exporter\n" + - "quarkus.otel.experimental.shutdown-wait-time=PT60S\n"), + """ + quarkus.otel.traces.exporter=test-span-exporter + quarkus.otel.metrics.exporter=none + quarkus.otel.experimental.shutdown-wait-time=PT60S + """), "application.properties")); @Test diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevModeTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevModeTest.java index 87b94744802ca..b32aab8c4acac 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevModeTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevModeTest.java @@ -23,7 +23,8 @@ public class OpenTelemetryDevModeTest { .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") .add(new StringAsset(ContinuousTestingTestUtils.appProperties( - "quarkus.otel.traces.exporter=test-span-exporter")), "application.properties")); + "quarkus.otel.traces.exporter=test-span-exporter", + "quarkus.otel.metrics.exporter=none")), "application.properties")); @Test void testDevMode() { diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevServicesDatasourcesTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevServicesDatasourcesTest.java index 1a7f03ab3d8e1..2eb1130ae02af 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevServicesDatasourcesTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDevServicesDatasourcesTest.java @@ -41,11 +41,14 @@ public class OpenTelemetryDevServicesDatasourcesTest { .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") .add(new StringAsset( - "quarkus.datasource.db-kind=h2\n" + - "quarkus.datasource.jdbc.telemetry=true\n" + - "quarkus.otel.traces.exporter=test-span-exporter\n" + - "quarkus.otel.bsp.export.timeout=1s\n" + - "quarkus.otel.bsp.schedule.delay=50\n"), + """ + quarkus.datasource.db-kind=h2 + quarkus.datasource.jdbc.telemetry=true + quarkus.otel.traces.exporter=test-span-exporter + quarkus.otel.metrics.exporter=none + quarkus.otel.bsp.export.timeout=1s + quarkus.otel.bsp.schedule.delay=50 + """), "application.properties")); @Test diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDisabledSdkTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDisabledSdkTest.java index a239051302a0e..d77a2f14dbbb2 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDisabledSdkTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryDisabledSdkTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.opentelemetry.api.OpenTelemetry; -import io.quarkus.opentelemetry.runtime.exporter.otlp.LateBoundBatchSpanProcessor; +import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundBatchSpanProcessor; import io.quarkus.test.QuarkusUnitTest; @Disabled("Not implemented") diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryMDCTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryMDCTest.java index 103ea2c4a531c..572a020cb89b7 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryMDCTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryMDCTest.java @@ -28,6 +28,8 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.sdk.trace.data.SpanData; import io.quarkus.arc.Unremovable; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; import io.quarkus.test.QuarkusUnitTest; @@ -40,9 +42,12 @@ public class OpenTelemetryMDCTest { .addClass(MdcEntry.class) .addClass(TestMdcCapturer.class) .addClass(TestResource.class) - .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) + .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class, + InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider")) .withConfigurationResource("application-default.properties"); @Inject diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryResourceTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryResourceTest.java index 92c919c44fe80..f1ee913e35cd6 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryResourceTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryResourceTest.java @@ -13,12 +13,17 @@ import jakarta.ws.rs.Path; import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; import io.quarkus.test.QuarkusUnitTest; @@ -29,17 +34,21 @@ public class OpenTelemetryResourceTest { @RegisterExtension static final QuarkusUnitTest TEST = new QuarkusUnitTest().setArchiveProducer( () -> ShrinkWrap.create(JavaArchive.class) - .addClass(TestSpanExporter.class) - .addClass(TestSpanExporterProvider.class) + .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class, + InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) .addAsResource("resource-config/application.properties", "application.properties") .addAsResource( "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")); + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider")); @Inject SmallRyeConfig config; @Inject TestSpanExporter spanExporter; + @Inject + InMemoryMetricExporter metricExporter; @Test void resource() { @@ -56,12 +65,19 @@ void resource() { assertEquals(config.getRawValue("quarkus.uuid"), server.getResource().getAttribute(AttributeKey.stringKey("service.instance.id"))); assertNotNull(server.getResource().getAttribute(AttributeKey.stringKey("host.name"))); + + metricExporter.assertCountAtLeast(1); + List finishedMetricItems = metricExporter.getFinishedMetricItems(); } @Path("/hello") public static class HelloResource { + @Inject + Meter meter; + @GET public String hello() { + meter.counterBuilder("hello").build().add(1); return "hello"; } } diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryMetricExporter.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryMetricExporter.java new file mode 100644 index 0000000000000..59bd8aa6f4399 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryMetricExporter.java @@ -0,0 +1,209 @@ +package io.quarkus.opentelemetry.deployment.common; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toMap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Assertions; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.PointData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.semconv.SemanticAttributes; +import io.quarkus.arc.Unremovable; + +@Unremovable +@ApplicationScoped +public class InMemoryMetricExporter implements MetricExporter { + + private static final List LEGACY_KEY_COMPONENTS = List.of(SemanticAttributes.HTTP_METHOD.getKey(), + SemanticAttributes.HTTP_ROUTE.getKey(), + SemanticAttributes.HTTP_STATUS_CODE.getKey()); + private static final List KEY_COMPONENTS = List.of(SemanticAttributes.HTTP_REQUEST_METHOD.getKey(), + SemanticAttributes.HTTP_ROUTE.getKey(), + SemanticAttributes.HTTP_RESPONSE_STATUS_CODE.getKey()); + + private final Queue finishedMetricItems = new ConcurrentLinkedQueue<>(); + private final AggregationTemporality aggregationTemporality = AggregationTemporality.CUMULATIVE; + private boolean isStopped = false; + + public static Map getPointAttributes(final MetricData metricData, final String path) { + try { + return metricData.getData().getPoints().stream() + .filter(point -> isPathFound(path, point.getAttributes())) + .map(point -> point.getAttributes()) + .map(attributes1 -> attributes1.asMap()) + .flatMap(map -> map.entrySet().stream()) + .collect(toMap(map -> map.getKey().toString(), map -> map.getValue().toString())); + } catch (Exception e) { + System.out.println("Error getting point attributes for " + metricData.getName()); + metricData.getData().getPoints().stream() + .filter(point -> isPathFound(path, point.getAttributes())) + .map(point -> point.getAttributes()) + .map(attributes1 -> attributes1.asMap()) + .flatMap(map -> map.entrySet().stream()) + .forEach(attributeKeyObjectEntry -> System.out + .println(attributeKeyObjectEntry.getKey() + " " + attributeKeyObjectEntry.getValue())); + throw e; + } + } + + public static Map getMostRecentPointsMap(List finishedMetricItems) { + return finishedMetricItems.stream() + .flatMap(metricData -> metricData.getData().getPoints().stream()) + // exclude data from /export endpoint + .filter(InMemoryMetricExporter::notExporterPointData) + // newer first + .sorted(Comparator.comparingLong(PointData::getEpochNanos).reversed()) + .collect(toMap( + pointData -> pointData.getAttributes().asMap().entrySet().stream() + //valid attributes for the resulting map key + .filter(entry -> { + if (SemconvStability.emitOldHttpSemconv()) { + return LEGACY_KEY_COMPONENTS.contains(entry.getKey().getKey()); + } else { + return KEY_COMPONENTS.contains(entry.getKey().getKey()); + } + }) + // ensure order + .sorted(Comparator.comparing(o -> o.getKey().getKey())) + // build key + .map(entry -> entry.getKey().getKey() + ":" + entry.getValue().toString()) + .collect(joining(",")), + pointData -> pointData, + // most recent points will surface + (older, newer) -> newer)); + } + + /* + * ignore points with /export in the route + */ + private static boolean notExporterPointData(PointData pointData) { + return pointData.getAttributes().asMap().entrySet().stream() + .noneMatch(entry -> entry.getKey().getKey().equals(SemanticAttributes.HTTP_ROUTE.getKey()) && + entry.getValue().toString().contains("/export")); + } + + private static boolean isPathFound(String path, Attributes attributes) { + if (path == null) { + return true;// any match + } + Object value = attributes.asMap().get(AttributeKey.stringKey(SemanticAttributes.HTTP_ROUTE.getKey())); + if (value == null) { + return false; + } + return value.toString().equals(path); + } + + public void assertCount(final int count) { + Awaitility.await().atMost(5, SECONDS) + .untilAsserted(() -> Assertions.assertEquals(count, getFinishedMetricItems().size())); + } + + public void assertCount(final String name, final String target, final int count) { + Awaitility.await().atMost(5, SECONDS) + .untilAsserted(() -> Assertions.assertEquals(count, getFinishedMetricItems(name, target).size())); + } + + public void assertCountAtLeast(final int count) { + Awaitility.await().atMost(5, SECONDS) + .untilAsserted(() -> Assertions.assertTrue(count < getFinishedMetricItems().size())); + } + + public void assertCountAtLeast(final String name, final String target, final int count) { + Awaitility.await().atMost(5, SECONDS) + .untilAsserted(() -> Assertions.assertTrue(count < getFinishedMetricItems(name, target).size())); + } + + /** + * Returns a {@code List} of the finished {@code Metric}s, represented by {@code MetricData}. + * + * @return a {@code List} of the finished {@code Metric}s. + */ + public List getFinishedMetricItems() { + return Collections.unmodifiableList(new ArrayList<>(finishedMetricItems)); + } + + public List getFinishedMetricItems(final String name, final String target) { + return Collections.unmodifiableList(new ArrayList<>( + finishedMetricItems.stream() + .filter(metricData -> metricData.getName().equals(name)) + .filter(metricData -> metricData.getData().getPoints().stream() + .anyMatch(point -> isPathFound(target, point.getAttributes()))) + .collect(Collectors.toList()))); + } + + /** + * Clears the internal {@code List} of finished {@code Metric}s. + * + *

+ * Does not reset the state of this exporter if already shutdown. + */ + public void reset() { + finishedMetricItems.clear(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return aggregationTemporality; + } + + /** + * Exports the collection of {@code Metric}s into the inmemory queue. + * + *

+ * If this is called after {@code shutdown}, this will return {@code ResultCode.FAILURE}. + */ + @Override + public CompletableResultCode export(Collection metrics) { + if (isStopped) { + return CompletableResultCode.ofFailure(); + } + finishedMetricItems.addAll(metrics); + return CompletableResultCode.ofSuccess(); + } + + /** + * The InMemory exporter does not batch metrics, so this method will immediately return with + * success. + * + * @return always Success + */ + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + /** + * Clears the internal {@code List} of finished {@code Metric}s. + * + *

+ * Any subsequent call to export() function on this MetricExporter, will return {@code + * CompletableResultCode.ofFailure()} + */ + @Override + public CompletableResultCode shutdown() { + isStopped = true; + finishedMetricItems.clear(); + return CompletableResultCode.ofSuccess(); + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryMetricExporterProvider.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryMetricExporterProvider.java new file mode 100644 index 0000000000000..6927ccb5ef4ae --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/InMemoryMetricExporterProvider.java @@ -0,0 +1,19 @@ +package io.quarkus.opentelemetry.deployment.common; + +import jakarta.enterprise.inject.spi.CDI; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; +import io.opentelemetry.sdk.metrics.export.MetricExporter; + +public class InMemoryMetricExporterProvider implements ConfigurableMetricExporterProvider { + @Override + public MetricExporter createExporter(ConfigProperties configProperties) { + return CDI.current().select(InMemoryMetricExporter.class).get(); + } + + @Override + public String getName() { + return "in-memory"; + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterConfigTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterConfigTest.java index 6f83f5bb76d1d..3692683973ad0 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterConfigTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterConfigTest.java @@ -15,8 +15,8 @@ public class OtlpExporterConfigTest { @RegisterExtension static final QuarkusUnitTest TEST = new QuarkusUnitTest() .withEmptyApplication() - .overrideConfigKey("otel.traces.exporter", "cdi") - .overrideConfigKey("otel.exporter.otlp.traces.protocol", "http/protobuf") + .overrideConfigKey("quarkus.otel.traces.exporter", "cdi") + .overrideConfigKey("quarkus.otel.exporter.otlp.traces.protocol", "http/protobuf") .overrideConfigKey("quarkus.otel.exporter.otlp.traces.legacy-endpoint", "http://localhost ") .overrideConfigKey("quarkus.otel.bsp.schedule.delay", "50") .overrideConfigKey("quarkus.otel.bsp.export.timeout", "PT1S"); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpTraceExporterDisabledTest.java similarity index 57% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterDisabledTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpTraceExporterDisabledTest.java index 72ffaa6fbada3..7d9e074025c18 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterDisabledTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpTraceExporterDisabledTest.java @@ -1,22 +1,24 @@ package io.quarkus.opentelemetry.deployment.exporter.otlp; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.opentelemetry.api.OpenTelemetry; -import io.quarkus.opentelemetry.runtime.exporter.otlp.LateBoundBatchSpanProcessor; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundBatchSpanProcessor; import io.quarkus.test.QuarkusUnitTest; -public class OtlpExporterDisabledTest { +public class OtlpTraceExporterDisabledTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() .withEmptyApplication() - .overrideConfigKey("otel.traces.exporter", "cdi") .overrideConfigKey("quarkus.otel.exporter.otlp.enabled", "false"); @Inject @@ -25,9 +27,13 @@ public class OtlpExporterDisabledTest { @Inject Instance lateBoundBatchSpanProcessorInstance; + @Inject + Instance metricExporters; + @Test void testOpenTelemetryButNoBatchSpanProcessor() { - Assertions.assertNotNull(openTelemetry); - Assertions.assertFalse(lateBoundBatchSpanProcessorInstance.isResolvable()); + assertNotNull(openTelemetry); + assertFalse(lateBoundBatchSpanProcessorInstance.isResolvable()); + assertFalse(metricExporters.isResolvable()); } } diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GraphQLOpenTelemetryTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GraphQLOpenTelemetryTest.java index b685abb0ec4c4..293e5279f2fb3 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GraphQLOpenTelemetryTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GraphQLOpenTelemetryTest.java @@ -62,6 +62,7 @@ public class GraphQLOpenTelemetryTest { .addAsResource(new StringAsset("smallrye.graphql.allowGet=true"), "application.properties") .addAsResource(new StringAsset("smallrye.graphql.printDataFetcherException=true"), "application.properties") .addAsResource(new StringAsset("smallrye.graphql.events.enabled=true"), "application.properties") + .addAsResource(new StringAsset("quarkus.otel.metrics.exporter=none"), "application.properties") .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenInstrumentationDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenInstrumentationDisabledTest.java index 5c6bb07a37f23..6b1a4af1b4c82 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenInstrumentationDisabledTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenInstrumentationDisabledTest.java @@ -30,6 +30,8 @@ import io.quarkus.opentelemetry.deployment.HelloRequest; import io.quarkus.opentelemetry.deployment.HelloRequestOrBuilder; import io.quarkus.opentelemetry.deployment.MutinyGreeterGrpc; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; import io.quarkus.test.QuarkusUnitTest; @@ -41,13 +43,16 @@ public class GrpcOpenInstrumentationDisabledTest { static final QuarkusUnitTest TEST = new QuarkusUnitTest() .withApplicationRoot(root -> root .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) + .addClasses(InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) .addClasses(HelloService.class) .addClasses(GreeterGrpc.class, MutinyGreeterGrpc.class, Greeter.class, GreeterBean.class, GreeterClient.class, HelloProto.class, HelloRequest.class, HelloRequestOrBuilder.class, HelloReply.class, HelloReplyOrBuilder.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider")) .withConfigurationResource("application-default.properties") .overrideConfigKey("quarkus.grpc.clients.hello.host", "localhost") .overrideConfigKey("quarkus.grpc.clients.hello.port", "9001") diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenTelemetryTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenTelemetryTest.java index e9d3d4c42c7b2..f2fc2c2c62638 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenTelemetryTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/GrpcOpenTelemetryTest.java @@ -58,6 +58,8 @@ import io.quarkus.opentelemetry.deployment.StreamingClient; import io.quarkus.opentelemetry.deployment.StreamingGrpc; import io.quarkus.opentelemetry.deployment.StreamingProto; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; import io.quarkus.opentelemetry.deployment.common.SemconvResolver; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; @@ -70,6 +72,7 @@ public class GrpcOpenTelemetryTest { static final QuarkusUnitTest TEST = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class, SemconvResolver.class) + .addClasses(InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) .addClasses(HelloService.class) .addClasses(GreeterGrpc.class, MutinyGreeterGrpc.class, Greeter.class, GreeterBean.class, GreeterClient.class, @@ -80,7 +83,9 @@ public class GrpcOpenTelemetryTest { Streaming.class, StreamingBean.class, StreamingClient.class, StreamingProto.class, Item.class, ItemOrBuilder.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider")) .withConfigurationResource("application-default.properties") .overrideConfigKey("quarkus.grpc.clients.greeter.host", "localhost") .overrideConfigKey("quarkus.grpc.clients.greeter.port", "9001") diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/RestClientOpenTelemetryTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/RestClientOpenTelemetryTest.java index 23730f0360408..d0ecccaa1dd57 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/RestClientOpenTelemetryTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/RestClientOpenTelemetryTest.java @@ -36,6 +36,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; import io.quarkus.opentelemetry.deployment.common.SemconvResolver; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; @@ -46,8 +48,11 @@ public class RestClientOpenTelemetryTest { @RegisterExtension static final QuarkusUnitTest TEST = new QuarkusUnitTest().withApplicationRoot((jar) -> jar .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class, SemconvResolver.class) + .addClasses(InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider")) .withConfigurationResource("application-default.properties") .overrideConfigKey("quarkus.rest-client.client.url", "${test.url}"); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxClientOpenTelemetryTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxClientOpenTelemetryTest.java index bfde0e210a3e5..fc828e8ccfbb6 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxClientOpenTelemetryTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxClientOpenTelemetryTest.java @@ -31,6 +31,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; import io.quarkus.opentelemetry.deployment.common.SemconvResolver; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; @@ -50,8 +52,11 @@ public class VertxClientOpenTelemetryTest { static final QuarkusUnitTest TEST = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class, SemconvResolver.class) + .addClasses(InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider")) .withConfigurationResource("application-default.properties"); @Inject diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxOpenTelemetryForwardedTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxOpenTelemetryForwardedTest.java index b7cb2da17bc83..5ecf22ad53dbb 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxOpenTelemetryForwardedTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxOpenTelemetryForwardedTest.java @@ -15,6 +15,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; import io.quarkus.opentelemetry.deployment.common.SemconvResolver; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; @@ -28,8 +30,11 @@ public class VertxOpenTelemetryForwardedTest { .withApplicationRoot((jar) -> jar .addClass(TracerRouter.class) .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class, SemconvResolver.class) + .addClasses(InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider")) .withConfigurationResource("application-default.properties"); @Inject diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxOpenTelemetryXForwardedTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxOpenTelemetryXForwardedTest.java index bbf97ebedb203..44c648d102ea2 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxOpenTelemetryXForwardedTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/instrumentation/VertxOpenTelemetryXForwardedTest.java @@ -15,6 +15,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; import io.quarkus.opentelemetry.deployment.common.SemconvResolver; import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; @@ -28,8 +30,11 @@ public class VertxOpenTelemetryXForwardedTest { .withApplicationRoot((jar) -> jar .addClass(TracerRouter.class) .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class, SemconvResolver.class) + .addClasses(InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), - "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider")) .withConfigurationResource("application-default.properties"); @Inject diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/AddingSpanAttributesInterceptorTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/AddingSpanAttributesInterceptorTest.java index 9853edf6aa817..eec6fb9ebd107 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/AddingSpanAttributesInterceptorTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/AddingSpanAttributesInterceptorTest.java @@ -38,7 +38,7 @@ public class AddingSpanAttributesInterceptorTest { .addAsManifestResource( "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", "services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") - .addAsResource("resource-config/application.properties", "application.properties")); + .addAsResource("resource-config/application-no-metrics.properties", "application.properties")); @Inject HelloRouter helloRouter; diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanInterceptorTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanInterceptorTest.java index b7a0796320eb8..a27ffefde3dc1 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanInterceptorTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanInterceptorTest.java @@ -53,7 +53,7 @@ public class WithSpanInterceptorTest { .addAsManifestResource( "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", "services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") - .addAsResource("resource-config/application.properties", "application.properties")); + .addAsResource("resource-config/application-no-metrics.properties", "application.properties")); @Inject SpanBean spanBean; diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanLegacyInterceptorTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanLegacyInterceptorTest.java index a38c18c57f626..6e334e0797421 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanLegacyInterceptorTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/interceptor/WithSpanLegacyInterceptorTest.java @@ -44,7 +44,7 @@ public class WithSpanLegacyInterceptorTest { .addAsManifestResource( "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", "services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") - .addAsResource("resource-config/application.properties", "application.properties")); + .addAsResource("resource-config/application-no-metrics.properties", "application.properties")); @Inject SpanBean spanBean; diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/GaugeCdiTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/GaugeCdiTest.java new file mode 100644 index 0000000000000..ae133ff6a805d --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/GaugeCdiTest.java @@ -0,0 +1,85 @@ +package io.quarkus.opentelemetry.deployment.metrics; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; +import io.quarkus.opentelemetry.deployment.common.TestUtil; +import io.quarkus.test.QuarkusUnitTest; + +public class GaugeCdiTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class) + .addClass(TestUtil.class) + .addClass(MeterBean.class) + .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) + .addClasses(InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) + .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider") + .add(new StringAsset( + "quarkus.otel.metrics.enabled=true\n" + + "quarkus.datasource.db-kind=h2\n" + + "quarkus.datasource.jdbc.telemetry=true\n" + + "quarkus.otel.traces.exporter=test-span-exporter\n" + + "quarkus.otel.metrics.exporter=in-memory\n" + + "quarkus.otel.metric.export.interval=300ms\n" + + "quarkus.otel.bsp.export.timeout=1s\n" + + "quarkus.otel.bsp.schedule.delay=50\n"), + "application.properties")); + + @Inject + MeterBean meterBean; + + @Inject + InMemoryMetricExporter exporter; + + @BeforeEach + void setUp() { + exporter.reset(); + } + + @Test + void gauge() throws InterruptedException { + meterBean.getMeter() + .gaugeBuilder("jvm.memory.total") + .setDescription("Reports JVM memory usage.") + .setUnit("byte") + .buildWithCallback( + result -> result.record(Runtime.getRuntime().totalMemory(), Attributes.empty())); + exporter.assertCountAtLeast("jvm.memory.total", null, 1); + assertNotNull(exporter.getFinishedMetricItems("jvm.memory.total", null).get(0)); + } + + @Test + void meter() { + assertNotNull(meterBean.getMeter()); + } + + @ApplicationScoped + public static class MeterBean { + @Inject + Meter meter; + + public Meter getMeter() { + return meter; + } + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/MetricsDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/MetricsDisabledTest.java new file mode 100644 index 0000000000000..41196b759df8f --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/MetricsDisabledTest.java @@ -0,0 +1,30 @@ +package io.quarkus.opentelemetry.deployment.metrics; + +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.metrics.Meter; +import io.quarkus.test.QuarkusUnitTest; + +public class MetricsDisabledTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.otel.metrics.enabled", "false") + .assertException(t -> Assertions.assertEquals(DeploymentException.class, t.getClass())); + + @Inject + Meter openTelemetryMeter; + + @Test + void testNoOpenTelemetry() { + //Should not be reached: dump what was injected if it somehow passed + Assertions.assertNull(openTelemetryMeter, + "A OpenTelemetry Meter instance should not be found/injected when OpenTelemetry metrics is disabled"); + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/MetricsTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/MetricsTest.java new file mode 100644 index 0000000000000..9c6ed9e2191bc --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/metrics/MetricsTest.java @@ -0,0 +1,385 @@ +package io.quarkus.opentelemetry.deployment.metrics; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import jakarta.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleCounter; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporter; +import io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.TestSpanExporterProvider; +import io.quarkus.test.QuarkusUnitTest; + +public class MetricsTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class) + .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) + .addClasses(InMemoryMetricExporter.class, InMemoryMetricExporterProvider.class) + .addAsResource(new StringAsset(InMemoryMetricExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider") + .add(new StringAsset( + "quarkus.otel.metrics.enabled=true\n" + + "quarkus.otel.traces.exporter=none\n" + + "quarkus.otel.metrics.exporter=in-memory\n" + + "quarkus.otel.metric.export.interval=300ms\n"), + "application.properties")); + + @Inject + protected Meter meter; + @Inject + protected InMemoryMetricExporter metricExporter; + + protected static String mapToString(Map, ?> map) { + return (String) map.keySet().stream() + .map(key -> "" + key.getKey() + "=" + map.get(key)) + .collect(Collectors.joining(", ", "{", "}")); + } + + @BeforeEach + void setUp() { + metricExporter.reset(); + } + + @Test + void asyncDoubleCounter() { + final String counterName = "testAsyncDoubleCounter"; + final String counterDescription = "Testing double counter"; + final String counterUnit = "Metric Tonnes"; + assertNotNull( + meter.counterBuilder(counterName) + .ofDoubles() + .setDescription(counterDescription) + .setUnit(counterUnit) + .buildWithCallback(measurement -> { + measurement.record(1, Attributes.empty()); + })); + + metricExporter.assertCountAtLeast(counterName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(counterName, null).get(0); + + assertEquals(metric.getName(), counterName); + assertEquals(metric.getDescription(), counterDescription); + assertEquals(metric.getUnit(), counterUnit); + + assertEquals(metric.getDoubleSumData() + .getPoints() + .stream() + .findFirst() + .get() + .getValue(), 1); + } + + @Test + void asyncLongCounter() { + final String counterName = "testAsyncLongCounter"; + final String counterDescription = "Testing Async long counter"; + final String counterUnit = "Metric Tonnes"; + assertNotNull( + meter.counterBuilder(counterName) + .setDescription(counterDescription) + .setUnit(counterUnit) + .buildWithCallback(measurement -> { + measurement.record(1, Attributes.empty()); + })); + + metricExporter.assertCountAtLeast(counterName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(counterName, null).get(0); + + assertEquals(metric.getName(), counterName); + assertEquals(metric.getDescription(), counterDescription); + assertEquals(metric.getUnit(), counterUnit); + + assertEquals(metric.getLongSumData() + .getPoints() + .stream() + .findFirst() + .get() + .getValue(), 1); + } + + @Test + void doubleCounter() { + final String counterName = "testDoubleCounter"; + final String counterDescription = "Testing double counter"; + final String counterUnit = "Metric Tonnes"; + + final double doubleWithAttributes = 20.2; + final double doubleWithoutAttributes = 10.1; + DoubleCounter doubleCounter = meter.counterBuilder(counterName) + .ofDoubles() + .setDescription(counterDescription) + .setUnit(counterUnit) + .build(); + assertNotNull(doubleCounter); + + Map expectedResults = new HashMap(); + expectedResults.put(doubleWithAttributes, Attributes.builder().put("K", "V").build()); + expectedResults.put(doubleWithoutAttributes, Attributes.empty()); + expectedResults.keySet().stream() + .forEach(key -> doubleCounter.add(key, expectedResults.get(key))); + + metricExporter.assertCountAtLeast(counterName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(counterName, null).get(0); + + assertEquals(metric.getName(), counterName); + assertEquals(metric.getDescription(), counterDescription); + assertEquals(metric.getUnit(), counterUnit); + + metric.getDoubleSumData().getPoints().stream() + .forEach(point -> { + assertTrue(expectedResults.containsKey(point.getValue()), + "Double" + point.getValue() + " was not an expected result"); + assertTrue(point.getAttributes().equals(expectedResults.get(point.getValue())), + "Attributes were not equal." + + System.lineSeparator() + "Actual values: " + + mapToString(point.getAttributes().asMap()) + + System.lineSeparator() + "Expected values: " + + mapToString(expectedResults.get(point.getValue()).asMap())); + }); + + } + + @Test + void doubleGauge() { + final String gaugeName = "testDoubleGauge"; + final String gaugeDescription = "Testing double gauge"; + final String gaugeUnit = "ms"; + assertNotNull( + meter.gaugeBuilder(gaugeName) + .setDescription(gaugeDescription) + .setUnit("ms") + .buildWithCallback(measurement -> { + measurement.record(1, Attributes.empty()); + })); + + metricExporter.assertCountAtLeast(gaugeName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(gaugeName, null).get(0); + + assertEquals(metric.getName(), gaugeName); + assertEquals(metric.getDescription(), gaugeDescription); + assertEquals(metric.getUnit(), gaugeUnit); + + assertEquals(metric.getDoubleGaugeData() + .getPoints() + .stream() + .findFirst() + .get() + .getValue(), 1); + } + + @Test + void doubleHistogram() { + final String histogramName = "testDoubleHistogram"; + final String histogramDescription = "Testing double histogram"; + final String histogramUnit = "Metric Tonnes"; + + final double doubleWithAttributes = 20; + final double doubleWithoutAttributes = 10; + DoubleHistogram doubleHistogram = meter.histogramBuilder(histogramName) + .setDescription(histogramDescription) + .setUnit(histogramUnit) + .build(); + assertNotNull(doubleHistogram); + + Map expectedResults = new HashMap(); + expectedResults.put(doubleWithAttributes, Attributes.builder().put("K", "V").build()); + expectedResults.put(doubleWithoutAttributes, Attributes.empty()); + expectedResults.keySet().stream() + .forEach(key -> doubleHistogram.record(key, expectedResults.get(key))); + + metricExporter.assertCountAtLeast(histogramName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(histogramName, null).get(0); + + assertEquals(metric.getName(), histogramName); + assertEquals(metric.getDescription(), histogramDescription); + assertEquals(metric.getUnit(), histogramUnit); + + metric.getHistogramData().getPoints().stream() + .forEach(point -> { + assertTrue(expectedResults.containsKey(point.getSum()), + "Double " + point.getSum() + " was not an expected result"); + assertTrue(point.getAttributes().equals(expectedResults.get(point.getSum())), + "Attributes were not equal." + + System.lineSeparator() + "Actual values: " + + mapToString(point.getAttributes().asMap()) + + System.lineSeparator() + "Expected values: " + + mapToString(expectedResults.get(point.getSum()).asMap())); + }); + } + + @Test + void longCounter() { + final String counterName = "testLongCounter"; + final String counterDescription = "Testing long counter"; + final String counterUnit = "Metric Tonnes"; + + final long longWithAttributes = 24; + final long longWithoutAttributes = 12; + LongCounter longCounter = meter.counterBuilder(counterName) + .setDescription(counterDescription) + .setUnit(counterUnit) + .build(); + assertNotNull(longCounter); + + Map expectedResults = new HashMap(); + expectedResults.put(longWithAttributes, Attributes.builder().put("K", "V").build()); + expectedResults.put(longWithoutAttributes, Attributes.empty()); + expectedResults.keySet().stream().forEach(key -> longCounter.add(key, expectedResults.get(key))); + + metricExporter.assertCountAtLeast(counterName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(counterName, null).get(0); + + assertEquals(metric.getName(), counterName); + assertEquals(metric.getDescription(), counterDescription); + assertEquals(metric.getUnit(), counterUnit); + + metric.getLongSumData().getPoints().stream() + .forEach(point -> { + assertTrue(expectedResults.containsKey(point.getValue()), + "Long" + point.getValue() + " was not an expected result"); + assertTrue(point.getAttributes().equals(expectedResults.get(point.getValue())), + "Attributes were not equal." + + System.lineSeparator() + "Actual values: " + + mapToString(point.getAttributes().asMap()) + + System.lineSeparator() + "Expected values: " + + mapToString(expectedResults.get(point.getValue()).asMap())); + }); + } + + @Test + void longGauge() { + final String gaugeName = "testLongGauge"; + final String gaugeDescription = "Testing long gauge"; + final String gaugeUnit = "ms"; + assertNotNull( + meter.gaugeBuilder(gaugeName) + .ofLongs() + .setDescription(gaugeDescription) + .setUnit("ms") + .buildWithCallback(measurement -> { + measurement.record(1, Attributes.empty()); + })); + + metricExporter.assertCountAtLeast(gaugeName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(gaugeName, null).get(0); + + assertEquals(metric.getName(), gaugeName); + assertEquals(metric.getDescription(), gaugeDescription); + assertEquals(metric.getUnit(), gaugeUnit); + + assertEquals(metric.getLongGaugeData() + .getPoints() + .stream() + .findFirst() + .get() + .getValue(), 1); + } + + @Test + void longHistogram() { + final String histogramName = "testLongHistogram"; + final String histogramDescription = "Testing long histogram"; + final String histogramUnit = "Metric Tonnes"; + + final long longWithAttributes = 20; + final long longWithoutAttributes = 10; + LongHistogram longHistogram = meter.histogramBuilder(histogramName) + .ofLongs() + .setDescription(histogramDescription) + .setUnit(histogramUnit) + .build(); + assertNotNull(longHistogram); + + Map expectedResults = new HashMap(); + expectedResults.put(longWithAttributes, Attributes.builder().put("K", "V").build()); + expectedResults.put(longWithoutAttributes, Attributes.empty()); + + expectedResults.keySet().stream() + .forEach(key -> longHistogram.record(key, expectedResults.get(key))); + + metricExporter.assertCountAtLeast(histogramName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(histogramName, null).get(0); + + assertEquals(metric.getName(), histogramName); + assertEquals(metric.getDescription(), histogramDescription); + assertEquals(metric.getUnit(), histogramUnit); + + metric.getHistogramData().getPoints().stream() + .forEach(point -> { + assertTrue(expectedResults.containsKey((long) point.getSum()), + "Long " + (long) point.getSum() + " was not an expected result"); + assertTrue(point.getAttributes().equals(expectedResults.get((long) point.getSum())), + "Attributes were not equal." + + System.lineSeparator() + "Actual values: " + + mapToString(point.getAttributes().asMap()) + + System.lineSeparator() + "Expected values: " + + mapToString(expectedResults.get((long) point.getSum()).asMap())); + }); + } + + @Test + void longUpDownCounter() { + final String counterName = "testLongUpDownCounter"; + final String counterDescription = "Testing long up down counter"; + final String counterUnit = "Metric Tonnes"; + + final long longWithAttributes = -20; + final long longWithoutAttributes = -10; + LongUpDownCounter longUpDownCounter = meter.upDownCounterBuilder(counterName) + .setDescription(counterDescription) + .setUnit(counterUnit) + .build(); + assertNotNull(longUpDownCounter); + + Map expectedResults = new HashMap(); + expectedResults.put(longWithAttributes, Attributes.builder().put("K", "V").build()); + expectedResults.put(longWithoutAttributes, Attributes.empty()); + + expectedResults.keySet().stream() + .forEach(key -> longUpDownCounter.add(key, expectedResults.get(key))); + + metricExporter.assertCountAtLeast(counterName, null, 1); + MetricData metric = metricExporter.getFinishedMetricItems(counterName, null).get(0); + + assertEquals(metric.getName(), counterName); + assertEquals(metric.getDescription(), counterDescription); + assertEquals(metric.getUnit(), counterUnit); + + metric.getDoubleSumData().getPoints().stream() + .forEach(point -> { + assertTrue(expectedResults.containsKey(point.getValue()), + "Long" + point.getValue() + " was not an expected result"); + assertTrue(point.getAttributes().equals(expectedResults.get(point.getValue())), + "Attributes were not equal." + + System.lineSeparator() + "Actual values: " + + mapToString(point.getAttributes().asMap()) + + System.lineSeparator() + "Expected values: " + + mapToString(expectedResults.get(point.getValue()).asMap())); + }); + + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTextMapPropagatorCustomizerTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/propagation/OpenTelemetryTextMapPropagatorCustomizerTest.java similarity index 96% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTextMapPropagatorCustomizerTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/propagation/OpenTelemetryTextMapPropagatorCustomizerTest.java index a230d019f7c41..abb2a1c762f9d 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryTextMapPropagatorCustomizerTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/propagation/OpenTelemetryTextMapPropagatorCustomizerTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.propagation; import static io.quarkus.opentelemetry.deployment.common.TestSpanExporter.getSpanByKindAndParentId; import static org.assertj.core.api.Assertions.assertThat; @@ -39,7 +39,7 @@ public class OpenTelemetryTextMapPropagatorCustomizerTest { .addClass(TestSpanExporter.class) .addClass(TestSpanExporterProvider.class) .addClass(TestTextMapPropagatorCustomizer.class) - .addAsResource("resource-config/application.properties", "application.properties") + .addAsResource("resource-config/application-no-metrics.properties", "application.properties") .addAsResource( "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsDisabledTest.java similarity index 92% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsDisabledTest.java index 0cf4e370432fe..cd0a044062461 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsDisabledTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -23,7 +23,7 @@ public class NonAppEndpointsDisabledTest { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties"); + .withConfigurationResource("resource-config/application-no-metrics.properties"); @Inject TestSpanExporter testSpanExporter; diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledWithRootPathTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsDisabledWithRootPathTest.java similarity index 93% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledWithRootPathTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsDisabledWithRootPathTest.java index c468f987e7690..48589c7abb23b 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledWithRootPathTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsDisabledWithRootPathTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -23,7 +23,7 @@ public class NonAppEndpointsDisabledWithRootPathTest { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties") + .withConfigurationResource("resource-config/application-no-metrics.properties") .overrideConfigKey("quarkus.http.root-path", "/app") .overrideConfigKey("quarkus.http.non-application-root-path", "quarkus"); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledLegacyConfigurationTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEnabledLegacyConfigurationTest.java similarity index 93% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledLegacyConfigurationTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEnabledLegacyConfigurationTest.java index 9c094dc703990..8d8b7fef27720 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledLegacyConfigurationTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEnabledLegacyConfigurationTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -23,7 +23,7 @@ public class NonAppEndpointsEnabledLegacyConfigurationTest { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties") + .withConfigurationResource("resource-config/application-no-metrics.properties") .overrideConfigKey("quarkus.otel.traces.suppress-non-application-uris", "false"); @Inject diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEnabledTest.java similarity index 93% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEnabledTest.java index cc7fe7621c4ee..8e36d68c90cbb 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEnabledTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -23,7 +23,7 @@ public class NonAppEndpointsEnabledTest { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties") + .withConfigurationResource("resource-config/application-no-metrics.properties") .overrideConfigKey("quarkus.otel.traces.suppress-non-application-uris", "false"); @Inject diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEqualRootPath.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEqualRootPath.java similarity index 93% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEqualRootPath.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEqualRootPath.java index a0785225cd75f..0ce7116abdbee 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEqualRootPath.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/NonAppEndpointsEqualRootPath.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -23,7 +23,7 @@ public class NonAppEndpointsEqualRootPath { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties") + .withConfigurationResource("resource-config/application-no-metrics.properties") .overrideConfigKey("quarkus.http.root-path", "/app") .overrideConfigKey("quarkus.http.non-application-root-path", "/app"); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryCustomSamplerBeanTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryCustomSamplerBeanTest.java similarity index 96% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryCustomSamplerBeanTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryCustomSamplerBeanTest.java index 95cb9c7ee9835..9196e280c3dbc 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryCustomSamplerBeanTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryCustomSamplerBeanTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -43,7 +43,7 @@ public class OpenTelemetryCustomSamplerBeanTest { .addClass(TestUtil.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties"); + .withConfigurationResource("resource-config/application-no-metrics.properties"); @Inject OpenTelemetry openTelemetry; diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDILegacyTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDILegacyTest.java similarity index 94% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDILegacyTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDILegacyTest.java index dd03e834fa0a9..fdb7218df99e9 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDILegacyTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDILegacyTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.api.trace.SpanKind.SERVER; @@ -42,7 +42,7 @@ public class OpenTelemetryHttpCDILegacyTest { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties"); + .withConfigurationResource("resource-config/application-no-metrics.properties"); @Inject TestSpanExporter spanExporter; @@ -66,7 +66,7 @@ void telemetry() { assertEquals(SERVER, server.getKind()); // verify that OpenTelemetryServerFilter took place assertStringAttribute(server, SemanticAttributes.CODE_NAMESPACE, - "io.quarkus.opentelemetry.deployment.OpenTelemetryHttpCDILegacyTest$HelloResource"); + "io.quarkus.opentelemetry.deployment.traces.OpenTelemetryHttpCDILegacyTest$HelloResource"); assertStringAttribute(server, SemanticAttributes.CODE_FUNCTION, "hello"); SpanData internal = getSpanByKindAndParentId(spans, INTERNAL, server.getSpanId()); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDITest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDITest.java similarity index 94% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDITest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDITest.java index 19027b911c568..f8bd9be6817e5 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryHttpCDITest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryHttpCDITest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static io.opentelemetry.api.trace.SpanKind.INTERNAL; import static io.opentelemetry.api.trace.SpanKind.SERVER; @@ -42,7 +42,7 @@ public class OpenTelemetryHttpCDITest { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties"); + .withConfigurationResource("resource-config/application-no-metrics.properties"); @Inject TestSpanExporter spanExporter; @@ -65,7 +65,7 @@ void telemetry() { assertEquals("GET /hello", server.getName()); // verify that OpenTelemetryServerFilter took place assertStringAttribute(server, SemanticAttributes.CODE_NAMESPACE, - "io.quarkus.opentelemetry.deployment.OpenTelemetryHttpCDITest$HelloResource"); + "io.quarkus.opentelemetry.deployment.traces.OpenTelemetryHttpCDITest$HelloResource"); assertStringAttribute(server, SemanticAttributes.CODE_FUNCTION, "hello"); final SpanData internalFromBean = getSpanByKindAndParentId(spans, INTERNAL, server.getSpanId()); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryIdGeneratorTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryIdGeneratorTest.java similarity index 98% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryIdGeneratorTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryIdGeneratorTest.java index 1d4f148342567..fbf70b4ea0a65 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryIdGeneratorTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryIdGeneratorTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryJdbcInstrumentationValidationTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryJdbcInstrumentationValidationTest.java similarity index 93% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryJdbcInstrumentationValidationTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryJdbcInstrumentationValidationTest.java index 4f9f34bcade87..535bfcbd02cc0 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryJdbcInstrumentationValidationTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryJdbcInstrumentationValidationTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -17,6 +17,7 @@ public class OpenTelemetryJdbcInstrumentationValidationTest { .withApplicationRoot((jar) -> jar .addAsResource(new StringAsset( "quarkus.datasource.db-kind=h2\n" + + "quarkus.otel.metrics.exporter=none\n" + "quarkus.datasource.jdbc.driver=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver\n"), "application.properties")) .assertException(t -> { diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryReactiveRoutesTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryReactiveRoutesTest.java similarity index 95% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryReactiveRoutesTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryReactiveRoutesTest.java index 2af44b8e60a37..fd9b3088e43e7 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetryReactiveRoutesTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetryReactiveRoutesTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static io.opentelemetry.api.trace.SpanKind.SERVER; import static io.quarkus.opentelemetry.deployment.common.TestSpanExporter.getSpanByKindAndParentId; @@ -35,7 +35,7 @@ public class OpenTelemetryReactiveRoutesTest { .addClass(TestUtil.class) .addClass(TestSpanExporter.class) .addClass(TestSpanExporterProvider.class) - .addAsResource("resource-config/application.properties", "application.properties") + .addAsResource("resource-config/application-no-metrics.properties", "application.properties") .addAsResource( "META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider", "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerBeanTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySamplerBeanTest.java similarity index 92% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerBeanTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySamplerBeanTest.java index c58566046a8be..d155ed7d7fb21 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerBeanTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySamplerBeanTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.stringContainsInOrder; @@ -27,7 +27,7 @@ public class OpenTelemetrySamplerBeanTest { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties"); + .withConfigurationResource("resource-config/application-no-metrics.properties"); @Inject OpenTelemetry openTelemetry; diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerConfigTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySamplerConfigTest.java similarity index 92% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerConfigTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySamplerConfigTest.java index 728c9b66af6c5..0ff64298d64b4 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerConfigTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySamplerConfigTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -24,7 +24,7 @@ public class OpenTelemetrySamplerConfigTest { .addClasses(TestSpanExporter.class, TestSpanExporterProvider.class) .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider")) - .withConfigurationResource("application-default.properties") + .withConfigurationResource("resource-config/application-no-metrics.properties") .overrideConfigKey("quarkus.otel.traces.sampler", "traceidratio") .overrideConfigKey("quarkus.otel.traces.sampler.arg", "0.5") .overrideConfigKey("quarkus.otel.traces.suppress-non-application-uris", "false"); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySpanSecurityEventsTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySpanSecurityEventsTest.java similarity index 97% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySpanSecurityEventsTest.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySpanSecurityEventsTest.java index 616786d7ee668..41c2648e11c50 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySpanSecurityEventsTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/OpenTelemetrySpanSecurityEventsTest.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -39,6 +39,7 @@ public class OpenTelemetrySpanSecurityEventsTest { CustomSecurityEvent.class) .addAsResource(new StringAsset(""" quarkus.otel.security-events.enabled=true + quarkus.otel.metrics.exporter=none quarkus.otel.security-events.event-types=AUTHENTICATION_SUCCESS,AUTHORIZATION_SUCCESS,OTHER """), "application.properties")); diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/TracerRouterUT.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/TracerRouterUT.java similarity index 89% rename from extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/TracerRouterUT.java rename to extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/TracerRouterUT.java index 094e26d8e89cf..ef125fe6bc45c 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/TracerRouterUT.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/traces/TracerRouterUT.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.deployment; +package io.quarkus.opentelemetry.deployment.traces; import static org.hamcrest.Matchers.is; diff --git a/extensions/opentelemetry/deployment/src/test/resources/META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider b/extensions/opentelemetry/deployment/src/test/resources/META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider new file mode 100644 index 0000000000000..52aa734e638e5 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/resources/META-INF/services-config/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider @@ -0,0 +1 @@ +io.quarkus.opentelemetry.deployment.common.InMemoryMetricExporterProvider \ No newline at end of file diff --git a/extensions/opentelemetry/deployment/src/test/resources/application-default.properties b/extensions/opentelemetry/deployment/src/test/resources/application-default.properties index 3284a760fc5eb..5871e27ba54f5 100644 --- a/extensions/opentelemetry/deployment/src/test/resources/application-default.properties +++ b/extensions/opentelemetry/deployment/src/test/resources/application-default.properties @@ -1,3 +1,5 @@ quarkus.otel.traces.exporter=test-span-exporter -quarkus.otel.bsp.schedule.delay=50 -quarkus.otel.bsp.export.timeout=1s \ No newline at end of file +quarkus.otel.bsp.schedule.delay=50ms +quarkus.otel.bsp.export.timeout=1s +quarkus.otel.metrics.exporter=in-memory +quarkus.otel.metric.export.interval=300ms \ No newline at end of file diff --git a/extensions/opentelemetry/deployment/src/test/resources/resource-config/application-no-metrics.properties b/extensions/opentelemetry/deployment/src/test/resources/resource-config/application-no-metrics.properties new file mode 100644 index 0000000000000..cb6529bd136d5 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/resources/resource-config/application-no-metrics.properties @@ -0,0 +1,8 @@ +quarkus.application.name=resource-test +quarkus.otel.resource.attributes=service.name=authservice,service.instance.id=${quarkus.uuid} + +quarkus.otel.traces.exporter=test-span-exporter +quarkus.otel.bsp.schedule.delay=50ms +quarkus.otel.metrics.exporter=none + +quarkus.rest-client.client.url=${test.url} diff --git a/extensions/opentelemetry/deployment/src/test/resources/resource-config/application.properties b/extensions/opentelemetry/deployment/src/test/resources/resource-config/application.properties index a63d6ddd2f6b9..3d4b806814f52 100644 --- a/extensions/opentelemetry/deployment/src/test/resources/resource-config/application.properties +++ b/extensions/opentelemetry/deployment/src/test/resources/resource-config/application.properties @@ -2,6 +2,9 @@ quarkus.application.name=resource-test quarkus.otel.resource.attributes=service.name=authservice,service.instance.id=${quarkus.uuid} quarkus.otel.traces.exporter=test-span-exporter -quarkus.otel.bsp.schedule.delay=50 +quarkus.otel.bsp.schedule.delay=50ms +quarkus.otel.metrics.enabled=true +quarkus.otel.metrics.exporter=in-memory +quarkus.otel.metric.export.interval=300ms quarkus.rest-client.client.url=${test.url} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryDestroyer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryDestroyer.java index b76b36bccfa1d..8807c18b74b37 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryDestroyer.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/OpenTelemetryDestroyer.java @@ -23,7 +23,8 @@ public void destroy(OpenTelemetry openTelemetry, CreationalContext !sn.equals(appConfig.name.orElse("unset"))) + .filter(new Predicate() { + @Override + public boolean test(String sn) { + return !sn.equals(appConfig.name.orElse("unset")); + } + }) .orElse(null); // must be resolved at startup, once. @@ -170,19 +178,54 @@ public void customize(AutoConfiguredOpenTelemetrySdkBuilder builder) { builder.addTracerProviderCustomizer( new BiFunction<>() { @Override - public SdkTracerProviderBuilder apply(SdkTracerProviderBuilder builder, + public SdkTracerProviderBuilder apply(SdkTracerProviderBuilder tracerProviderBuilder, ConfigProperties configProperties) { if (oTelBuildConfig.traces().enabled().orElse(TRUE)) { - idGenerator.stream().findFirst().ifPresent(builder::setIdGenerator); // from cdi - spanProcessors.stream().filter(sp -> !(sp instanceof RemoveableLateBoundBatchSpanProcessor)) - .forEach(builder::addSpanProcessor); + idGenerator.stream().findFirst().ifPresent(tracerProviderBuilder::setIdGenerator); // from cdi + spanProcessors.stream().filter(new Predicate() { + @Override + public boolean test(SpanProcessor sp) { + return !(sp instanceof RemoveableLateBoundBatchSpanProcessor); + } + }) + .forEach(tracerProviderBuilder::addSpanProcessor); } - return builder; + return tracerProviderBuilder; } }); } } + @Singleton + final class MetricProviderCustomizer implements AutoConfiguredOpenTelemetrySdkBuilderCustomizer { + private final OTelBuildConfig oTelBuildConfig; + private final Instance clock; + + public MetricProviderCustomizer(OTelBuildConfig oTelBuildConfig, + final Instance clock) { + this.oTelBuildConfig = oTelBuildConfig; + this.clock = clock; + } + + @Override + public void customize(AutoConfiguredOpenTelemetrySdkBuilder builder) { + if (oTelBuildConfig.metrics().enabled().orElse(TRUE)) { + builder.addMeterProviderCustomizer( + new BiFunction() { + @Override + public SdkMeterProviderBuilder apply(SdkMeterProviderBuilder metricProvider, + ConfigProperties configProperties) { + if (clock.isUnsatisfied()) { + throw new IllegalStateException("No Clock bean found"); + } + metricProvider.setClock(clock.get()); + return metricProvider; + } + }); + } + } + } + @Singleton final class TextMapPropagatorCustomizers implements AutoConfiguredOpenTelemetrySdkBuilderCustomizer { diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/ExporterType.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/ExporterType.java index 69c4378e170a8..85ce3e31e17c4 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/ExporterType.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/ExporterType.java @@ -2,7 +2,7 @@ public enum ExporterType { OTLP(Constants.OTLP_VALUE), - // HTTP(Constants.HTTP_VALUE), // TODO not supported yet + HTTP(Constants.HTTP_VALUE), // JAEGER(Constants.JAEGER), // Moved to Quarkiverse /** * To be used by legacy CDI beans setup. Will be removed soon. @@ -23,7 +23,7 @@ public String getValue() { public static class Constants { public static final String OTLP_VALUE = "otlp"; public static final String CDI_VALUE = "cdi"; - // public static final String HTTP_VALUE = "http"; + public static final String HTTP_VALUE = "http"; public static final String NONE_VALUE = "none"; public static final String JAEGER = "jaeger"; } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/MetricsBuildConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/MetricsBuildConfig.java new file mode 100644 index 0000000000000..eb58074ef2e77 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/MetricsBuildConfig.java @@ -0,0 +1,28 @@ +package io.quarkus.opentelemetry.runtime.config.build; + +import static io.quarkus.opentelemetry.runtime.config.build.ExporterType.Constants.CDI_VALUE; + +import java.util.List; +import java.util.Optional; + +import io.smallrye.config.WithDefault; + +public interface MetricsBuildConfig { + + /** + * Enable metrics with OpenTelemetry. + *

+ * This property is not available in the Open Telemetry SDK. It's Quarkus specific. + *

+ * Support for metrics will be enabled if OpenTelemetry support is enabled + * and either this value is true, or this value is unset. + */ + @WithDefault("false") + Optional enabled(); + + /** + * The Metrics exporter to use. + */ + @WithDefault(CDI_VALUE) + List exporter(); +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/OTelBuildConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/OTelBuildConfig.java index 6a7a5aa09928b..1e1bef92e25a3 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/OTelBuildConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/build/OTelBuildConfig.java @@ -35,7 +35,6 @@ public interface OTelBuildConfig { *

* Defaults to true. */ - @Deprecated // TODO only use runtime (soon) @WithDefault("true") boolean enabled(); @@ -45,11 +44,9 @@ public interface OTelBuildConfig { TracesBuildConfig traces(); /** - * No Metrics exporter for now + * Metrics exporter configurations. */ - @WithName("metrics.exporter") - @WithDefault("none") - List metricsExporter(); + MetricsBuildConfig metrics(); /** * No Log exporter for now. diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/ExemplarsFilterType.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/ExemplarsFilterType.java new file mode 100644 index 0000000000000..9504070122212 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/ExemplarsFilterType.java @@ -0,0 +1,23 @@ +package io.quarkus.opentelemetry.runtime.config.runtime; + +public enum ExemplarsFilterType { + TRACE_BASED(Constants.TRACE_BASED), + ALWAYS_ON(Constants.ALWAYS_ON), + ALWAYS_OFF(Constants.ALWAYS_OFF); + + private final String value; + + ExemplarsFilterType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static class Constants { + public static final String TRACE_BASED = "trace_based"; + public static final String ALWAYS_ON = "always_on"; + public static final String ALWAYS_OFF = "always_off"; + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/MetricsRuntimeConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/MetricsRuntimeConfig.java new file mode 100644 index 0000000000000..da9abd483eb3e --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/MetricsRuntimeConfig.java @@ -0,0 +1,20 @@ +package io.quarkus.opentelemetry.runtime.config.runtime; + +import java.time.Duration; + +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; + +public interface MetricsRuntimeConfig { + + /** + * The interval, between the start of two metric export attempts. + *

+ * Default is 1min. + * + * @return the interval Duration. + */ + @WithName("export.interval") + @WithDefault("60s") + Duration exportInterval(); +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/OTelRuntimeConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/OTelRuntimeConfig.java index 3de7decd485ca..8960f04d76f53 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/OTelRuntimeConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/OTelRuntimeConfig.java @@ -28,6 +28,11 @@ public interface OTelRuntimeConfig { */ TracesRuntimeConfig traces(); + /** + * Metric runtime config. + */ + MetricsRuntimeConfig metric(); + /** * environment variables for the types of attributes, for which that SDK implements truncation mechanism. */ diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfig.java new file mode 100644 index 0000000000000..8c6ac92c6c330 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfig.java @@ -0,0 +1,134 @@ +package io.quarkus.opentelemetry.runtime.config.runtime.exporter; + +import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig.DEFAULT_GRPC_BASE_URI; + +import java.time.Duration; +import java.util.List; +import java.util.Optional; +import java.util.OptionalInt; + +import io.quarkus.runtime.annotations.ConfigDocDefault; +import io.quarkus.runtime.annotations.ConfigGroup; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; + +@ConfigGroup +public interface OtlpExporterConfig { + + /** + * OTLP Exporter specific. Will override otel.exporter.otlp.endpoint, if set. + *

+ * Fallbacks to the legacy property quarkus.opentelemetry.tracer.exporter.otlp.endpoint< or + * defaults to {@value OtlpExporterRuntimeConfig#DEFAULT_GRPC_BASE_URI}. + */ + @WithDefault(DEFAULT_GRPC_BASE_URI) + Optional endpoint(); + + /** + * Key-value pairs to be used as headers associated with gRPC requests. + * The format is similar to the {@code OTEL_EXPORTER_OTLP_HEADERS} environment variable, + * a list of key-value pairs separated by the "=" character. i.e.: key1=value1,key2=value2 + */ + Optional> headers(); + + /** + * Sets the method used to compress payloads. If unset, compression is disabled. Currently + * supported compression methods include `gzip` and `none`. + */ + Optional compression(); + + /** + * Sets the maximum time to wait for the collector to process an exported batch of spans. If + * unset, defaults to {@value OtlpExporterRuntimeConfig#DEFAULT_TIMEOUT_SECS}s. + */ + @WithDefault("10s") + Duration timeout(); + + /** + * OTLP defines the encoding of telemetry data and the protocol used to exchange data between the client and the + * server. Depending on the exporter, the available protocols will be different. + *

+ * Currently, only {@code grpc} and {@code http/protobuf} are allowed. + */ + @WithDefault(Protocol.GRPC) + Optional protocol(); + + /** + * Key/cert configuration in the PEM format. + */ + @WithName("key-cert") + KeyCert keyCert(); + + /** + * Trust configuration in the PEM format. + */ + @WithName("trust-cert") + TrustCert trustCert(); + + /** + * The name of the TLS configuration to use. + *

+ * If not set and the default TLS configuration is configured ({@code quarkus.tls.*}) then that will be used. + * If a name is configured, it uses the configuration from {@code quarkus.tls..*} + * If a name is configured, but no TLS configuration is found with that name then an error will be thrown. + */ + Optional tlsConfigurationName(); + + /** + * Set proxy options + */ + ProxyConfig proxyOptions(); + + interface ProxyConfig { + /** + * If proxy connection must be used. + */ + @WithDefault("false") + boolean enabled(); + + /** + * Set proxy username. + */ + Optional username(); + + /** + * Set proxy password. + */ + Optional password(); + + /** + * Set proxy port. + */ + @ConfigDocDefault("3128") + OptionalInt port(); + + /** + * Set proxy host. + */ + Optional host(); + } + + interface KeyCert { + /** + * Comma-separated list of the path to the key files (Pem format). + */ + Optional> keys(); + + /** + * Comma-separated list of the path to the certificate files (Pem format). + */ + Optional> certs(); + } + + interface TrustCert { + /** + * Comma-separated list of the trust certificate files (Pem format). + */ + Optional> certs(); + } + + class Protocol { + public static final String GRPC = "grpc"; + public static final String HTTP_PROTOBUF = "http/protobuf"; + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterMetricsConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterMetricsConfig.java new file mode 100644 index 0000000000000..a97889a9f055b --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterMetricsConfig.java @@ -0,0 +1,33 @@ +package io.quarkus.opentelemetry.runtime.config.runtime.exporter; + +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.smallrye.config.WithDefault; + +@ConfigGroup +public interface OtlpExporterMetricsConfig extends OtlpExporterConfig { + + /** + * The preferred output aggregation temporality. Options include DELTA, LOWMEMORY, and CUMULATIVE. + *

+ * If CUMULATIVE, all instruments will have cumulative temporality. + * If DELTA, counter (sync and async) and histograms will be delta, up down counters (sync and async) will be cumulative. + * If LOWMEMORY, sync counter and histograms will be delta, async counter and up down counters (sync and async) will be + * cumulative. + *

+ * Default is CUMULATIVE. + */ + @WithDefault("cumulative") + Optional temporalityPreference(); + + /** + * The preferred default histogram aggregation. + *

+ * Options include BASE2_EXPONENTIAL_BUCKET_HISTOGRAM and EXPLICIT_BUCKET_HISTOGRAM. + *

+ * Default is EXPLICIT_BUCKET_HISTOGRAM. + */ + @WithDefault("explicit_bucket_histogram") + Optional defaultHistogramAggregation(); +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterRuntimeConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterRuntimeConfig.java index a6e8725cf3cff..d2d308c841296 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterRuntimeConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterRuntimeConfig.java @@ -31,7 +31,11 @@ public interface OtlpExporterRuntimeConfig { * OTLP traces exporter configuration. */ OtlpExporterTracesConfig traces(); - // TODO metrics(); + + /** + * OTLP metrics exporter configuration. + */ + OtlpExporterMetricsConfig metrics(); // TODO logs(); // TODO additional global exporter configuration diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterTracesConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterTracesConfig.java index e815230ac4fd2..12eac3685da91 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterTracesConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterTracesConfig.java @@ -2,27 +2,14 @@ import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig.DEFAULT_GRPC_BASE_URI; -import java.time.Duration; -import java.util.List; import java.util.Optional; -import java.util.OptionalInt; -import io.quarkus.runtime.annotations.ConfigDocDefault; import io.quarkus.runtime.annotations.ConfigGroup; import io.smallrye.config.WithDefault; import io.smallrye.config.WithName; @ConfigGroup -public interface OtlpExporterTracesConfig { - - /** - * OTLP Exporter specific. Will override otel.exporter.otlp.endpoint, if set. - *

- * Fallbacks to the legacy property quarkus.opentelemetry.tracer.exporter.otlp.endpoint< or - * defaults to {@value OtlpExporterRuntimeConfig#DEFAULT_GRPC_BASE_URI}. - */ - @WithDefault(DEFAULT_GRPC_BASE_URI) - Optional endpoint(); +public interface OtlpExporterTracesConfig extends OtlpExporterConfig { /** * See {@link OtlpExporterTracesConfig#endpoint} @@ -32,113 +19,4 @@ public interface OtlpExporterTracesConfig { @WithName("legacy-endpoint") @WithDefault(DEFAULT_GRPC_BASE_URI) Optional legacyEndpoint(); - - /** - * Key-value pairs to be used as headers associated with gRPC requests. - * The format is similar to the {@code OTEL_EXPORTER_OTLP_HEADERS} environment variable, - * a list of key-value pairs separated by the "=" character. i.e.: key1=value1,key2=value2 - */ - Optional> headers(); - - /** - * Sets the method used to compress payloads. If unset, compression is disabled. Currently - * supported compression methods include `gzip` and `none`. - */ - Optional compression(); - - /** - * Sets the maximum time to wait for the collector to process an exported batch of spans. If - * unset, defaults to {@value OtlpExporterRuntimeConfig#DEFAULT_TIMEOUT_SECS}s. - */ - @WithDefault("10s") - Duration timeout(); - - /** - * OTLP defines the encoding of telemetry data and the protocol used to exchange data between the client and the - * server. Depending on the exporter, the available protocols will be different. - *

- * Currently, only {@code grpc} and {@code http/protobuf} are allowed. - */ - @WithDefault(Protocol.GRPC) - Optional protocol(); - - /** - * Key/cert configuration in the PEM format. - */ - @WithName("key-cert") - KeyCert keyCert(); - - /** - * Trust configuration in the PEM format. - */ - @WithName("trust-cert") - TrustCert trustCert(); - - /** - * The name of the TLS configuration to use. - *

- * If not set and the default TLS configuration is configured ({@code quarkus.tls.*}) then that will be used. - * If a name is configured, it uses the configuration from {@code quarkus.tls..*} - * If a name is configured, but no TLS configuration is found with that name then an error will be thrown. - */ - Optional tlsConfigurationName(); - - /** - * Set proxy options - */ - ProxyConfig proxyOptions(); - - interface KeyCert { - /** - * Comma-separated list of the path to the key files (Pem format). - */ - Optional> keys(); - - /** - * Comma-separated list of the path to the certificate files (Pem format). - */ - Optional> certs(); - } - - interface TrustCert { - /** - * Comma-separated list of the trust certificate files (Pem format). - */ - Optional> certs(); - } - - interface ProxyConfig { - - /** - * If proxy connection must be used. - */ - @WithDefault("false") - boolean enabled(); - - /** - * Set proxy username. - */ - Optional username(); - - /** - * Set proxy password. - */ - Optional password(); - - /** - * Set proxy port. - */ - @ConfigDocDefault("3128") - OptionalInt port(); - - /** - * Set proxy host. - */ - Optional host(); - } - - class Protocol { - public static final String GRPC = "grpc"; - public static final String HTTP_PROTOBUF = "http/protobuf"; - } } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterRecorder.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterRecorder.java index 4ba8a52a6e6b9..607c5f03ab95e 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterRecorder.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterRecorder.java @@ -1,12 +1,15 @@ package io.quarkus.opentelemetry.runtime.exporter.otlp; +import static io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram; +import static io.quarkus.opentelemetry.runtime.config.build.ExporterType.Constants.OTLP_VALUE; +import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfig.Protocol.GRPC; +import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfig.Protocol.HTTP_PROTOBUF; import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig.DEFAULT_GRPC_BASE_URI; -import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterTracesConfig.Protocol.GRPC; -import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterTracesConfig.Protocol.HTTP_PROTOBUF; import java.net.URI; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.function.Consumer; @@ -18,17 +21,37 @@ import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.ExporterBuilderUtil; +import io.opentelemetry.exporter.internal.grpc.GrpcExporter; import io.opentelemetry.exporter.internal.http.HttpExporter; +import io.opentelemetry.exporter.internal.otlp.metrics.MetricsRequestMarshaler; import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler; import io.opentelemetry.exporter.otlp.internal.OtlpUserAgent; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; +import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil; import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.opentelemetry.sdk.trace.export.BatchSpanProcessorBuilder; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig; import io.quarkus.opentelemetry.runtime.config.runtime.exporter.CompressionType; +import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfig; +import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterMetricsConfig; import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig; import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterTracesConfig; +import io.quarkus.opentelemetry.runtime.exporter.otlp.metrics.NoopMetricExporter; +import io.quarkus.opentelemetry.runtime.exporter.otlp.metrics.VertxGrpcMetricExporter; +import io.quarkus.opentelemetry.runtime.exporter.otlp.metrics.VertxHttpMetricsExporter; +import io.quarkus.opentelemetry.runtime.exporter.otlp.sender.VertxGrpcSender; +import io.quarkus.opentelemetry.runtime.exporter.otlp.sender.VertxHttpSender; +import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundBatchSpanProcessor; +import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.RemoveableLateBoundBatchSpanProcessor; +import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.VertxGrpcSpanExporter; +import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.VertxHttpSpanExporter; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.tls.TlsConfiguration; import io.quarkus.tls.TlsConfigurationRegistry; @@ -42,9 +65,14 @@ @Recorder public class OTelExporterRecorder { + public static final String BASE2EXPONENTIAL_AGGREGATION_NAME = AggregationUtil + .aggregationName(Aggregation.base2ExponentialBucketHistogram()); + public static final String EXPLICIT_BUCKET_AGGREGATION_NAME = AggregationUtil.aggregationName(explicitBucketHistogram()); + public Function, LateBoundBatchSpanProcessor> batchSpanProcessorForOtlp( OTelRuntimeConfig otelRuntimeConfig, - OtlpExporterRuntimeConfig exporterRuntimeConfig, Supplier vertx) { + OtlpExporterRuntimeConfig exporterRuntimeConfig, + Supplier vertx) { URI baseUri = getBaseUri(exporterRuntimeConfig); // do the creation and validation here in order to preserve backward compatibility return new Function<>() { @Override @@ -107,17 +135,18 @@ private SpanExporter createOtlpGrpcSpanExporter(OtlpExporterRuntimeConfig export OtlpExporterTracesConfig tracesConfig = exporterRuntimeConfig.traces(); - return new VertxGrpcExporter( - "otlp", // use the same as OTel does + return new VertxGrpcSpanExporter(new GrpcExporter( + OTLP_VALUE, // use the same as OTel does "span", // use the same as OTel does - MeterProvider::noop, - baseUri, - determineCompression(tracesConfig), - tracesConfig.timeout(), - populateTracingExportHttpHeaders(tracesConfig), - new HttpClientOptionsConsumer(tracesConfig, baseUri, tlsConfigurationRegistry), - vertx); - + new VertxGrpcSender( + baseUri, + VertxGrpcSender.GRPC_TRACE_SERVICE_NAME, + determineCompression(tracesConfig), + tracesConfig.timeout(), + populateTracingExportHttpHeaders(tracesConfig), + new HttpClientOptionsConsumer(tracesConfig, baseUri, tlsConfigurationRegistry), + vertx), + MeterProvider::noop)); } private SpanExporter createHttpSpanExporter(OtlpExporterRuntimeConfig exporterRuntimeConfig, Vertx vertx, @@ -128,11 +157,12 @@ private SpanExporter createHttpSpanExporter(OtlpExporterRuntimeConfig exporterRu boolean exportAsJson = false; //TODO: this will be enhanced in the future - return new VertxHttpExporter(new HttpExporter( - "otlp", // use the same as OTel does + return new VertxHttpSpanExporter(new HttpExporter( + OTLP_VALUE, // use the same as OTel does "span", // use the same as OTel does - new VertxHttpExporter.VertxHttpSender( + new VertxHttpSender( baseUri, + VertxHttpSender.TRACES_PATH, determineCompression(tracesConfig), tracesConfig.timeout(), populateTracingExportHttpHeaders(tracesConfig), @@ -145,18 +175,127 @@ private SpanExporter createHttpSpanExporter(OtlpExporterRuntimeConfig exporterRu }; } - private static boolean determineCompression(OtlpExporterTracesConfig tracesConfig) { - if (tracesConfig.compression().isPresent()) { - return (tracesConfig.compression().get() == CompressionType.GZIP); + public Function, MetricExporter> createMetricExporter( + OTelRuntimeConfig otelRuntimeConfig, + OtlpExporterRuntimeConfig exporterRuntimeConfig, + Supplier vertx) { + + final URI baseUri = getBaseUri(exporterRuntimeConfig); + + return new Function<>() { + @Override + public MetricExporter apply(SyntheticCreationalContext context) { + + if (otelRuntimeConfig.sdkDisabled() || baseUri == null) { + return NoopMetricExporter.INSTANCE; + } + + MetricExporter metricExporter; + + try { + TlsConfigurationRegistry tlsConfigurationRegistry = context + .getInjectedReference(TlsConfigurationRegistry.class); + OtlpExporterMetricsConfig metricsConfig = exporterRuntimeConfig.metrics(); + if (metricsConfig.protocol().isEmpty()) { + throw new IllegalStateException("No OTLP protocol specified. " + + "Please check `quarkus.otel.exporter.otlp.traces.protocol` property"); + } + + String protocol = metricsConfig.protocol().get(); + if (GRPC.equals(protocol)) { + metricExporter = new VertxGrpcMetricExporter( + new GrpcExporter( + OTLP_VALUE, // use the same as OTel does + "metric", // use the same as OTel does + new VertxGrpcSender( + baseUri, + VertxGrpcSender.GRPC_METRIC_SERVICE_NAME, + determineCompression(metricsConfig), + metricsConfig.timeout(), + populateTracingExportHttpHeaders(metricsConfig), + new HttpClientOptionsConsumer(metricsConfig, baseUri, tlsConfigurationRegistry), + vertx.get()), + MeterProvider::noop), + aggregationTemporalityResolver(metricsConfig), + aggregationResolver(metricsConfig)); + } else if (HTTP_PROTOBUF.equals(protocol)) { + boolean exportAsJson = false; //TODO: this will be enhanced in the future + metricExporter = new VertxHttpMetricsExporter( + new HttpExporter( + OTLP_VALUE, // use the same as OTel does + "metric", // use the same as OTel does + new VertxHttpSender( + baseUri, + VertxHttpSender.METRICS_PATH, + determineCompression(metricsConfig), + metricsConfig.timeout(), + populateTracingExportHttpHeaders(metricsConfig), + exportAsJson ? "application/json" : "application/x-protobuf", + new HttpClientOptionsConsumer(metricsConfig, baseUri, tlsConfigurationRegistry), + vertx.get()), + MeterProvider::noop, + exportAsJson), + aggregationTemporalityResolver(metricsConfig), + aggregationResolver(metricsConfig)); + } else { + throw new IllegalArgumentException(String.format("Unsupported OTLP protocol %s specified. " + + "Please check `quarkus.otel.exporter.otlp.traces.protocol` property", protocol)); + } + + } catch (IllegalArgumentException iae) { + throw new IllegalStateException("Unable to install OTLP Exporter", iae); + } + return metricExporter; + } + }; + } + + private static DefaultAggregationSelector aggregationResolver(OtlpExporterMetricsConfig metricsConfig) { + String defaultHistogramAggregation = metricsConfig.defaultHistogramAggregation() + .map(s -> s.toLowerCase(Locale.ROOT)) + .orElse("explicit_bucket_histogram"); + + DefaultAggregationSelector aggregationSelector; + if (defaultHistogramAggregation.equals("explicit_bucket_histogram")) { + aggregationSelector = DefaultAggregationSelector.getDefault(); + } else if (BASE2EXPONENTIAL_AGGREGATION_NAME.equalsIgnoreCase(defaultHistogramAggregation)) { + + aggregationSelector = DefaultAggregationSelector + .getDefault() + .with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram()); + + } else { + throw new ConfigurationException( + "Unrecognized default histogram aggregation: " + defaultHistogramAggregation); + } + return aggregationSelector; + } + + private static AggregationTemporalitySelector aggregationTemporalityResolver(OtlpExporterMetricsConfig metricsConfig) { + String temporalityValue = metricsConfig.temporalityPreference() + .map(s -> s.toLowerCase(Locale.ROOT)) + .orElse("cumulative"); + AggregationTemporalitySelector temporalitySelector = switch (temporalityValue) { + case "cumulative" -> AggregationTemporalitySelector.alwaysCumulative(); + case "delta" -> AggregationTemporalitySelector.deltaPreferred(); + case "lowmemory" -> AggregationTemporalitySelector.lowMemory(); + default -> throw new ConfigurationException("Unrecognized aggregation temporality: " + temporalityValue); + }; + return temporalitySelector; + } + + private static boolean determineCompression(OtlpExporterConfig config) { + if (config.compression().isPresent()) { + return (config.compression().get() == CompressionType.GZIP); } return false; } - private static Map populateTracingExportHttpHeaders(OtlpExporterTracesConfig tracesConfig) { + private static Map populateTracingExportHttpHeaders(OtlpExporterConfig config) { Map headersMap = new HashMap<>(); OtlpUserAgent.addUserAgentHeader(headersMap::put); - if (tracesConfig.headers().isPresent()) { - List headers = tracesConfig.headers().get(); + if (config.headers().isPresent()) { + List headers = config.headers().get(); if (!headers.isEmpty()) { for (String header : headers) { if (header.isEmpty()) { @@ -173,7 +312,7 @@ private static Map populateTracingExportHttpHeaders(OtlpExporter } private URI getBaseUri(OtlpExporterRuntimeConfig exporterRuntimeConfig) { - String endpoint = resolveEndpoint(exporterRuntimeConfig).trim(); + String endpoint = resolveEndpoint(exporterRuntimeConfig).trim(); // FIXME must be signal independent if (endpoint.isEmpty()) { return null; } @@ -196,23 +335,23 @@ private static boolean excludeDefaultEndpoint(String endpoint) { } static class HttpClientOptionsConsumer implements Consumer { - private final OtlpExporterTracesConfig tracesConfig; + private final OtlpExporterConfig config; private final URI baseUri; private final Optional maybeTlsConfiguration; private final TlsConfigurationRegistry tlsConfigurationRegistry; - public HttpClientOptionsConsumer(OtlpExporterTracesConfig tracesConfig, URI baseUri, + public HttpClientOptionsConsumer(OtlpExporterConfig config, URI baseUri, TlsConfigurationRegistry tlsConfigurationRegistry) { - this.tracesConfig = tracesConfig; + this.config = config; this.baseUri = baseUri; - this.maybeTlsConfiguration = TlsConfiguration.from(tlsConfigurationRegistry, tracesConfig.tlsConfigurationName()); + this.maybeTlsConfiguration = TlsConfiguration.from(tlsConfigurationRegistry, config.tlsConfigurationName()); this.tlsConfigurationRegistry = tlsConfigurationRegistry; } @Override public void accept(HttpClientOptions options) { configureTLS(options); - if (tracesConfig.proxyOptions().enabled()) { + if (config.proxyOptions().enabled()) { configureProxyOptions(options); } } @@ -240,7 +379,7 @@ public Boolean get() { } private void configureProxyOptions(HttpClientOptions options) { - var proxyConfig = tracesConfig.proxyOptions(); + var proxyConfig = config.proxyOptions(); Optional proxyHost = proxyConfig.host(); if (proxyHost.isPresent()) { ProxyOptions proxyOptions = new ProxyOptions() @@ -293,7 +432,7 @@ private void configureKeyCertOptions(HttpClientOptions options) { return; } - OtlpExporterTracesConfig.KeyCert keyCert = tracesConfig.keyCert(); + OtlpExporterTracesConfig.KeyCert keyCert = config.keyCert(); if (keyCert.certs().isEmpty() && keyCert.keys().isEmpty()) { return; } @@ -318,7 +457,7 @@ private void configureTrustOptions(HttpClientOptions options) { return; } - OtlpExporterTracesConfig.TrustCert trustCert = tracesConfig.trustCert(); + OtlpExporterTracesConfig.TrustCert trustCert = config.trustCert(); if (trustCert.certs().isPresent()) { List certs = trustCert.certs().get(); if (!certs.isEmpty()) { diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterUtil.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterUtil.java index 02aefe66744f9..7853c77875e9d 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterUtil.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterUtil.java @@ -3,12 +3,12 @@ import java.net.URI; import java.util.Locale; -final class OTelExporterUtil { +public final class OTelExporterUtil { private OTelExporterUtil() { } - static int getPort(URI uri) { + public static int getPort(URI uri) { int originalPort = uri.getPort(); if (originalPort > -1) { return originalPort; @@ -20,7 +20,7 @@ static int getPort(URI uri) { return 80; } - static boolean isHttps(URI uri) { + public static boolean isHttps(URI uri) { return "https".equals(uri.getScheme().toLowerCase(Locale.ROOT)); } } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/VertxHttpExporter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/VertxHttpExporter.java deleted file mode 100644 index 23c0175621898..0000000000000 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/VertxHttpExporter.java +++ /dev/null @@ -1,305 +0,0 @@ -package io.quarkus.opentelemetry.runtime.exporter.otlp; - -import static io.quarkus.opentelemetry.runtime.exporter.otlp.OTelExporterUtil.getPort; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.URI; -import java.time.Duration; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.GZIPOutputStream; - -import io.opentelemetry.exporter.internal.http.HttpExporter; -import io.opentelemetry.exporter.internal.http.HttpSender; -import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.internal.ThrottlingLogger; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.quarkus.vertx.core.runtime.BufferOutputStream; -import io.smallrye.mutiny.Uni; -import io.vertx.core.AsyncResult; -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpClientResponse; -import io.vertx.core.http.HttpMethod; -import io.vertx.core.tracing.TracingPolicy; - -final class VertxHttpExporter implements SpanExporter { - - private static final Logger internalLogger = Logger.getLogger(VertxHttpExporter.class.getName()); - private static final ThrottlingLogger logger = new ThrottlingLogger(internalLogger); - - private static final int MAX_ATTEMPTS = 3; - - private final HttpExporter delegate; - private final AtomicBoolean isShutdown = new AtomicBoolean(); - - VertxHttpExporter(HttpExporter delegate) { - this.delegate = delegate; - } - - @Override - public CompletableResultCode export(Collection spans) { - if (isShutdown.get()) { - return CompletableResultCode.ofFailure(); - } - - TraceRequestMarshaler exportRequest = TraceRequestMarshaler.create(spans); - return delegate.export(exportRequest, spans.size()); - } - - @Override - public CompletableResultCode flush() { - return CompletableResultCode.ofSuccess(); - } - - @Override - public CompletableResultCode shutdown() { - if (!isShutdown.compareAndSet(false, true)) { - logger.log(Level.FINE, "Calling shutdown() multiple times."); - return CompletableResultCode.ofSuccess(); - } - return delegate.shutdown(); - } - - static final class VertxHttpSender implements HttpSender { - - private static final String TRACES_PATH = "/v1/traces"; - private final String basePath; - private final boolean compressionEnabled; - private final Map headers; - private final String contentType; - private final HttpClient client; - - VertxHttpSender( - URI baseUri, - boolean compressionEnabled, - Duration timeout, - Map headersMap, - String contentType, - Consumer clientOptionsCustomizer, - Vertx vertx) { - this.basePath = determineBasePath(baseUri); - this.compressionEnabled = compressionEnabled; - this.headers = headersMap; - this.contentType = contentType; - var httpClientOptions = new HttpClientOptions() - .setReadIdleTimeout((int) timeout.getSeconds()) - .setDefaultHost(baseUri.getHost()) - .setDefaultPort(getPort(baseUri)) - .setTracingPolicy(TracingPolicy.IGNORE); // needed to avoid tracing the calls from this http client - clientOptionsCustomizer.accept(httpClientOptions); - this.client = vertx.createHttpClient(httpClientOptions); - } - - private final CompletableResultCode shutdownResult = new CompletableResultCode(); - - private static String determineBasePath(URI baseUri) { - String path = baseUri.getPath(); - if (path.isEmpty() || path.equals("/")) { - return ""; - } - if (path.endsWith("/")) { // strip ending slash - path = path.substring(0, path.length() - 1); - } - if (!path.startsWith("/")) { // prepend leading slash - path = "/" + path; - } - return path; - } - - @Override - public void send(Consumer marshaler, - int contentLength, - Consumer onHttpResponseRead, - Consumer onError) { - - String requestURI = basePath + TRACES_PATH; - var clientRequestSuccessHandler = new ClientRequestSuccessHandler(client, requestURI, headers, compressionEnabled, - contentType, - contentLength, onHttpResponseRead, - onError, marshaler, 1); - initiateSend(client, requestURI, MAX_ATTEMPTS, clientRequestSuccessHandler, onError); - } - - private static void initiateSend(HttpClient client, String requestURI, - int numberOfAttempts, - Handler clientRequestSuccessHandler, - Consumer onError) { - Uni.createFrom().completionStage(new Supplier>() { - @Override - public CompletionStage get() { - return client.request(HttpMethod.POST, requestURI).toCompletionStage(); - } - }).onFailure().retry() - .withBackOff(Duration.ofMillis(100)) - .atMost(numberOfAttempts) - .subscribe().with(new Consumer<>() { - @Override - public void accept(HttpClientRequest request) { - clientRequestSuccessHandler.handle(request); - } - }, onError); - } - - @Override - public CompletableResultCode shutdown() { - client.close() - .onSuccess( - new Handler<>() { - @Override - public void handle(Void event) { - shutdownResult.succeed(); - } - }) - .onFailure(new Handler<>() { - @Override - public void handle(Throwable event) { - shutdownResult.fail(); - } - }); - return shutdownResult; - } - - private static class ClientRequestSuccessHandler implements Handler { - private final HttpClient client; - private final String requestURI; - private final Map headers; - private final boolean compressionEnabled; - private final String contentType; - private final int contentLength; - private final Consumer onHttpResponseRead; - private final Consumer onError; - private final Consumer marshaler; - - private final int attemptNumber; - - public ClientRequestSuccessHandler(HttpClient client, - String requestURI, Map headers, - boolean compressionEnabled, - String contentType, - int contentLength, - Consumer onHttpResponseRead, - Consumer onError, - Consumer marshaler, - int attemptNumber) { - this.client = client; - this.requestURI = requestURI; - this.headers = headers; - this.compressionEnabled = compressionEnabled; - this.contentType = contentType; - this.contentLength = contentLength; - this.onHttpResponseRead = onHttpResponseRead; - this.onError = onError; - this.marshaler = marshaler; - this.attemptNumber = attemptNumber; - } - - @Override - public void handle(HttpClientRequest request) { - - HttpClientRequest clientRequest = request.response(new Handler<>() { - @Override - public void handle(AsyncResult callResult) { - if (callResult.succeeded()) { - HttpClientResponse clientResponse = callResult.result(); - clientResponse.body(new Handler<>() { - @Override - public void handle(AsyncResult bodyResult) { - if (bodyResult.succeeded()) { - if (clientResponse.statusCode() >= 500) { - if (attemptNumber <= MAX_ATTEMPTS) { - // we should retry for 5xx error as they might be recoverable - initiateSend(client, requestURI, - MAX_ATTEMPTS - attemptNumber, - newAttempt(), - onError); - return; - } - } - onHttpResponseRead.accept(new Response() { - @Override - public int statusCode() { - return clientResponse.statusCode(); - } - - @Override - public String statusMessage() { - return clientResponse.statusMessage(); - } - - @Override - public byte[] responseBody() { - return bodyResult.result().getBytes(); - } - }); - } else { - if (attemptNumber <= MAX_ATTEMPTS) { - // retry - initiateSend(client, requestURI, - MAX_ATTEMPTS - attemptNumber, - newAttempt(), - onError); - } else { - onError.accept(bodyResult.cause()); - } - } - } - }); - } else { - if (attemptNumber <= MAX_ATTEMPTS) { - // retry - initiateSend(client, requestURI, - MAX_ATTEMPTS - attemptNumber, - newAttempt(), - onError); - } else { - onError.accept(callResult.cause()); - } - } - } - }) - .putHeader("Content-Type", contentType); - - Buffer buffer = Buffer.buffer(contentLength); - OutputStream os = new BufferOutputStream(buffer); - if (compressionEnabled) { - clientRequest.putHeader("Content-Encoding", "gzip"); - try (var gzos = new GZIPOutputStream(os)) { - marshaler.accept(gzos); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } else { - marshaler.accept(os); - } - - if (!headers.isEmpty()) { - for (var entry : headers.entrySet()) { - clientRequest.putHeader(entry.getKey(), entry.getValue()); - } - } - - clientRequest.send(buffer); - } - - public ClientRequestSuccessHandler newAttempt() { - return new ClientRequestSuccessHandler(client, requestURI, headers, compressionEnabled, - contentType, contentLength, onHttpResponseRead, - onError, marshaler, attemptNumber + 1); - } - } - } -} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/graal/Target_io_opentelemetry_exporter_otlp_internal_OtlpMetricExporterProvider.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/graal/Target_io_opentelemetry_exporter_otlp_internal_OtlpMetricExporterProvider.java new file mode 100644 index 0000000000000..d63ca09dad004 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/graal/Target_io_opentelemetry_exporter_otlp_internal_OtlpMetricExporterProvider.java @@ -0,0 +1,9 @@ +package io.quarkus.opentelemetry.runtime.exporter.otlp.graal; + +import com.oracle.svm.core.annotate.Delete; +import com.oracle.svm.core.annotate.TargetClass; + +@TargetClass(className = "io.opentelemetry.exporter.otlp.internal.OtlpMetricExporterProvider") +@Delete +public final class Target_io_opentelemetry_exporter_otlp_internal_OtlpMetricExporterProvider { +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/NoopMetricExporter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/NoopMetricExporter.java new file mode 100644 index 0000000000000..a02cfd815c7fb --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/NoopMetricExporter.java @@ -0,0 +1,37 @@ +package io.quarkus.opentelemetry.runtime.exporter.otlp.metrics; + +import java.util.Collection; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; + +public class NoopMetricExporter implements MetricExporter { + + public static final NoopMetricExporter INSTANCE = new NoopMetricExporter(); + + private NoopMetricExporter() { + } + + @Override + public CompletableResultCode export(Collection metrics) { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return AggregationTemporality.CUMULATIVE; + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/VertxGrpcMetricExporter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/VertxGrpcMetricExporter.java new file mode 100644 index 0000000000000..61eeb1d1390ee --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/VertxGrpcMetricExporter.java @@ -0,0 +1,60 @@ +package io.quarkus.opentelemetry.runtime.exporter.otlp.metrics; + +import java.util.Collection; + +import io.opentelemetry.exporter.internal.grpc.GrpcExporter; +import io.opentelemetry.exporter.internal.otlp.metrics.MetricsRequestMarshaler; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; +import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; +import io.opentelemetry.sdk.metrics.export.MetricExporter; + +public class VertxGrpcMetricExporter implements MetricExporter { + + private final GrpcExporter delegate; + private final AggregationTemporalitySelector aggregationTemporalitySelector; + private final DefaultAggregationSelector defaultAggregationSelector; + + public VertxGrpcMetricExporter(GrpcExporter grpcExporter, + AggregationTemporalitySelector aggregationTemporalitySelector, + DefaultAggregationSelector defaultAggregationSelector) { + this.delegate = grpcExporter; + this.aggregationTemporalitySelector = aggregationTemporalitySelector; + this.defaultAggregationSelector = defaultAggregationSelector; + } + + @Override + public CompletableResultCode export(Collection metrics) { + return delegate.export(MetricsRequestMarshaler.create(metrics), metrics.size()); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return this.aggregationTemporalitySelector.getAggregationTemporality(instrumentType); + } + + @Override + public Aggregation getDefaultAggregation(InstrumentType instrumentType) { + return defaultAggregationSelector.getDefaultAggregation(instrumentType); + } + + @Override + public MemoryMode getMemoryMode() { + return MemoryMode.IMMUTABLE_DATA; // Same as the default in the OTLP exporter + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/VertxHttpMetricsExporter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/VertxHttpMetricsExporter.java new file mode 100644 index 0000000000000..bf4afaede728b --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/metrics/VertxHttpMetricsExporter.java @@ -0,0 +1,60 @@ +package io.quarkus.opentelemetry.runtime.exporter.otlp.metrics; + +import java.util.Collection; + +import io.opentelemetry.exporter.internal.http.HttpExporter; +import io.opentelemetry.exporter.internal.otlp.metrics.MetricsRequestMarshaler; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.export.MemoryMode; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector; +import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector; +import io.opentelemetry.sdk.metrics.export.MetricExporter; + +public class VertxHttpMetricsExporter implements MetricExporter { + + private final HttpExporter delegate; + private final AggregationTemporalitySelector aggregationTemporalitySelector; + private final DefaultAggregationSelector defaultAggregationSelector; + + public VertxHttpMetricsExporter(HttpExporter delegate, + AggregationTemporalitySelector aggregationTemporalitySelector, + DefaultAggregationSelector defaultAggregationSelector) { + this.delegate = delegate; + this.aggregationTemporalitySelector = aggregationTemporalitySelector; + this.defaultAggregationSelector = defaultAggregationSelector; + } + + @Override + public CompletableResultCode export(Collection metrics) { + return delegate.export(MetricsRequestMarshaler.create(metrics), metrics.size()); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return this.aggregationTemporalitySelector.getAggregationTemporality(instrumentType); + } + + @Override + public Aggregation getDefaultAggregation(InstrumentType instrumentType) { + return defaultAggregationSelector.getDefaultAggregation(instrumentType); + } + + @Override + public MemoryMode getMemoryMode() { + return MemoryMode.IMMUTABLE_DATA; // Same as the default in the OTLP exporter + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/VertxGrpcExporter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/sender/VertxGrpcSender.java similarity index 74% rename from extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/VertxGrpcExporter.java rename to extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/sender/VertxGrpcSender.java index cb6e208632afd..d7c856243609a 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/VertxGrpcExporter.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/sender/VertxGrpcSender.java @@ -1,26 +1,27 @@ -package io.quarkus.opentelemetry.runtime.exporter.otlp; +package io.quarkus.opentelemetry.runtime.exporter.otlp.sender; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.util.Collection; import java.util.Map; import java.util.concurrent.CompletionStage; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; import io.netty.handler.codec.http.QueryStringDecoder; -import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.exporter.internal.ExporterMetrics; -import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler; +import io.opentelemetry.exporter.internal.grpc.GrpcResponse; +import io.opentelemetry.exporter.internal.grpc.GrpcSender; +import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.internal.ThrottlingLogger; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.quarkus.opentelemetry.runtime.exporter.otlp.OTelExporterUtil; import io.quarkus.vertx.core.runtime.BufferOutputStream; import io.smallrye.mutiny.Uni; import io.vertx.core.Handler; @@ -36,15 +37,16 @@ import io.vertx.grpc.common.GrpcStatus; import io.vertx.grpc.common.ServiceName; -final class VertxGrpcExporter implements SpanExporter { +public final class VertxGrpcSender implements GrpcSender { - private static final String GRPC_SERVICE_NAME = "opentelemetry.proto.collector.trace.v1.TraceService"; + public static final String GRPC_TRACE_SERVICE_NAME = "opentelemetry.proto.collector.trace.v1.TraceService"; + public static final String GRPC_METRIC_SERVICE_NAME = "opentelemetry.proto.collector.metrics.v1.MetricsService"; private static final String GRPC_METHOD_NAME = "Export"; private static final String GRPC_STATUS = "grpc-status"; private static final String GRPC_MESSAGE = "grpc-message"; - private static final Logger internalLogger = Logger.getLogger(VertxGrpcExporter.class.getName()); + private static final Logger internalLogger = Logger.getLogger(VertxGrpcSender.class.getName()); private static final int MAX_ATTEMPTS = 3; private final ThrottlingLogger logger = new ThrottlingLogger(internalLogger); // TODO: is there something in JBoss Logging we can use? @@ -53,25 +55,22 @@ final class VertxGrpcExporter implements SpanExporter { private final AtomicBoolean loggedUnimplemented = new AtomicBoolean(); private final AtomicBoolean isShutdown = new AtomicBoolean(); private final CompletableResultCode shutdownResult = new CompletableResultCode(); - private final String type; - private final ExporterMetrics exporterMetrics; private final SocketAddress server; private final boolean compressionEnabled; private final Map headers; + private final String grpcEndpointPath; private final GrpcClient client; - VertxGrpcExporter( - String exporterName, - String type, - Supplier meterProviderSupplier, - URI grpcBaseUri, boolean compressionEnabled, + public VertxGrpcSender( + URI grpcBaseUri, + String grpcEndpointPath, + boolean compressionEnabled, Duration timeout, Map headersMap, Consumer clientOptionsCustomizer, Vertx vertx) { - this.type = type; - this.exporterMetrics = ExporterMetrics.createGrpcOkHttp(exporterName, type, meterProviderSupplier); + this.grpcEndpointPath = grpcEndpointPath; this.server = SocketAddress.inetSocketAddress(OTelExporterUtil.getPort(grpcBaseUri), grpcBaseUri.getHost()); this.compressionEnabled = compressionEnabled; this.headers = headersMap; @@ -83,74 +82,24 @@ final class VertxGrpcExporter implements SpanExporter { this.client = GrpcClient.client(vertx, httpClientOptions); } - private CompletableResultCode export(TraceRequestMarshaler marshaler, int numItems) { + @Override + public void send(Marshaler request, Runnable onSuccess, BiConsumer onError) { if (isShutdown.get()) { - return CompletableResultCode.ofFailure(); + return; } - exporterMetrics.addSeen(numItems); - - var result = new CompletableResultCode(); - var onSuccessHandler = new ClientRequestOnSuccessHandler(client, server, headers, compressionEnabled, exporterMetrics, - marshaler, - loggedUnimplemented, logger, type, numItems, result, 1); + final String marshalerType = request.getClass().getSimpleName(); + var onSuccessHandler = new ClientRequestOnSuccessHandler(client, server, headers, compressionEnabled, + request, + loggedUnimplemented, logger, marshalerType, onSuccess, onError, 1, grpcEndpointPath, + isShutdown::get); initiateSend(client, server, MAX_ATTEMPTS, onSuccessHandler, new Consumer<>() { @Override public void accept(Throwable throwable) { - failOnClientRequest(numItems, throwable, result); + failOnClientRequest(marshalerType, throwable, onError); } }); - - return result; - } - - private static void initiateSend(GrpcClient client, SocketAddress server, - int numberOfAttempts, - Handler> onSuccessHandler, - Consumer onFailureCallback) { - Uni.createFrom().completionStage(new Supplier>>() { - - @Override - public CompletionStage> get() { - return client.request(server).toCompletionStage(); - } - }).onFailure().retry() - .withBackOff(Duration.ofMillis(100)) - .atMost(numberOfAttempts).subscribe().with( - new Consumer<>() { - @Override - public void accept(GrpcClientRequest request) { - onSuccessHandler.handle(request); - } - }, onFailureCallback); - } - - private void failOnClientRequest(int numItems, Throwable t, CompletableResultCode result) { - exporterMetrics.addFailed(numItems); - logger.log( - Level.SEVERE, - "Failed to export " - + type - + "s. The request could not be executed. Full error message: " - + t.getMessage()); - result.fail(); - } - - @Override - public CompletableResultCode export(Collection spans) { - if (isShutdown.get()) { - return CompletableResultCode.ofFailure(); - } - - TraceRequestMarshaler request = TraceRequestMarshaler.create(spans); - - return export(request, spans.size()); - } - - @Override - public CompletableResultCode flush() { - return CompletableResultCode.ofSuccess(); } @Override @@ -177,47 +126,96 @@ public void handle(Throwable event) { return shutdownResult; } + private static void initiateSend(GrpcClient client, SocketAddress server, + int numberOfAttempts, + Handler> onSuccessHandler, + Consumer onFailureCallback) { + Uni.createFrom().completionStage(new Supplier>>() { + @Override + public CompletionStage> get() { + return client.request(server).toCompletionStage(); + } + }) + .onFailure(new Predicate() { + @Override + public boolean test(Throwable t) { + // Will not retry on shutdown + return t instanceof IllegalStateException || + t instanceof RejectedExecutionException; + } + }) + .recoverWithUni(new Supplier>>() { + @Override + public Uni> get() { + return Uni.createFrom().nothing(); + } + }) + .onFailure() + .retry() + .withBackOff(Duration.ofMillis(100)) + .atMost(numberOfAttempts) + .subscribe().with( + new Consumer<>() { + @Override + public void accept(GrpcClientRequest request) { + onSuccessHandler.handle(request); + } + }, onFailureCallback); + } + + private void failOnClientRequest(String type, Throwable t, BiConsumer onError) { + String message = "Failed to export " + + type + + "s. The request could not be executed. Full error message: " + + (t.getMessage() == null ? t.getClass().getName() : t.getMessage()); + logger.log(Level.WARNING, message); + onError.accept(GrpcResponse.create(2 /* UNKNOWN */, message), t); + } + private static final class ClientRequestOnSuccessHandler implements Handler> { private final GrpcClient client; private final SocketAddress server; private final Map headers; private final boolean compressionEnabled; - private final ExporterMetrics exporterMetrics; - private final TraceRequestMarshaler marshaler; + private final Marshaler marshaler; private final AtomicBoolean loggedUnimplemented; private final ThrottlingLogger logger; private final String type; - private final int numItems; - private final CompletableResultCode result; + private final Runnable onSuccess; + private final BiConsumer onError; + private final String grpcEndpointPath; private final int attemptNumber; + private final Supplier isShutdown; public ClientRequestOnSuccessHandler(GrpcClient client, SocketAddress server, Map headers, boolean compressionEnabled, - ExporterMetrics exporterMetrics, - TraceRequestMarshaler marshaler, + Marshaler marshaler, AtomicBoolean loggedUnimplemented, ThrottlingLogger logger, String type, - int numItems, - CompletableResultCode result, - int attemptNumber) { + Runnable onSuccess, + BiConsumer onError, + int attemptNumber, + String grpcEndpointPath, + Supplier isShutdown) { this.client = client; this.server = server; + this.grpcEndpointPath = grpcEndpointPath; this.headers = headers; this.compressionEnabled = compressionEnabled; - this.exporterMetrics = exporterMetrics; this.marshaler = marshaler; this.loggedUnimplemented = loggedUnimplemented; this.logger = logger; this.type = type; - this.numItems = numItems; - this.result = result; + this.onSuccess = onSuccess; + this.onError = onError; this.attemptNumber = attemptNumber; + this.isShutdown = isShutdown; } @Override @@ -227,7 +225,7 @@ public void handle(GrpcClientRequest request) { } // Set the service name and the method to call - request.serviceName(ServiceName.create(GRPC_SERVICE_NAME)); + request.serviceName(ServiceName.create(grpcEndpointPath)); request.methodName(GRPC_METHOD_NAME); if (!headers.isEmpty()) { @@ -248,7 +246,7 @@ public void handle(GrpcClientResponse response) { response.exceptionHandler(new Handler<>() { @Override public void handle(Throwable t) { - if (attemptNumber <= MAX_ATTEMPTS) { + if (attemptNumber <= MAX_ATTEMPTS && !isShutdown.get()) { // retry initiateSend(client, server, MAX_ATTEMPTS - attemptNumber, @@ -256,19 +254,12 @@ public void handle(Throwable t) { new Consumer<>() { @Override public void accept(Throwable throwable) { - failOnClientRequest(numItems, throwable, result); + failOnClientRequest(throwable, onError, attemptNumber); } }); } else { - exporterMetrics.addFailed(numItems); - logger.log( - Level.SEVERE, - "Failed to export " - + type - + "s. The stream failed. Full error message: " - + t.getMessage()); - result.fail(); + failOnClientRequest(t, onError, attemptNumber); } } }).errorHandler(new Handler<>() { @@ -281,8 +272,7 @@ public void handle(GrpcError error) { public void handle(Void ignored) { GrpcStatus status = getStatus(response); if (status == GrpcStatus.OK) { - exporterMetrics.addSuccess(numItems); - result.succeed(); + onSuccess.run(); } else { handleError(status, response); } @@ -293,8 +283,9 @@ public void handle(Void ignored) { private void handleError(GrpcStatus status, GrpcClientResponse response) { String statusMessage = getStatusMessage(response); logAppropriateWarning(status, statusMessage); - exporterMetrics.addFailed(numItems); - result.fail(); + onError.accept( + GrpcResponse.create(2 /* UNKNOWN */, statusMessage), + new IllegalStateException(statusMessage)); } private void logAppropriateWarning(GrpcStatus status, @@ -305,7 +296,7 @@ private void logAppropriateWarning(GrpcStatus status, } } else if (status == GrpcStatus.UNAVAILABLE) { logger.log( - Level.SEVERE, + Level.WARNING, "Failed to export " + type + "s. Server is UNAVAILABLE. " @@ -359,7 +350,7 @@ private void logUnimplemented(Logger logger, String type, String fullErrorMessag } logger.log( - Level.SEVERE, + Level.WARNING, "Failed to export " + type + "s. Server responded with UNIMPLEMENTED. " @@ -401,7 +392,7 @@ private String getStatusMessage(GrpcClientResponse response) { }).onFailure(new Handler<>() { @Override public void handle(Throwable t) { - if (attemptNumber <= MAX_ATTEMPTS) { + if (attemptNumber <= MAX_ATTEMPTS && !isShutdown.get()) { // retry initiateSend(client, server, MAX_ATTEMPTS - attemptNumber, @@ -409,47 +400,38 @@ public void handle(Throwable t) { new Consumer<>() { @Override public void accept(Throwable throwable) { - failOnClientRequest(numItems, throwable, result); + failOnClientRequest(throwable, onError, attemptNumber); } }); } else { - exporterMetrics.addFailed(numItems); - logger.log( - Level.SEVERE, - "Failed to export " - + type - + "s. The request could not be executed. Full error message: " - + t.getMessage()); - result.fail(); + failOnClientRequest(t, onError, attemptNumber); } } }); } catch (IOException e) { - exporterMetrics.addFailed(numItems); - logger.log( - Level.SEVERE, - "Failed to export " - + type - + "s. Unable to serialize payload. Full error message: " - + e.getMessage()); - result.fail(); + final String message = "Failed to export " + + type + + "s. Unable to serialize payload. Full error message: " + + (e.getMessage() == null ? e.getClass().getName() : e.getMessage()); + logger.log(Level.WARNING, message); + onError.accept(GrpcResponse.create(2 /* UNKNOWN */, message), e); } } - private void failOnClientRequest(int numItems, Throwable t, CompletableResultCode result) { - exporterMetrics.addFailed(numItems); - logger.log( - Level.SEVERE, - "Failed to export " - + type - + "s. The request could not be executed. Full error message: " - + t.getMessage()); - result.fail(); + private void failOnClientRequest(Throwable t, BiConsumer onError, int attemptNumber) { + final String message = "Failed to export " + + type + + "s. The request could not be executed after " + attemptNumber + + " attempts. Full error message: " + + (t != null ? t.getMessage() : ""); + logger.log(Level.WARNING, message); + onError.accept(GrpcResponse.create(2 /* UNKNOWN */, message), t); } public ClientRequestOnSuccessHandler newAttempt() { - return new ClientRequestOnSuccessHandler(client, server, headers, compressionEnabled, exporterMetrics, marshaler, - loggedUnimplemented, logger, type, numItems, result, attemptNumber + 1); + return new ClientRequestOnSuccessHandler(client, server, headers, compressionEnabled, marshaler, + loggedUnimplemented, logger, type, onSuccess, onError, attemptNumber + 1, + grpcEndpointPath, isShutdown); } } } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/sender/VertxHttpSender.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/sender/VertxHttpSender.java new file mode 100644 index 0000000000000..e3178c18f856e --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/sender/VertxHttpSender.java @@ -0,0 +1,307 @@ +package io.quarkus.opentelemetry.runtime.exporter.otlp.sender; + +import static io.quarkus.opentelemetry.runtime.exporter.otlp.OTelExporterUtil.getPort; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPOutputStream; + +import io.opentelemetry.exporter.internal.http.HttpSender; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.internal.ThrottlingLogger; +import io.quarkus.vertx.core.runtime.BufferOutputStream; +import io.smallrye.mutiny.Uni; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.tracing.TracingPolicy; + +public final class VertxHttpSender implements HttpSender { + + public static final String TRACES_PATH = "/v1/traces"; + public static final String METRICS_PATH = "/v1/metrics"; + + private static final Logger internalLogger = Logger.getLogger(VertxHttpSender.class.getName()); + private static final ThrottlingLogger logger = new ThrottlingLogger(internalLogger); + + private static final int MAX_ATTEMPTS = 3; + + private final String basePath; + private final boolean compressionEnabled; + private final Map headers; + private final String contentType; + private final HttpClient client; + private final String signalPath; + + public VertxHttpSender( + URI baseUri, + String signalPath, + boolean compressionEnabled, + Duration timeout, + Map headersMap, + String contentType, + Consumer clientOptionsCustomizer, + Vertx vertx) { + this.basePath = determineBasePath(baseUri); + this.signalPath = signalPath; + this.compressionEnabled = compressionEnabled; + this.headers = headersMap; + this.contentType = contentType; + var httpClientOptions = new HttpClientOptions() + .setReadIdleTimeout((int) timeout.getSeconds()) + .setDefaultHost(baseUri.getHost()) + .setDefaultPort(getPort(baseUri)) + .setTracingPolicy(TracingPolicy.IGNORE); // needed to avoid tracing the calls from this http client + clientOptionsCustomizer.accept(httpClientOptions); + this.client = vertx.createHttpClient(httpClientOptions); + } + + private final AtomicBoolean isShutdown = new AtomicBoolean(); + private final CompletableResultCode shutdownResult = new CompletableResultCode(); + + private static String determineBasePath(URI baseUri) { + String path = baseUri.getPath(); + if (path.isEmpty() || path.equals("/")) { + return ""; + } + if (path.endsWith("/")) { // strip ending slash + path = path.substring(0, path.length() - 1); + } + if (!path.startsWith("/")) { // prepend leading slash + path = "/" + path; + } + return path; + } + + @Override + public void send(Consumer marshaler, + int contentLength, + Consumer onHttpResponseRead, + Consumer onError) { + if (isShutdown.get()) { + return; + } + + String requestURI = basePath + signalPath; + var clientRequestSuccessHandler = new ClientRequestSuccessHandler(client, requestURI, headers, compressionEnabled, + contentType, + contentLength, onHttpResponseRead, + onError, marshaler, 1, isShutdown::get); + initiateSend(client, requestURI, MAX_ATTEMPTS, clientRequestSuccessHandler, onError, isShutdown::get); + } + + private static void initiateSend(HttpClient client, String requestURI, + int numberOfAttempts, + Handler clientRequestSuccessHandler, + Consumer onError, + Supplier isShutdown) { + Uni.createFrom().completionStage(new Supplier>() { + @Override + public CompletionStage get() { + return client.request(HttpMethod.POST, requestURI).toCompletionStage(); + } + }) + .onFailure(new Predicate() { + @Override + public boolean test(Throwable t) { + // Will not retry on shutdown + return t instanceof IllegalStateException || + t instanceof RejectedExecutionException; + } + }) + .recoverWithUni(new Supplier>() { + @Override + public Uni get() { + return Uni.createFrom().nothing(); + } + }) + .onFailure() + .retry() + .withBackOff(Duration.ofMillis(100)) + .atMost(numberOfAttempts) + .subscribe().with( + new Consumer<>() { + @Override + public void accept(HttpClientRequest request) { + clientRequestSuccessHandler.handle(request); + } + }, onError); + } + + @Override + public CompletableResultCode shutdown() { + if (!isShutdown.compareAndSet(false, true)) { + logger.log(Level.FINE, "Calling shutdown() multiple times."); + return shutdownResult; + } + + client.close() + .onSuccess( + new Handler<>() { + @Override + public void handle(Void event) { + shutdownResult.succeed(); + } + }) + .onFailure(new Handler<>() { + @Override + public void handle(Throwable event) { + shutdownResult.fail(); + } + }); + return shutdownResult; + } + + private static class ClientRequestSuccessHandler implements Handler { + private final HttpClient client; + private final String requestURI; + private final Map headers; + private final boolean compressionEnabled; + private final String contentType; + private final int contentLength; + private final Consumer onHttpResponseRead; + private final Consumer onError; + private final Consumer marshaler; + + private final int attemptNumber; + private final Supplier isShutdown; + + public ClientRequestSuccessHandler(HttpClient client, + String requestURI, Map headers, + boolean compressionEnabled, + String contentType, + int contentLength, + Consumer onHttpResponseRead, + Consumer onError, + Consumer marshaler, + int attemptNumber, + Supplier isShutdown) { + this.client = client; + this.requestURI = requestURI; + this.headers = headers; + this.compressionEnabled = compressionEnabled; + this.contentType = contentType; + this.contentLength = contentLength; + this.onHttpResponseRead = onHttpResponseRead; + this.onError = onError; + this.marshaler = marshaler; + this.attemptNumber = attemptNumber; + this.isShutdown = isShutdown; + } + + @Override + public void handle(HttpClientRequest request) { + + HttpClientRequest clientRequest = request.response(new Handler<>() { + @Override + public void handle(AsyncResult callResult) { + if (callResult.succeeded()) { + HttpClientResponse clientResponse = callResult.result(); + Throwable cause = callResult.cause(); + clientResponse.body(new Handler<>() { + @Override + public void handle(AsyncResult bodyResult) { + if (bodyResult.succeeded()) { + if (clientResponse.statusCode() >= 500) { + if (attemptNumber <= MAX_ATTEMPTS && !isShutdown.get()) { + // we should retry for 5xx error as they might be recoverable + initiateSend(client, requestURI, + MAX_ATTEMPTS - attemptNumber, + newAttempt(), + onError, + isShutdown); + return; + } + } + onHttpResponseRead.accept(new Response() { + @Override + public int statusCode() { + return clientResponse.statusCode(); + } + + @Override + public String statusMessage() { + return clientResponse.statusMessage(); + } + + @Override + public byte[] responseBody() { + return bodyResult.result().getBytes(); + } + }); + } else { + if (attemptNumber <= MAX_ATTEMPTS && !isShutdown.get()) { + // retry + initiateSend(client, requestURI, + MAX_ATTEMPTS - attemptNumber, + newAttempt(), + onError, + isShutdown); + } else { + onError.accept(bodyResult.cause()); + } + } + } + }); + } else { + if (attemptNumber <= MAX_ATTEMPTS && !isShutdown.get()) { + // retry + initiateSend(client, requestURI, + MAX_ATTEMPTS - attemptNumber, + newAttempt(), + onError, + isShutdown); + } else { + onError.accept(callResult.cause()); + } + } + } + }) + .putHeader("Content-Type", contentType); + + Buffer buffer = Buffer.buffer(contentLength); + OutputStream os = new BufferOutputStream(buffer); + if (compressionEnabled) { + clientRequest.putHeader("Content-Encoding", "gzip"); + try (var gzos = new GZIPOutputStream(os)) { + marshaler.accept(gzos); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } else { + marshaler.accept(os); + } + + if (!headers.isEmpty()) { + for (var entry : headers.entrySet()) { + clientRequest.putHeader(entry.getKey(), entry.getValue()); + } + } + + clientRequest.send(buffer); + } + + public ClientRequestSuccessHandler newAttempt() { + return new ClientRequestSuccessHandler(client, requestURI, headers, compressionEnabled, + contentType, contentLength, onHttpResponseRead, + onError, marshaler, attemptNumber + 1, isShutdown); + } + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/LateBoundBatchSpanProcessor.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/LateBoundBatchSpanProcessor.java similarity index 97% rename from extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/LateBoundBatchSpanProcessor.java rename to extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/LateBoundBatchSpanProcessor.java index dde43e7c9dcc0..90318940e3497 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/LateBoundBatchSpanProcessor.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/LateBoundBatchSpanProcessor.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.runtime.exporter.otlp; +package io.quarkus.opentelemetry.runtime.exporter.otlp.tracing; import org.jboss.logging.Logger; diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/RemoveableLateBoundBatchSpanProcessor.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/RemoveableLateBoundBatchSpanProcessor.java similarity index 90% rename from extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/RemoveableLateBoundBatchSpanProcessor.java rename to extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/RemoveableLateBoundBatchSpanProcessor.java index da44a0084b5b4..d8654e5ff634e 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/RemoveableLateBoundBatchSpanProcessor.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/RemoveableLateBoundBatchSpanProcessor.java @@ -1,4 +1,4 @@ -package io.quarkus.opentelemetry.runtime.exporter.otlp; +package io.quarkus.opentelemetry.runtime.exporter.otlp.tracing; import io.quarkus.opentelemetry.runtime.AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracerProviderCustomizer; diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/VertxGrpcSpanExporter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/VertxGrpcSpanExporter.java new file mode 100644 index 0000000000000..58e03f24a4e45 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/VertxGrpcSpanExporter.java @@ -0,0 +1,34 @@ +package io.quarkus.opentelemetry.runtime.exporter.otlp.tracing; + +import java.util.Collection; + +import io.opentelemetry.exporter.internal.grpc.GrpcExporter; +import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +public final class VertxGrpcSpanExporter implements SpanExporter { + + private final GrpcExporter delegate; + + public VertxGrpcSpanExporter(GrpcExporter delegate) { + this.delegate = delegate; + } + + @Override + public CompletableResultCode export(Collection spans) { + TraceRequestMarshaler exportRequest = TraceRequestMarshaler.create(spans); + return delegate.export(exportRequest, spans.size()); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/VertxHttpSpanExporter.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/VertxHttpSpanExporter.java new file mode 100644 index 0000000000000..dedba95064e45 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/tracing/VertxHttpSpanExporter.java @@ -0,0 +1,34 @@ +package io.quarkus.opentelemetry.runtime.exporter.otlp.tracing; + +import java.util.Collection; + +import io.opentelemetry.exporter.internal.http.HttpExporter; +import io.opentelemetry.exporter.internal.otlp.traces.TraceRequestMarshaler; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +public final class VertxHttpSpanExporter implements SpanExporter { + + private final HttpExporter delegate; + + public VertxHttpSpanExporter(HttpExporter delegate) { + this.delegate = delegate; + } + + @Override + public CompletableResultCode export(Collection spans) { + TraceRequestMarshaler exportRequest = TraceRequestMarshaler.create(spans); + return delegate.export(exportRequest, spans.size()); + } + + @Override + public CompletableResultCode flush() { + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/graal/Substitutions.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/graal/Substitutions.java index 9893313af25a5..f5809d4d27a19 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/graal/Substitutions.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/graal/Substitutions.java @@ -1,7 +1,6 @@ package io.quarkus.opentelemetry.runtime.graal; import java.io.Closeable; -import java.util.Collections; import java.util.List; import java.util.function.BiFunction; @@ -14,26 +13,9 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder; import io.opentelemetry.sdk.logs.export.LogRecordExporter; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.MetricReader; public class Substitutions { - @TargetClass(className = "io.opentelemetry.sdk.autoconfigure.MeterProviderConfiguration") - static final class Target_MeterProviderConfiguration { - - @Substitute - static List configureMetricReaders( - ConfigProperties config, - SpiHelper spiHelper, - BiFunction metricExporterCustomizer, - List closeables) { - // OTel metrics not supported and there is no need to call - // MetricExporterConfiguration.configurePrometheusMetricReader down the line. - return Collections.emptyList(); - } - } - @TargetClass(className = "io.opentelemetry.sdk.autoconfigure.LoggerProviderConfiguration") static final class Target_LoggerProviderConfiguration { diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/metrics/cdi/MetricsProducer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/metrics/cdi/MetricsProducer.java new file mode 100644 index 0000000000000..14972b1c82341 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/metrics/cdi/MetricsProducer.java @@ -0,0 +1,29 @@ +package io.quarkus.opentelemetry.runtime.metrics.cdi; + +import static io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig.INSTRUMENTATION_NAME; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Singleton; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.sdk.common.Clock; +import io.quarkus.arc.DefaultBean; + +@Singleton +public class MetricsProducer { + @Produces + @ApplicationScoped + @DefaultBean + public Meter getMeter() { + return GlobalOpenTelemetry.getMeter(INSTRUMENTATION_NAME); + } + + @Produces + @Singleton + @DefaultBean + public Clock getClock() { + return Clock.getDefault(); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/metrics/spi/MetricsExporterCDIProvider.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/metrics/spi/MetricsExporterCDIProvider.java new file mode 100644 index 0000000000000..03c63704d44df --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/metrics/spi/MetricsExporterCDIProvider.java @@ -0,0 +1,38 @@ +package io.quarkus.opentelemetry.runtime.metrics.spi; + +import static io.quarkus.opentelemetry.runtime.config.build.ExporterType.Constants.CDI_VALUE; + +import java.util.logging.Logger; + +import jakarta.enterprise.inject.Any; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.spi.CDI; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.quarkus.opentelemetry.runtime.exporter.otlp.metrics.NoopMetricExporter; + +public class MetricsExporterCDIProvider implements ConfigurableMetricExporterProvider { + + Logger log = Logger.getLogger(MetricsExporterCDIProvider.class.getName()); + + @Override + public MetricExporter createExporter(ConfigProperties configProperties) { + Instance exporters = CDI.current().select(MetricExporter.class, Any.Literal.INSTANCE); + log.fine("available exporters: " + exporters.stream() + .map(e -> e.getClass().getName()) + .reduce((a, b) -> a + ", " + b) + .orElse("none")); + if (exporters.isUnsatisfied()) { + return NoopMetricExporter.INSTANCE; + } else { + return exporters.get(); + } + } + + @Override + public String getName() { + return CDI_VALUE; + } +} diff --git a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider new file mode 100644 index 0000000000000..124be714f4e1f --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider @@ -0,0 +1 @@ +io.quarkus.opentelemetry.runtime.metrics.spi.MetricsExporterCDIProvider \ No newline at end of file diff --git a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.jboss.resteasy.spi.concurrent.ThreadContext b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.jboss.resteasy.spi.concurrent.ThreadContext index c2b7e3c4bf1e7..972c5e8b0ec4f 100644 --- a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.jboss.resteasy.spi.concurrent.ThreadContext +++ b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/org.jboss.resteasy.spi.concurrent.ThreadContext @@ -1 +1 @@ -io.quarkus.opentelemetry.runtime.tracing.intrumentation.resteasy.OpenTelemetryClassicThreadContext +io.quarkus.opentelemetry.runtime.tracing.intrumentation.resteasy.OpenTelemetryClassicThreadContext \ No newline at end of file diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OtlpExporterProviderTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OtlpExporterProviderTest.java index b3391b3aa932e..7275aecf8b81b 100644 --- a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OtlpExporterProviderTest.java +++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OtlpExporterProviderTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test; import io.quarkus.opentelemetry.runtime.config.runtime.exporter.CompressionType; +import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterMetricsConfig; import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig; import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterTracesConfig; @@ -172,6 +173,82 @@ public Optional host() { } }; } + + @Override + public OtlpExporterMetricsConfig metrics() { + return new OtlpExporterMetricsConfig() { + @Override + public Optional temporalityPreference() { + return Optional.empty(); + } + + @Override + public Optional defaultHistogramAggregation() { + return Optional.empty(); + } + + @Override + public Optional endpoint() { + return Optional.ofNullable(newTrace); + } + + @Override + public Optional> headers() { + return Optional.empty(); + } + + @Override + public Optional compression() { + return Optional.empty(); + } + + @Override + public Duration timeout() { + return null; + } + + @Override + public Optional protocol() { + return Optional.empty(); + } + + @Override + public KeyCert keyCert() { + return new KeyCert() { + @Override + public Optional> keys() { + return Optional.empty(); + } + + @Override + public Optional> certs() { + return Optional.empty(); + } + }; + } + + @Override + public TrustCert trustCert() { + return new TrustCert() { + @Override + public Optional> certs() { + return Optional.empty(); + } + }; + } + + @Override + public Optional tlsConfigurationName() { + return Optional.empty(); + } + + @Override + public ProxyConfig proxyOptions() { + return null; + } + }; + + } }; } } diff --git a/integration-tests/opentelemetry-vertx-exporter/src/main/java/io/quarkus/it/opentelemetry/vertx/exporter/HelloResource.java b/integration-tests/opentelemetry-vertx-exporter/src/main/java/io/quarkus/it/opentelemetry/vertx/exporter/HelloResource.java index f8c3ccc5a605b..a98fe5bfa1b10 100644 --- a/integration-tests/opentelemetry-vertx-exporter/src/main/java/io/quarkus/it/opentelemetry/vertx/exporter/HelloResource.java +++ b/integration-tests/opentelemetry-vertx-exporter/src/main/java/io/quarkus/it/opentelemetry/vertx/exporter/HelloResource.java @@ -1,13 +1,22 @@ package io.quarkus.it.opentelemetry.vertx.exporter; +import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; + @Path("hello") public class HelloResource { + @Inject + Meter meter; + @GET public String get() { + meter.counterBuilder("hello").build().add(1, Attributes.of(AttributeKey.stringKey("key"), "value")); return "get"; } diff --git a/integration-tests/opentelemetry-vertx-exporter/src/main/resources/application.properties b/integration-tests/opentelemetry-vertx-exporter/src/main/resources/application.properties index edb424258ea0a..b45db46c4211b 100644 --- a/integration-tests/opentelemetry-vertx-exporter/src/main/resources/application.properties +++ b/integration-tests/opentelemetry-vertx-exporter/src/main/resources/application.properties @@ -1 +1,3 @@ quarkus.application.name=integration test +quarkus.otel.metrics.enabled=true +quarkus.otel.metric.export.interval=1000ms \ No newline at end of file diff --git a/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/AbstractExporterTest.java b/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/AbstractExporterTest.java index 0dc4e49f6413f..d3f0968c8f997 100644 --- a/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/AbstractExporterTest.java +++ b/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/AbstractExporterTest.java @@ -1,18 +1,29 @@ package io.quarkus.it.opentelemetry.vertx.exporter; +import static io.opentelemetry.semconv.ResourceAttributes.SERVICE_NAME; import static io.restassured.RestAssured.when; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.KeyValue; +import io.opentelemetry.proto.metrics.v1.AggregationTemporality; +import io.opentelemetry.proto.metrics.v1.Metric; +import io.opentelemetry.proto.metrics.v1.NumberDataPoint; +import io.opentelemetry.proto.metrics.v1.ResourceMetrics; +import io.opentelemetry.proto.metrics.v1.Sum; import io.opentelemetry.proto.trace.v1.ResourceSpans; import io.opentelemetry.proto.trace.v1.ScopeSpans; import io.opentelemetry.proto.trace.v1.Span; @@ -21,17 +32,20 @@ public abstract class AbstractExporterTest { Traces traces; + Metrics metrics; @BeforeEach @AfterEach void setUp() { traces.reset(); + metrics.reset(); } @Test void test() { verifyHttpResponse(); verifyTraces(); + verifyMetrics(); } private void verifyHttpResponse() { @@ -53,7 +67,7 @@ private void verifyTraces() { assertThat(resourceSpans.getResource().getAttributesList()) .contains( KeyValue.newBuilder() - .setKey(ResourceAttributes.SERVICE_NAME.getKey()) + .setKey(SERVICE_NAME.getKey()) .setValue(AnyValue.newBuilder() .setStringValue("integration test").build()) .build()) @@ -76,4 +90,46 @@ private void verifyTraces() { .setStringValue("GET").build()) .build()); } + + private void verifyMetrics() { + List metricRequests = metrics.getMetricRequests(); + await() + .atMost(Duration.ofSeconds(30)) + .untilAsserted(() -> assertThat(metricRequests).hasSizeGreaterThan(1)); + ExportMetricsServiceRequest request = metricRequests.get(metricRequests.size() - 1); + assertEquals(1, request.getResourceMetricsCount()); + + ResourceMetrics resourceMetrics = request.getResourceMetrics(0); + assertThat(resourceMetrics.getResource().getAttributesList()) + .contains( + KeyValue.newBuilder() + .setKey(SERVICE_NAME.getKey()) + .setValue(AnyValue.newBuilder().setStringValue("integration test").build()) + .build()); + assertThat(resourceMetrics.getScopeMetricsCount()).isEqualTo(2); + + Optional helloMetric = resourceMetrics.getScopeMetricsList().stream() + .map(scopeMetrics -> scopeMetrics.getMetricsList()) + .filter(metrics -> metrics.stream().anyMatch(metric -> metric.getName().equals("hello"))) + .flatMap(List::stream) + .findFirst(); + + assertThat(helloMetric).isPresent(); + assertThat(helloMetric.get().getDataCase()).isEqualTo(Metric.DataCase.SUM); + + Sum sum = helloMetric.get().getSum(); + assertThat(sum.getAggregationTemporality()) + .isEqualTo(AggregationTemporality.AGGREGATION_TEMPORALITY_CUMULATIVE); + assertThat(sum.getDataPointsCount()).isEqualTo(1); + + NumberDataPoint dataPoint = sum.getDataPoints(0); + assertThat(dataPoint.getAsInt()).isEqualTo(1); + assertThat(dataPoint.getAttributesList()) + .isEqualTo( + Collections.singletonList( + KeyValue.newBuilder() + .setKey("key") + .setValue(AnyValue.newBuilder().setStringValue("value").build()) + .build())); + } } diff --git a/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/Metrics.java b/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/Metrics.java new file mode 100644 index 0000000000000..7448563dbd3af --- /dev/null +++ b/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/Metrics.java @@ -0,0 +1,19 @@ +package io.quarkus.it.opentelemetry.vertx.exporter; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; + +public final class Metrics { + + private final List metricRequests = new CopyOnWriteArrayList<>(); + + public List getMetricRequests() { + return metricRequests; + } + + public void reset() { + metricRequests.clear(); + } +} diff --git a/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/OtelCollectorLifecycleManager.java b/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/OtelCollectorLifecycleManager.java index bcb6bd7eda002..718817a3d543a 100644 --- a/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/OtelCollectorLifecycleManager.java +++ b/integration-tests/opentelemetry-vertx-exporter/src/test/java/io/quarkus/it/opentelemetry/vertx/exporter/OtelCollectorLifecycleManager.java @@ -1,6 +1,6 @@ package io.quarkus.it.opentelemetry.vertx.exporter; -import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterTracesConfig.Protocol.GRPC; +import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfig.Protocol.GRPC; import static org.testcontainers.Testcontainers.exposeHostPorts; import java.util.HashMap; @@ -18,6 +18,7 @@ import com.google.protobuf.InvalidProtocolBufferException; +import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest; import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; @@ -42,7 +43,9 @@ public class OtelCollectorLifecycleManager implements QuarkusTestResourceLifecyc private static final Integer COLLECTOR_HEALTH_CHECK_PORT = 13133; private static final ServiceName TRACE_SERVICE_NAME = ServiceName .create("opentelemetry.proto.collector.trace.v1.TraceService"); - private static final String TRACE_METHOD_NAME = "Export"; + private static final ServiceName METRIC_SERVICE_NAME = ServiceName + .create("opentelemetry.proto.collector.metrics.v1.MetricsService"); + private static final String EXPORT_METHOD_NAME = "Export"; private SelfSignedCertificate serverTls; private SelfSignedCertificate clientTlS; @@ -58,6 +61,7 @@ public class OtelCollectorLifecycleManager implements QuarkusTestResourceLifecyc private GenericContainer collector; private Traces collectedTraces; + private Metrics collectedMetrics; @Override public void init(Map initArgs) { @@ -128,6 +132,7 @@ public Map start() { Map result = new HashMap<>(); result.put("quarkus.otel.exporter.otlp.traces.protocol", protocol); + result.put("quarkus.otel.exporter.otlp.metrics.protocol", protocol); boolean isGrpc = GRPC.equals(protocol); int secureEndpointPort = isGrpc ? COLLECTOR_OTLP_GRPC_MTLS_PORT : COLLECTOR_OTLP_HTTP_MTLS_PORT; @@ -136,8 +141,11 @@ public Map start() { if (enableTLS) { result.put("quarkus.otel.exporter.otlp.traces.endpoint", "https://" + collector.getHost() + ":" + collector.getMappedPort(secureEndpointPort)); + result.put("quarkus.otel.exporter.otlp.metrics.endpoint", + "https://" + collector.getHost() + ":" + collector.getMappedPort(secureEndpointPort)); if (tlsRegistryName != null) { result.put("quarkus.otel.exporter.otlp.traces.tls-configuration-name", tlsRegistryName); + result.put("quarkus.otel.exporter.otlp.metrics.tls-configuration-name", tlsRegistryName); if (!preventTrustCert) { result.put(String.format("quarkus.tls.%s.trust-store.pem.certs", tlsRegistryName), serverTls.certificatePath()); @@ -147,17 +155,23 @@ public Map start() { } else { if (!preventTrustCert) { result.put("quarkus.otel.exporter.otlp.traces.trust-cert.certs", serverTls.certificatePath()); + result.put("quarkus.otel.exporter.otlp.metrics.trust-cert.certs", serverTls.certificatePath()); } result.put("quarkus.otel.exporter.otlp.traces.key-cert.certs", clientTlS.certificatePath()); result.put("quarkus.otel.exporter.otlp.traces.key-cert.keys", clientTlS.privateKeyPath()); + result.put("quarkus.otel.exporter.otlp.metrics.key-cert.certs", clientTlS.certificatePath()); + result.put("quarkus.otel.exporter.otlp.metrics.key-cert.keys", clientTlS.privateKeyPath()); } } else { result.put("quarkus.otel.exporter.otlp.traces.endpoint", "http://" + collector.getHost() + ":" + collector.getMappedPort(inSecureEndpointPort)); + result.put("quarkus.otel.exporter.otlp.metrics.endpoint", + "http://" + collector.getHost() + ":" + collector.getMappedPort(inSecureEndpointPort)); } if (enableCompression) { result.put("quarkus.otel.exporter.otlp.traces.compression", "gzip"); + result.put("quarkus.otel.exporter.otlp.metrics.compression", "gzip"); } return result; @@ -166,15 +180,17 @@ public Map start() { @Override public void inject(TestInjector testInjector) { testInjector.injectIntoFields(collectedTraces, f -> f.getType().equals(Traces.class)); + testInjector.injectIntoFields(collectedMetrics, f -> f.getType().equals(Metrics.class)); } private void setupVertxGrpcServer() { vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(1).setEventLoopPoolSize(1)); GrpcServer grpcServer = GrpcServer.server(vertx); collectedTraces = new Traces(); + collectedMetrics = new Metrics(); grpcServer.callHandler(request -> { - if (request.serviceName().equals(TRACE_SERVICE_NAME) && request.methodName().equals(TRACE_METHOD_NAME)) { + if (request.serviceName().equals(TRACE_SERVICE_NAME) && request.methodName().equals(EXPORT_METHOD_NAME)) { request.handler(message -> { try { @@ -186,12 +202,25 @@ private void setupVertxGrpcServer() { .end(); } }); + } else if (request.serviceName().equals(METRIC_SERVICE_NAME) && request.methodName().equals(EXPORT_METHOD_NAME)) { + + request.handler(message -> { + try { + collectedMetrics.getMetricRequests().add(ExportMetricsServiceRequest.parseFrom(message.getBytes())); + request.response().end(Buffer.buffer(ExportMetricsServiceRequest.getDefaultInstance().toByteArray())); + } catch (InvalidProtocolBufferException e) { + request.response() + .status(GrpcStatus.INVALID_ARGUMENT) + .end(); + } + }); } else { request.response() .status(GrpcStatus.NOT_FOUND) .end(); } }); + server = vertx.createHttpServer(new HttpServerOptions().setPort(0)); try { server.requestHandler(grpcServer).listen().toCompletionStage().toCompletableFuture().get(20, TimeUnit.SECONDS); diff --git a/integration-tests/opentelemetry-vertx-exporter/src/test/resources/otel-config.yaml b/integration-tests/opentelemetry-vertx-exporter/src/test/resources/otel-config.yaml index bab60e41819c7..427a292121c46 100644 --- a/integration-tests/opentelemetry-vertx-exporter/src/test/resources/otel-config.yaml +++ b/integration-tests/opentelemetry-vertx-exporter/src/test/resources/otel-config.yaml @@ -32,9 +32,9 @@ exporters: service: extensions: [health_check] pipelines: -# metrics: -# receivers: [otlp, otlp/mtls] -# exporters: [logging, otlp] + metrics: + receivers: [otlp, otlp/mtls] + exporters: [logging, otlp] traces: receivers: [otlp, otlp/mtls] exporters: [logging, otlp] diff --git a/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java b/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java index a611fda7c2c7b..19048542c7907 100644 --- a/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java +++ b/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/ExporterResource.java @@ -1,5 +1,7 @@ package io.quarkus.it.opentelemetry; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -9,32 +11,65 @@ import jakarta.inject.Singleton; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Response; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter; import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.semconv.SemanticAttributes; @Path("") public class ExporterResource { @Inject InMemorySpanExporter inMemorySpanExporter; + @Inject + InMemoryMetricExporter inMemoryMetricExporter; @GET @Path("/reset") public Response reset() { inMemorySpanExporter.reset(); + inMemoryMetricExporter.reset(); return Response.ok().build(); } @GET @Path("/export") - public List export() { + public List exportTraces() { return inMemorySpanExporter.getFinishedSpanItems() .stream() .filter(sd -> !sd.getName().contains("export") && !sd.getName().contains("reset")) .collect(Collectors.toList()); } + @GET + @Path("/export/metrics") + public List exportMetrics(@QueryParam("name") String name, @QueryParam("target") String target) { + return Collections.unmodifiableList(new ArrayList<>( + inMemoryMetricExporter.getFinishedMetricItems().stream() + .filter(metricData -> name == null ? true : metricData.getName().equals(name)) + .filter(metricData -> target == null ? true + : metricData.getData() + .getPoints().stream() + .anyMatch(point -> isPathFound(target, point.getAttributes()))) + .collect(Collectors.toList()))); + } + + private static boolean isPathFound(String path, Attributes attributes) { + if (path == null) { + return true;// any match + } + Object value = attributes.asMap().get(AttributeKey.stringKey(SemanticAttributes.HTTP_ROUTE.getKey())); + if (value == null) { + return false; + } + return value.toString().equals(path); + } + @ApplicationScoped static class InMemorySpanExporterProducer { @Produces @@ -43,4 +78,13 @@ InMemorySpanExporter inMemorySpanExporter() { return InMemorySpanExporter.create(); } } + + @ApplicationScoped + static class InMemoryMetricExporterProducer { + @Produces + @Singleton + InMemoryMetricExporter inMemoryMetricsExporter() { + return InMemoryMetricExporter.create(); + } + } } diff --git a/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/SimpleResource.java b/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/SimpleResource.java index 088a66a943c29..878ea3a44e6ac 100644 --- a/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/SimpleResource.java +++ b/integration-tests/opentelemetry/src/main/java/io/quarkus/it/opentelemetry/SimpleResource.java @@ -11,6 +11,9 @@ import org.eclipse.microprofile.rest.client.inject.RestClient; import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.context.Scope; @Path("") @@ -41,6 +44,9 @@ public interface SimpleClient { @Inject Baggage baggage; + @Inject + Meter meter; + @GET public TraceData noPath() { TraceData data = new TraceData(); @@ -88,6 +94,19 @@ public TraceData directTrace() { return data; } + @GET + @Path("/direct-metrics") + public TraceData directTraceWithMetrics() { + meter.counterBuilder("direct-trace-counter") + .setUnit("items") + .setDescription("A counter of direct traces") + .build() + .add(1, Attributes.of(AttributeKey.stringKey("key"), "low-cardinality-value")); + TraceData data = new TraceData(); + data.message = "Direct trace"; + return data; + } + @GET @Path("/chained") public TraceData chainedTrace() { diff --git a/integration-tests/opentelemetry/src/main/resources/application.properties b/integration-tests/opentelemetry/src/main/resources/application.properties index 514f563c31933..6ea19c81c9929 100644 --- a/integration-tests/opentelemetry/src/main/resources/application.properties +++ b/integration-tests/opentelemetry/src/main/resources/application.properties @@ -5,6 +5,8 @@ quarkus.application.version=999-SNAPSHOT # speed up build quarkus.otel.bsp.schedule.delay=100 quarkus.otel.bsp.export.timeout=5s +quarkus.otel.metrics.enabled=true +quarkus.otel.metric.export.interval=100ms pingpong/mp-rest/url=${test.url} simple/mp-rest/url=${test.url} diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsIT.java b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsIT.java new file mode 100644 index 0000000000000..ab89e166dd7ef --- /dev/null +++ b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsIT.java @@ -0,0 +1,7 @@ +package io.quarkus.it.opentelemetry; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class MetricsIT extends MetricsTest { +} diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsTest.java b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsTest.java new file mode 100644 index 0000000000000..8dfe4fb3fea5f --- /dev/null +++ b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsTest.java @@ -0,0 +1,71 @@ +package io.quarkus.it.opentelemetry; + +import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.given; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.common.mapper.TypeRef; + +@QuarkusTest +public class MetricsTest { + @BeforeEach + @AfterEach + void reset() { + given().get("/reset").then().statusCode(HTTP_OK); + } + + private List> getSpans() { + return get("/export").body().as(new TypeRef<>() { + }); + } + + private List> getMetrics() { + return given() + .when() + .queryParam("name", "direct-trace-counter") + .get("/export/metrics") + .body().as(new TypeRef<>() { + }); + } + + @Test + public void directCounterTest() { + given() + .when() + .get("/direct-metrics") + .then() + .statusCode(200); + given() + .when().get("/direct-metrics") + .then() + .statusCode(200); + + await().atMost(5, SECONDS).until(() -> getSpans().size() == 2); + await().atMost(10, SECONDS).until(() -> getMetrics().size() > 2); + + List> metrics = getMetrics(); + Integer value = (Integer) ((Map) ((List) ((Map) (getMetrics() + .get(metrics.size() - 1) + .get("longSumData"))) + .get("points")) + .get(0)) + .get("value"); + + assertEquals(2, value, "received: " + given() + .when() + .get("/export/metrics") + .body().as(new TypeRef<>() { + })); + } +} diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/StaticResourceTest.java b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/StaticResourceTest.java index 6412826e33141..8d6934bc7ebe0 100644 --- a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/StaticResourceTest.java +++ b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/StaticResourceTest.java @@ -24,8 +24,15 @@ public class StaticResourceTest { @BeforeEach @AfterEach void reset() { - given().get("/reset").then().statusCode(HTTP_OK); - await().atMost(5, TimeUnit.SECONDS).until(() -> getSpans().size() == 0); + await().atMost(5, TimeUnit.SECONDS).until(() -> { + List> spans = getSpans(); + if (spans.size() == 0) { + return true; + } else { + given().get("/reset").then().statusCode(HTTP_OK); + return false; + } + }); } @Test diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryIT.java b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingIT.java similarity index 93% rename from integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryIT.java rename to integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingIT.java index a7e516388cfd1..0d5e378981885 100644 --- a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryIT.java +++ b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingIT.java @@ -7,7 +7,7 @@ import io.quarkus.test.junit.QuarkusIntegrationTest; @QuarkusIntegrationTest -public class OpenTelemetryIT extends OpenTelemetryTest { +public class TracingIT extends TracingTest { @Override protected void buildGlobalTelemetryInstance() { // When running native tests the test class is outside the Quarkus application, diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryTest.java b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingTest.java similarity index 99% rename from integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryTest.java rename to integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingTest.java index 8b9173127ea6d..afe87de653e74 100644 --- a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryTest.java +++ b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/TracingTest.java @@ -49,7 +49,7 @@ import io.restassured.specification.RequestSpecification; @QuarkusTest -public class OpenTelemetryTest { +public class TracingTest { @TestHTTPResource("direct") URL directUrl; @TestHTTPResource("chained") @@ -62,8 +62,15 @@ public class OpenTelemetryTest { @BeforeEach @AfterEach void reset() { - given().get("/reset").then().statusCode(HTTP_OK); - await().atMost(5, SECONDS).until(() -> getSpans().size() == 0); + await().atMost(5, SECONDS).until(() -> { + List> spans = getSpans(); + if (spans.size() == 0) { + return true; + } else { + given().get("/reset").then().statusCode(HTTP_OK); + return false; + } + }); } private List> getSpans() { diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/tck/SpanBeanTest.java b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/tck/SpanBeanTest.java new file mode 100644 index 0000000000000..4a4106d636adf --- /dev/null +++ b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/tck/SpanBeanTest.java @@ -0,0 +1,68 @@ +package io.quarkus.it.opentelemetry.tck; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import io.quarkus.test.junit.QuarkusTest; + +/** + * Same as in the TCK, for early warning and debugging + */ +@QuarkusTest +public class SpanBeanTest { + @Inject + private Span injectedSpan; + + @Inject + private Tracer tracer; + + @Test + public void spanBeanChange() { + Span originalSpan = Span.current(); + // Check the injected span reflects the current span initially + assertEquals(originalSpan.getSpanContext().getSpanId(), injectedSpan.getSpanContext().getSpanId()); + + // Create a new span + Span span1 = tracer.spanBuilder("span1").startSpan(); + // Check we have a real span with a different spanId + assertNotEquals(originalSpan.getSpanContext().getSpanId(), span1.getSpanContext().getSpanId()); + + // The original span should still be "current", so the injected span should still reflect it + assertEquals(originalSpan.getSpanContext().getSpanId(), injectedSpan.getSpanContext().getSpanId()); + + // Make span1 current + try (Scope s = span1.makeCurrent()) { + Span current = Span.current(); + // Now the injected span should reflect span1 + assertEquals(span1.getSpanContext().getSpanId(), injectedSpan.getSpanContext().getSpanId()); + + // Make a new span + Span span2 = tracer.spanBuilder("span2").startSpan(); + // Injected span should still reflect span1 + assertEquals(span1.getSpanContext().getSpanId(), injectedSpan.getSpanContext().getSpanId()); + + // Make span2 current + try (Scope s2 = span2.makeCurrent()) { + // Now the injected span should reflect span2 + assertEquals(span2.getSpanContext().getSpanId(), injectedSpan.getSpanContext().getSpanId()); + } finally { + span2.end(); + } + + // After closing the scope, span1 is current again and the injected bean should reflect that + assertEquals(span1.getSpanContext().getSpanId(), injectedSpan.getSpanContext().getSpanId()); + } finally { + span1.end(); + } + + // After closing the scope, the original span is current again + assertEquals(originalSpan.getSpanContext().getSpanId(), injectedSpan.getSpanContext().getSpanId()); + } +} From eecc1ae55a45042e1fcb1bca36d70f8ad845a085 Mon Sep 17 00:00:00 2001 From: Rolfe Dlugy-Hegwer Date: Mon, 8 Jul 2024 13:49:07 -0400 Subject: [PATCH 53/94] For NOTE and TIP, use admonition syntax instead of ID syntax --- docs/src/main/asciidoc/security-customization.adoc | 2 +- docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/security-customization.adoc b/docs/src/main/asciidoc/security-customization.adoc index 5a0ee041e6672..b42ce570911ed 100644 --- a/docs/src/main/asciidoc/security-customization.adoc +++ b/docs/src/main/asciidoc/security-customization.adoc @@ -682,7 +682,7 @@ The observers can be either synchronous or asynchronous. * `io.quarkus.oidc.SecurityEvent` * `io.quarkus.vertx.http.runtime.security.FormAuthenticationEvent` -[[TIP]] +[TIP] For more information about security events specific to the Quarkus OpenID Connect extension, please see the xref:security-oidc-code-flow-authentication.adoc#listen-to-authentication-events[Listening to important authentication events] section of the OIDC code flow mechanism for protecting web applications guide. diff --git a/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc b/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc index 6e1ffbb1d991f..6fe1d7753f41e 100644 --- a/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc +++ b/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc @@ -689,7 +689,7 @@ quarkus.oidc.auth-server-url=https://dev-3ve0cgn7.us.auth0.com which is all what is needed for the OIDC `service` application to fetch Auth0 public verification keys and use them to verify Auth0 access tokens in JWT format. -[[NOTE]] +[NOTE] ==== In this tutorial you have already configured the OIDC `hybrid` application which can handle both authorization code and bearer token authentication flows. In production you will run microservices as separate servers but for the sake of simplicity `ApiEchoService` will not have to be started as a second server with its own configuration containing `quarkus.oidc.auth-server-url=https://dev-3ve0cgn7.us.auth0.com` only, and therefore the current configuration which already has the Auth0 dev tenant address configured will be reused. From 582ed717770a79b44830de078c47150107c062af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 22:06:38 +0000 Subject: [PATCH 54/94] Bump wildfly-elytron.version from 2.4.2.Final to 2.5.0.Final Bumps `wildfly-elytron.version` from 2.4.2.Final to 2.5.0.Final. Updates `org.wildfly.security:wildfly-elytron` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-ssh-util` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-auth-server` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-password-impl` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-realm` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-realm-token` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-realm-jdbc` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-realm-ldap` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-ssl` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-sasl-plain` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-sasl-digest` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-sasl-external` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-sasl-oauth2` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-sasl-scram` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-x500-cert` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-credential` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-sasl-gs2` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-asn1` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-sasl-gssapi` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-security-manager-action` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-auth` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-base` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-http` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-keystore` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-mechanism-digest` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-mechanism-gssapi` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-mechanism-oauth2` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-mechanism-scram` from 2.4.2.Final to 2.5.0.Final Updates `org.wildfly.security:wildfly-elytron-mechanism` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-permission` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-provider-util` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-sasl` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-util` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-x500-cert-util` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) Updates `org.wildfly.security:wildfly-elytron-x500` from 2.4.2.Final to 2.5.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.4.2.Final...2.5.0.Final) --- updated-dependencies: - dependency-name: org.wildfly.security:wildfly-elytron dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-ssh-util dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-auth-server dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-password-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-realm dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-realm-token dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-realm-jdbc dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-realm-ldap dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-ssl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-plain dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-digest dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-external dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-oauth2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-scram dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-x500-cert dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-credential dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-gs2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-asn1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-gssapi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-security-manager-action dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-auth dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-base dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-http dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-keystore dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-digest dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-gssapi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-oauth2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-scram dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-permission dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-provider-util dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-util dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-x500-cert-util dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-x500 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 6a4cbeaf71ea3..33da9cafc0641 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.4.2.Final + 2.5.0.Final 2.1.4.SP1 3.6.1.Final 4.5.8 From 423b4f4d7dc159a605ab139e33d827791e606a78 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Mon, 8 Jul 2024 13:03:31 -0300 Subject: [PATCH 55/94] Avoid storing timestamp in Gradle.properties --- .github/dependabot.yml | 1 + .../archetype-resources/gradle.properties | 4 ++-- .../base/gradle.tpl.qute.properties | 5 +++-- .../gradle/base/gradle.tpl.qute.properties | 5 +++-- .../tools/devtools-common/pom.xml | 4 ++++ .../buildfile/AbstractGradleBuildFile.java | 21 ++++++++++--------- .../AbstractGradleBuildFilesCreator.java | 7 ++++++- .../devtools/project/buildfile/BuildFile.java | 4 ++++ independent-projects/tools/pom.xml | 6 ++++++ .../compile-only-lombok/gradle.properties | 6 +++--- .../gradle.properties | 4 ++-- .../gradle.properties | 4 ++-- .../gradle.properties | 5 +++-- 13 files changed, 50 insertions(+), 26 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 813bcbca04fc3..2efe111f0a409 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -186,6 +186,7 @@ updates: - dependency-name: biz.paluch.logging:logstash-gelf - dependency-name: org.bitbucket.b_c:jose4j - dependency-name: io.fabric8:maven-model-helper + - dependency-name: org.codejive:java-properties ignore: # this one cannot be upgraded due to the usage of proxies in new versions # the proxy implements interfaces in a random order which causes issues diff --git a/extensions/amazon-lambda/maven-archetype/src/main/resources/archetype-resources/gradle.properties b/extensions/amazon-lambda/maven-archetype/src/main/resources/archetype-resources/gradle.properties index 644310c2b3a94..c77b7693b0052 100644 --- a/extensions/amazon-lambda/maven-archetype/src/main/resources/archetype-resources/gradle.properties +++ b/extensions/amazon-lambda/maven-archetype/src/main/resources/archetype-resources/gradle.properties @@ -1,5 +1,5 @@ -#Gradle properties -#Tue Mar 17 10:20:48 UTC 2020 +# Gradle properties + quarkusPluginVersion=${project.version} quarkusPlatformArtifactId=quarkus-bom quarkusPlatformVersion=${project.version} diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/base/gradle.tpl.qute.properties b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/base/gradle.tpl.qute.properties index d3160667382c5..80d94672c2262 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/base/gradle.tpl.qute.properties +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle-kotlin-dsl/base/gradle.tpl.qute.properties @@ -1,6 +1,7 @@ -#Gradle properties +# Gradle properties + quarkusPluginId={quarkus.gradle-plugin.id} quarkusPluginVersion={quarkus.gradle-plugin.version} quarkusPlatformGroupId={quarkus.platform.group-id} quarkusPlatformArtifactId={quarkus.platform.artifact-id} -quarkusPlatformVersion={quarkus.platform.version} \ No newline at end of file +quarkusPlatformVersion={quarkus.platform.version} diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/base/gradle.tpl.qute.properties b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/base/gradle.tpl.qute.properties index d3160667382c5..80d94672c2262 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/base/gradle.tpl.qute.properties +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/gradle/base/gradle.tpl.qute.properties @@ -1,6 +1,7 @@ -#Gradle properties +# Gradle properties + quarkusPluginId={quarkus.gradle-plugin.id} quarkusPluginVersion={quarkus.gradle-plugin.version} quarkusPlatformGroupId={quarkus.platform.group-id} quarkusPlatformArtifactId={quarkus.platform.artifact-id} -quarkusPlatformVersion={quarkus.platform.version} \ No newline at end of file +quarkusPlatformVersion={quarkus.platform.version} diff --git a/independent-projects/tools/devtools-common/pom.xml b/independent-projects/tools/devtools-common/pom.xml index 2e5832fddb6af..e7b70a29daea2 100644 --- a/independent-projects/tools/devtools-common/pom.xml +++ b/independent-projects/tools/devtools-common/pom.xml @@ -106,6 +106,10 @@ jakarta.enterprise jakarta.enterprise.cdi-api + + org.codejive + java-properties + org.junit.jupiter junit-jupiter diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java index dd5ea2931ce1e..d9ad8bdb7eb79 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFile.java @@ -1,18 +1,19 @@ package io.quarkus.devtools.project.buildfile; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.StringWriter; import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Properties; import java.util.Scanner; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.codejive.properties.Properties; + import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.registry.catalog.ExtensionCatalog; @@ -47,22 +48,22 @@ public void writeToDisk() throws IOException { if (rootProjectPath != null) { Files.write(rootProjectPath.resolve(getSettingsGradlePath()), getModel().getRootSettingsContent().getBytes()); if (hasRootProjectFile(GRADLE_PROPERTIES_PATH)) { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - getModel().getRootPropertiesContent().store(out, "Gradle properties"); - Files.write(rootProjectPath.resolve(GRADLE_PROPERTIES_PATH), - out.toByteArray()); + try (StringWriter sw = new StringWriter()) { + getModel().getRootPropertiesContent().store(sw, "Gradle properties"); + Files.writeString(rootProjectPath.resolve(GRADLE_PROPERTIES_PATH), + sw.toString()); } } } else { writeToProjectFile(getSettingsGradlePath(), getModel().getSettingsContent().getBytes()); if (hasProjectFile(GRADLE_PROPERTIES_PATH)) { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - getModel().getPropertiesContent().store(out, "Gradle properties"); - writeToProjectFile(GRADLE_PROPERTIES_PATH, out.toByteArray()); + try (StringWriter sw = new StringWriter()) { + getModel().getPropertiesContent().store(sw, "Gradle properties"); + writeToProjectFile(GRADLE_PROPERTIES_PATH, sw.toString()); } } } - writeToProjectFile(getBuildGradlePath(), getModel().getBuildContent().getBytes()); + writeToProjectFile(getBuildGradlePath(), getModel().getBuildContent()); } static boolean containsProperty(ArtifactCoords coords) { diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFilesCreator.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFilesCreator.java index 5d6ad5618536f..e3c8f24cf8069 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFilesCreator.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/AbstractGradleBuildFilesCreator.java @@ -8,9 +8,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; -import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; +import org.codejive.properties.Properties; + import io.quarkus.devtools.project.QuarkusProject; import io.quarkus.devtools.project.buildfile.AbstractGradleBuildFile.Model; import io.quarkus.maven.dependency.ArtifactCoords; @@ -122,6 +123,10 @@ protected void writeToProjectFile(final String fileName, final byte[] content) t Files.write(quarkusProject.getProjectDirPath().resolve(fileName), content); } + protected void writeToProjectFile(final String fileName, final String content) throws IOException { + Files.writeString(quarkusProject.getProjectDirPath().resolve(fileName), content); + } + private void createProperties() throws IOException { final ExtensionCatalog platform = quarkusProject.getExtensionsCatalog(); Properties props = getModel().getPropertiesContent(); diff --git a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java index a05d32b9e103a..ce9cb33e71f3f 100644 --- a/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java +++ b/independent-projects/tools/devtools-common/src/main/java/io/quarkus/devtools/project/buildfile/BuildFile.java @@ -155,6 +155,10 @@ protected void writeToProjectFile(final String fileName, final byte[] content) t Files.write(projectDirPath.resolve(fileName), content); } + protected void writeToProjectFile(final String fileName, final String content) throws IOException { + Files.writeString(projectDirPath.resolve(fileName), content); + } + private Set getDependenciesKeys() throws IOException { return getDependencies().stream().map(ArtifactCoords::getKey).collect(Collectors.toSet()); } diff --git a/independent-projects/tools/pom.xml b/independent-projects/tools/pom.xml index fdb667652e510..28abaed4649c6 100644 --- a/independent-projects/tools/pom.xml +++ b/independent-projects/tools/pom.xml @@ -60,6 +60,7 @@ 3.2.0 2.0.2 4.2.1 + 0.0.7 registry-client @@ -175,6 +176,11 @@ jboss-logging ${jboss-logging.version} + + org.codejive + java-properties + ${java-properties.version} + org.junit.jupiter junit-jupiter diff --git a/integration-tests/gradle/src/main/resources/compile-only-lombok/gradle.properties b/integration-tests/gradle/src/main/resources/compile-only-lombok/gradle.properties index cac35f5608ec1..542d9cf933db7 100644 --- a/integration-tests/gradle/src/main/resources/compile-only-lombok/gradle.properties +++ b/integration-tests/gradle/src/main/resources/compile-only-lombok/gradle.properties @@ -1,6 +1,6 @@ -#Gradle properties -#Tue Aug 04 17:04:55 CEST 2020 +# Gradle properties + quarkusPlatformArtifactId=quarkus-bom quarkusPlatformGroupId=io.quarkus org.gradle.logging.level=INFO -lombokVersion=1.18.12 \ No newline at end of file +lombokVersion=1.18.12 diff --git a/integration-tests/gradle/src/main/resources/multi-module-included-build/gradle.properties b/integration-tests/gradle/src/main/resources/multi-module-included-build/gradle.properties index a0bb7cd4da3b4..0b9b349582ac3 100644 --- a/integration-tests/gradle/src/main/resources/multi-module-included-build/gradle.properties +++ b/integration-tests/gradle/src/main/resources/multi-module-included-build/gradle.properties @@ -1,4 +1,4 @@ -#Gradle properties -#Tue Aug 25 06:41:36 GMT 2020 +# Gradle properties + quarkusPlatformArtifactId=quarkus-bom quarkusPlatformGroupId=io.quarkus diff --git a/integration-tests/gradle/src/main/resources/multi-module-kotlin-project/gradle.properties b/integration-tests/gradle/src/main/resources/multi-module-kotlin-project/gradle.properties index b8ff81712a497..d54b9c8a6b92b 100644 --- a/integration-tests/gradle/src/main/resources/multi-module-kotlin-project/gradle.properties +++ b/integration-tests/gradle/src/main/resources/multi-module-kotlin-project/gradle.properties @@ -1,5 +1,5 @@ -#Gradle properties -#Wed May 06 08:31:08 UTC 2020 +# Gradle properties + quarkusPlatformArtifactId=quarkus-bom quarkusPlatformGroupId=io.quarkus diff --git a/integration-tests/gradle/src/main/resources/multi-module-with-empty-module/gradle.properties b/integration-tests/gradle/src/main/resources/multi-module-with-empty-module/gradle.properties index e6b64c6a56957..0b9b349582ac3 100644 --- a/integration-tests/gradle/src/main/resources/multi-module-with-empty-module/gradle.properties +++ b/integration-tests/gradle/src/main/resources/multi-module-with-empty-module/gradle.properties @@ -1,3 +1,4 @@ -#Gradle properties +# Gradle properties + quarkusPlatformArtifactId=quarkus-bom -quarkusPlatformGroupId=io.quarkus \ No newline at end of file +quarkusPlatformGroupId=io.quarkus From 1ad48ceeae3f273d1533b68a1b5c3b9c2dcdb805 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Mon, 8 Jul 2024 21:18:56 -0300 Subject: [PATCH 56/94] Using safe Integer comparison --- .../quarkus/kubernetes/deployment/AddPortToKnativeConfig.java | 2 +- .../kubernetes/deployment/AddPortToKubernetesConfig.java | 2 +- .../quarkus/kubernetes/deployment/AddPortToOpenshiftConfig.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKnativeConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKnativeConfig.java index bb89510d27d3a..6a5cd38ce4d8e 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKnativeConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKnativeConfig.java @@ -29,7 +29,7 @@ public void visit(KnativeConfigFluent config) { */ private boolean hasPort(KnativeConfigFluent config) { for (Port p : config.buildPorts()) { - if (p.getContainerPort() == port.getContainerPort()) { + if (Objects.equals(p.getContainerPort(), port.getContainerPort())) { return true; } } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKubernetesConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKubernetesConfig.java index c2305c8227fc6..13ac4c240335c 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKubernetesConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToKubernetesConfig.java @@ -29,7 +29,7 @@ public void visit(KubernetesConfigFluent config) { */ private boolean hasPort(KubernetesConfigFluent config) { for (Port p : config.buildPorts()) { - if (p.getContainerPort() == port.getContainerPort()) { + if (Objects.equals(p.getContainerPort(), port.getContainerPort())) { return true; } } diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToOpenshiftConfig.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToOpenshiftConfig.java index 6235424c3a43c..e8077aed4ebd4 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToOpenshiftConfig.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/AddPortToOpenshiftConfig.java @@ -29,7 +29,7 @@ public void visit(OpenshiftConfigFluent config) { */ private boolean hasPort(OpenshiftConfigFluent config) { for (Port p : config.buildPorts()) { - if (p.getContainerPort() == port.getContainerPort()) { + if (Objects.equals(p.getContainerPort(), port.getContainerPort())) { return true; } } From b1be2fb298d5b9467b5b553f57fa92820e6ebb07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 00:29:17 +0000 Subject: [PATCH 57/94] Bump narayana.version from 7.0.1.Final to 7.0.2.Final Bumps `narayana.version` from 7.0.1.Final to 7.0.2.Final. Updates `org.jboss.narayana.jta:narayana-jta` from 7.0.1.Final to 7.0.2.Final Updates `org.jboss.narayana.jts:narayana-jts-integration` from 7.0.1.Final to 7.0.2.Final Updates `org.jboss.narayana.stm:stm` from 7.0.1.Final to 7.0.2.Final Updates `org.jboss.narayana.rts:lra-coordinator-jar` from 7.0.1.Final to 7.0.2.Final Updates `org.jboss.narayana.rts:narayana-lra` from 7.0.1.Final to 7.0.2.Final Updates `org.jboss.narayana.rts:lra-service-base` from 7.0.1.Final to 7.0.2.Final Updates `org.jboss.narayana.rts:lra-proxy-api` from 7.0.1.Final to 7.0.2.Final Updates `org.jboss.narayana.rts:lra-client` from 7.0.1.Final to 7.0.2.Final --- updated-dependencies: - dependency-name: org.jboss.narayana.jta:narayana-jta dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jboss.narayana.jts:narayana-jts-integration dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jboss.narayana.stm:stm dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jboss.narayana.rts:lra-coordinator-jar dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jboss.narayana.rts:narayana-lra dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jboss.narayana.rts:lra-service-base dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jboss.narayana.rts:lra-proxy-api dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jboss.narayana.rts:lra-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 6a4cbeaf71ea3..befa599efeb15 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -99,7 +99,7 @@ 1.7.0 - 7.0.1.Final + 7.0.2.Final 2.4 8.0.0.Final 8.14.2 From b97f08b1d8642f184095cb704bc08eb0d96a6c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 8 Jul 2024 16:27:03 +0200 Subject: [PATCH 58/94] Clarify that quarkus.hibernate-orm.dialect allows simpler names --- docs/src/main/asciidoc/_attributes.adoc | 1 + docs/src/main/asciidoc/hibernate-orm.adoc | 11 +++++++++-- .../HibernateOrmConfigPersistenceUnit.java | 12 ++++++++---- .../src/main/resources/application.properties | 4 ++-- .../src/main/resources/application.properties | 4 ++-- .../test/resources/property-warnings-test.properties | 2 +- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/docs/src/main/asciidoc/_attributes.adoc b/docs/src/main/asciidoc/_attributes.adoc index 4eed987d1446a..5729f47a657a9 100644 --- a/docs/src/main/asciidoc/_attributes.adoc +++ b/docs/src/main/asciidoc/_attributes.adoc @@ -52,6 +52,7 @@ :quickstarts-tree-url: ${quickstarts-base-url}/tree/main // . :hibernate-orm-docs-url: https://docs.jboss.org/hibernate/orm/{hibernate-orm-version-major-minor}/userguide/html_single/Hibernate_User_Guide.html +:hibernate-orm-dialect-docs-url: https://docs.jboss.org/hibernate/orm/{hibernate-orm-version-major-minor}/dialect/dialect.html :hibernate-search-docs-url: https://docs.jboss.org/hibernate/search/{hibernate-search-version-major-minor}/reference/en-US/html_single/ // . :amazon-services-guide: https://quarkiverse.github.io/quarkiverse-docs/quarkus-amazon-services/dev/index.html diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index f41dd334d1faa..5b2a0d63c3db8 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -227,9 +227,16 @@ quarkus.datasource.username = hibernate quarkus.datasource.password = hibernate quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:26257/hibernate_db -quarkus.hibernate-orm.dialect=org.hibernate.dialect.CockroachDialect <1> +quarkus.hibernate-orm.dialect=Cockroach <1> ---- <1> Set the Hibernate ORM dialect. ++ +For built-in dialects, the expected value is one of the names +in the link:{hibernate-orm-dialect-docs-url}[official list of dialects], *without* the `Dialect` suffix, +for example `Cockroach` for `CockroachDialect`. ++ +For third-party dialects, the expected value is the fully-qualified class name, +for example `com.acme.hibernate.AcmeDbDialect`. [WARNING] ==== @@ -249,7 +256,7 @@ quarkus.datasource.username = hibernate quarkus.datasource.password = hibernate quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:26257/hibernate_db -quarkus.hibernate-orm.dialect=org.hibernate.dialect.CockroachDialect <2> +quarkus.hibernate-orm.dialect=Cockroach <2> ---- <1> Set the database version. The Hibernate ORM dialect will target that version. Since we're targeting CockroachDB here, we're passing the CockroachDB version, not the PostgreSQL version. diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java index 94fe4d3b64c94..e6c30584166c6 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java @@ -276,11 +276,15 @@ default boolean isAnyPropertySet() { interface HibernateOrmConfigPersistenceUnitDialect { /** - * Class name of the Hibernate ORM dialect. + * Name of the Hibernate ORM dialect. * - * The complete list of bundled dialects is available in the - * https://docs.jboss.org/hibernate/stable/orm/javadocs/org/hibernate/dialect/package-summary.html[Hibernate ORM - * JavaDoc]. + * For built-in dialects, the expected value is one of the names + * in the link:{hibernate-orm-dialect-docs-url}[official list of dialects], + * *without* the `Dialect` suffix, + * for example `Cockroach` for `CockroachDialect`. + * + * For third-party dialects, the expected value is the fully-qualified class name, + * for example `com.acme.hibernate.AcmeDbDialect`. * * Setting the dialect directly is only recommended as a last resort: * most popular databases have a corresponding Quarkus extension, diff --git a/integration-tests/hibernate-orm-tenancy/connection-resolver-legacy-qualifiers/src/main/resources/application.properties b/integration-tests/hibernate-orm-tenancy/connection-resolver-legacy-qualifiers/src/main/resources/application.properties index 86da8eec68667..b943eb6cc10f0 100644 --- a/integration-tests/hibernate-orm-tenancy/connection-resolver-legacy-qualifiers/src/main/resources/application.properties +++ b/integration-tests/hibernate-orm-tenancy/connection-resolver-legacy-qualifiers/src/main/resources/application.properties @@ -3,7 +3,7 @@ quarkus.hibernate-orm.database.generation=none quarkus.hibernate-orm.multitenant=database # Necessary because we're creating datasources dynamically, # which means the extension can't rely on a static datasource to guess the dialect -quarkus.hibernate-orm.dialect=org.hibernate.dialect.MariaDBDialect +quarkus.hibernate-orm.dialect=MariaDB quarkus.hibernate-orm.packages=io.quarkus.it.hibernate.multitenancy.fruit # We create datasources manually, so a lack of configuration doesn't mean Quarkus should step in with defaults. @@ -12,7 +12,7 @@ quarkus.datasource.devservices.enabled=false # Inventory persistence unit quarkus.hibernate-orm."inventory".database.generation=none quarkus.hibernate-orm."inventory".multitenant=database -quarkus.hibernate-orm."inventory".dialect=org.hibernate.dialect.MariaDBDialect +quarkus.hibernate-orm."inventory".dialect=MariaDB quarkus.hibernate-orm."inventory".packages=io.quarkus.it.hibernate.multitenancy.inventory #mariadb.base_url is set through Maven config diff --git a/integration-tests/hibernate-orm-tenancy/connection-resolver/src/main/resources/application.properties b/integration-tests/hibernate-orm-tenancy/connection-resolver/src/main/resources/application.properties index 86da8eec68667..b943eb6cc10f0 100644 --- a/integration-tests/hibernate-orm-tenancy/connection-resolver/src/main/resources/application.properties +++ b/integration-tests/hibernate-orm-tenancy/connection-resolver/src/main/resources/application.properties @@ -3,7 +3,7 @@ quarkus.hibernate-orm.database.generation=none quarkus.hibernate-orm.multitenant=database # Necessary because we're creating datasources dynamically, # which means the extension can't rely on a static datasource to guess the dialect -quarkus.hibernate-orm.dialect=org.hibernate.dialect.MariaDBDialect +quarkus.hibernate-orm.dialect=MariaDB quarkus.hibernate-orm.packages=io.quarkus.it.hibernate.multitenancy.fruit # We create datasources manually, so a lack of configuration doesn't mean Quarkus should step in with defaults. @@ -12,7 +12,7 @@ quarkus.datasource.devservices.enabled=false # Inventory persistence unit quarkus.hibernate-orm."inventory".database.generation=none quarkus.hibernate-orm."inventory".multitenant=database -quarkus.hibernate-orm."inventory".dialect=org.hibernate.dialect.MariaDBDialect +quarkus.hibernate-orm."inventory".dialect=MariaDB quarkus.hibernate-orm."inventory".packages=io.quarkus.it.hibernate.multitenancy.inventory #mariadb.base_url is set through Maven config diff --git a/integration-tests/spring-data-jpa/src/test/resources/property-warnings-test.properties b/integration-tests/spring-data-jpa/src/test/resources/property-warnings-test.properties index e73c7a6fe1469..318540bf44426 100644 --- a/integration-tests/spring-data-jpa/src/test/resources/property-warnings-test.properties +++ b/integration-tests/spring-data-jpa/src/test/resources/property-warnings-test.properties @@ -1,5 +1,5 @@ spring.jpa.show-sql=true -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect +spring.jpa.properties.hibernate.dialect=MySQL5 spring.jpa.open-in-view=false spring.jpa.hibernate.naming.physical-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy spring.jpa.hibernate.naming.implicit-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy From 37b1fb98322230a9165105b146c978e36afa93bd Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 9 Jul 2024 13:19:04 +0200 Subject: [PATCH 59/94] WebSockets Next: Dev UI fixes - a dev mode build should not fail if no server endpoint is defined - fix Server Endpoints static label --- .../devui/WebSocketServerDevUIProcessor.java | 2 +- .../devmode/NoServerEndpointDevModeTest.java | 46 +++++++++++++++++++ .../devui/WebSocketNextJsonRPCService.java | 31 +++++++++---- 3 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/devmode/NoServerEndpointDevModeTest.java diff --git a/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/devui/WebSocketServerDevUIProcessor.java b/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/devui/WebSocketServerDevUIProcessor.java index 5f89d5cfd0482..2785efe1fbdd4 100644 --- a/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/devui/WebSocketServerDevUIProcessor.java +++ b/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/devui/WebSocketServerDevUIProcessor.java @@ -40,7 +40,7 @@ public void pages(List endpoints, List root + .addClass(MyBean.class)); + + @Test + public void testConnectorIsInjected() { + assertEquals("1", RestAssured.get("mybeantest").then().statusCode(200).extract().body().asString()); + } + + @Singleton + public static class MyBean { + + @Inject + BasicWebSocketConnector connector; + + void addRoute(@Observes Router router) { + router.get("/mybeantest").handler(rc -> { + rc.end(connector != null ? "1" : "0"); + }); + } + + } + +} diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/devui/WebSocketNextJsonRPCService.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/devui/WebSocketNextJsonRPCService.java index 5878b1e6950fd..64916971e4c3e 100644 --- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/devui/WebSocketNextJsonRPCService.java +++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/devui/WebSocketNextJsonRPCService.java @@ -12,6 +12,7 @@ import java.util.concurrent.ConcurrentMap; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; import org.jboss.logging.Logger; @@ -52,16 +53,18 @@ public class WebSocketNextJsonRPCService implements ConnectionListener { private final WebSocketsServerRuntimeConfig.DevMode devModeConfig; - WebSocketNextJsonRPCService(ConnectionManager connectionManager, Vertx vertx, HttpConfiguration httpConfig, + WebSocketNextJsonRPCService(Instance connectionManager, Vertx vertx, HttpConfiguration httpConfig, WebSocketsServerRuntimeConfig config) { this.connectionStatus = BroadcastProcessor.create(); this.connectionMessages = BroadcastProcessor.create(); - this.connectionManager = connectionManager; + this.connectionManager = connectionManager.isResolvable() ? connectionManager.get() : null; this.vertx = vertx; this.httpConfig = httpConfig; this.devModeConfig = config.devMode(); this.sockets = new ConcurrentHashMap<>(); - connectionManager.addListener(this); + if (this.connectionManager != null) { + this.connectionManager.addListener(this); + } } public Multi connectionStatus() { @@ -74,14 +77,16 @@ public Multi connectionMessages() { public JsonObject getConnections(List endpoints) { JsonObject json = new JsonObject(); - for (String endpoint : endpoints) { - List connections = new ArrayList<>(connectionManager.getConnections(endpoint)); - connections.sort(Comparator.comparing(WebSocketConnection::creationTime)); - JsonArray array = new JsonArray(); - for (WebSocketConnection c : connections) { - array.add(toJsonObject(endpoint, c)); + if (connectionManager != null) { + for (String endpoint : endpoints) { + List connections = new ArrayList<>(connectionManager.getConnections(endpoint)); + connections.sort(Comparator.comparing(WebSocketConnection::creationTime)); + JsonArray array = new JsonArray(); + for (WebSocketConnection c : connections) { + array.add(toJsonObject(endpoint, c)); + } + json.put(endpoint, array); } - json.put(endpoint, array); } json.put("connectionMessagesLimit", devModeConfig.connectionMessagesLimit()); return json; @@ -103,6 +108,9 @@ public JsonArray getMessages(String connectionKey) { } public Uni openDevConnection(String path, String endpointPath) { + if (connectionManager == null) { + return failureUni(); + } if (isInvalidPath(path, endpointPath)) { LOG.errorf("Invalid path %s; original endpoint path %s", path, endpointPath); return failureUni(); @@ -179,6 +187,9 @@ private static String normalize(String path) { } public Uni closeDevConnection(String connectionKey) { + if (connectionManager == null) { + return failureUni(); + } DevWebSocket socket = sockets.remove(connectionKey); if (socket != null) { Uni uni = Uni.createFrom().completionStage(() -> socket.socket.close().toCompletionStage()); From 5e4e3ba225a1583f9e1f2b7305b216632531aae9 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 9 Jul 2024 13:20:40 +0200 Subject: [PATCH 60/94] WebSockets Next - client: use 443 if port is undefined and https is set --- .../runtime/BasicWebSocketConnectorImpl.java | 5 +---- .../next/runtime/WebSocketConnectorBase.java | 18 ++++++++++++++++++ .../next/runtime/WebSocketConnectorImpl.java | 5 +---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/BasicWebSocketConnectorImpl.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/BasicWebSocketConnectorImpl.java index 620369dddf0f4..6442502058725 100644 --- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/BasicWebSocketConnectorImpl.java +++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/BasicWebSocketConnectorImpl.java @@ -116,10 +116,7 @@ public Uni connect() { // TODO would it make sense to share clients? WebSocketClient client = vertx.createWebSocketClient(populateClientOptions()); - WebSocketConnectOptions connectOptions = new WebSocketConnectOptions() - .setSsl(baseUri.getScheme().equals("https")) - .setHost(baseUri.getHost()) - .setPort(baseUri.getPort()); + WebSocketConnectOptions connectOptions = newConnectOptions(baseUri); StringBuilder requestUri = new StringBuilder(); String mergedPath = mergePath(baseUri.getPath(), replacePathParameters(path)); requestUri.append(mergedPath); diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorBase.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorBase.java index ee098d6a43d16..1556899636c9f 100644 --- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorBase.java +++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorBase.java @@ -21,6 +21,7 @@ import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.WebSocketClientOptions; +import io.vertx.core.http.WebSocketConnectOptions; import io.vertx.core.net.SSLOptions; abstract class WebSocketConnectorBase> { @@ -191,4 +192,21 @@ protected WebSocketClientOptions populateClientOptions() { } return clientOptions; } + + protected WebSocketConnectOptions newConnectOptions(URI serverEndpointUri) { + WebSocketConnectOptions connectOptions = new WebSocketConnectOptions() + .setSsl(isHttps(serverEndpointUri)) + .setHost(serverEndpointUri.getHost()); + if (serverEndpointUri.getPort() != -1) { + connectOptions.setPort(serverEndpointUri.getPort()); + } else if (isHttps(serverEndpointUri)) { + // If port is undefined and https is used then use 443 by default + connectOptions.setPort(443); + } + return connectOptions; + } + + protected boolean isHttps(URI uri) { + return "https".equals(uri.getScheme()); + } } diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorImpl.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorImpl.java index cb04b5cbb61ad..686f132c71038 100644 --- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorImpl.java +++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectorImpl.java @@ -72,10 +72,7 @@ public Uni connect() { throw new WebSocketClientException(e); } - WebSocketConnectOptions connectOptions = new WebSocketConnectOptions() - .setSsl(serverEndpointUri.getScheme().equals("https")) - .setHost(serverEndpointUri.getHost()) - .setPort(serverEndpointUri.getPort()); + WebSocketConnectOptions connectOptions = newConnectOptions(serverEndpointUri); StringBuilder uri = new StringBuilder(); if (serverEndpointUri.getPath() != null) { uri.append(serverEndpointUri.getRawPath()); From 485f45d9ce7b2a687ae5b81f8a58b158e7b3bafb Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sat, 6 Jul 2024 16:01:41 +0200 Subject: [PATCH 61/94] Avoid making IsolatedDevModeMain and allegates static for no good reason Also avoid a reference to IsolatedDevModeMain from ApplicationLifecycleManager. --- .../quarkus/deployment/dev/DevModeMain.java | 2 +- .../deployment/dev/IsolatedDevModeMain.java | 97 +++++++++++-------- .../dev/IsolatedRemoteDevModeMain.java | 13 ++- .../deployment/dev/IsolatedTestModeMain.java | 3 +- .../dev/RuntimeUpdatesProcessor.java | 9 +- .../runtime/ApplicationLifecycleManager.java | 2 + 6 files changed, 76 insertions(+), 50 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/DevModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/DevModeMain.java index 02bfdcd3405e5..6377b9c3b908a 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/DevModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/DevModeMain.java @@ -40,7 +40,7 @@ public class DevModeMain implements Closeable { private final DevModeContext context; - private static volatile CuratedApplication curatedApplication; + private volatile CuratedApplication curatedApplication; private Closeable realCloseable; public DevModeMain(DevModeContext context) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java index b68f3b9bbc676..7fb810f807708 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedDevModeMain.java @@ -15,6 +15,7 @@ import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -61,16 +62,16 @@ public class IsolatedDevModeMain implements BiConsumer hotReplacementSetups = new ArrayList<>(); - private static volatile RunningQuarkusApplication runner; - static volatile Throwable deploymentProblem; - private static volatile CuratedApplication curatedApplication; - private static volatile AugmentAction augmentAction; - private static volatile boolean restarting; - private static volatile boolean firstStartCompleted; - private static final CountDownLatch shutdownLatch = new CountDownLatch(1); + private volatile RunningQuarkusApplication runner; + final AtomicReference deploymentProblem = new AtomicReference<>(); + private volatile CuratedApplication curatedApplication; + private volatile AugmentAction augmentAction; + private volatile boolean restarting; + private volatile boolean firstStartCompleted; + private final CountDownLatch shutdownLatch = new CountDownLatch(1); private Thread shutdownThread; private CodeGenWatcher codeGenWatcher; - private static volatile ConsoleStateManager.ConsoleContext consoleContext; + private volatile ConsoleStateManager.ConsoleContext consoleContext; private final List listeners = new ArrayList<>(); private synchronized void firstStart() { @@ -85,30 +86,7 @@ private synchronized void firstStart() { //TODO: look at implementing a common core classloader, that removes the need for this sort of crappy hack curatedApplication.getOrCreateBaseRuntimeClassLoader().loadClass(ApplicationLifecycleManager.class.getName()) .getMethod("setDefaultExitCodeHandler", Consumer.class) - .invoke(null, new Consumer() { - @Override - public void accept(Integer integer) { - if (restarting || ApplicationLifecycleManager.isVmShuttingDown() - || context.isAbortOnFailedStart() || - context.isTest()) { - return; - } - if (consoleContext == null) { - consoleContext = ConsoleStateManager.INSTANCE - .createContext("Completed Application"); - } - //this sucks, but when we get here logging is gone - //so we just setup basic console logging - InitialConfigurator.DELAYED_HANDLER.addHandler(new ConsoleHandler( - ConsoleHandler.Target.SYSTEM_OUT, - new ColorPatternFormatter("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{3.}] (%t) %s%e%n"))); - consoleContext.reset(new ConsoleCommand(' ', "Restarts the application", "to restart", 0, null, - () -> { - consoleContext.reset(); - RuntimeUpdatesProcessor.INSTANCE.doScan(true, true); - })); - } - }); + .invoke(null, getExitCodeHandler()); StartupAction start = augmentAction.createInitialRuntimeApplication(); @@ -127,7 +105,7 @@ public void accept(Integer integer) { rootCause = rootCause.getCause(); } if (!(rootCause instanceof BindException)) { - deploymentProblem = t; + deploymentProblem.set(t); if (!context.isAbortOnFailedStart()) { //we need to set this here, while we still have the correct TCCL //this is so the config is still valid, and we can read HTTP config from application.properties @@ -174,6 +152,35 @@ public void accept(Integer integer) { } } + private Consumer getExitCodeHandler() { + if (context.isTest() || context.isAbortOnFailedStart()) { + return TestExitCodeHandler.INSTANCE; + } + + return new Consumer() { + @Override + public void accept(Integer integer) { + if (restarting || ApplicationLifecycleManager.isVmShuttingDown()) { + return; + } + if (consoleContext == null) { + consoleContext = ConsoleStateManager.INSTANCE + .createContext("Completed Application"); + } + //this sucks, but when we get here logging is gone + //so we just setup basic console logging + InitialConfigurator.DELAYED_HANDLER.addHandler(new ConsoleHandler( + ConsoleHandler.Target.SYSTEM_OUT, + new ColorPatternFormatter("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{3.}] (%t) %s%e%n"))); + consoleContext.reset(new ConsoleCommand(' ', "Restarts the application", "to restart", 0, null, + () -> { + consoleContext.reset(); + RuntimeUpdatesProcessor.INSTANCE.doScan(true, true); + })); + } + }; + } + public void restartCallback(Set changedResources, ClassScanResult result) { restartApp(changedResources, new ClassChangeInformation(result.changedClassNames, result.deletedClassNames, result.addedClassNames)); @@ -186,7 +193,7 @@ public synchronized void restartApp(Set changedResources, ClassChangeInf } stop(); Timing.restart(curatedApplication.getOrCreateAugmentClassLoader()); - deploymentProblem = null; + deploymentProblem.set(null); ClassLoader old = Thread.currentThread().getContextClassLoader(); try { @@ -200,7 +207,7 @@ public synchronized void restartApp(Set changedResources, ClassChangeInf firstStartCompleted = true; } } catch (Throwable t) { - deploymentProblem = t; + deploymentProblem.set(t); Throwable rootCause = t; while (rootCause.getCause() != null) { rootCause = rootCause.getCause(); @@ -253,7 +260,7 @@ private RuntimeUpdatesProcessor setupRuntimeCompilation(DevModeContext context, public byte[] apply(String s, byte[] bytes) { return ClassTransformingBuildStep.transform(s, bytes); } - }, testSupport); + }, testSupport, deploymentProblem); for (HotReplacementSetup service : ServiceLoader.load(HotReplacementSetup.class, curatedApplication.getOrCreateBaseRuntimeClassLoader())) { @@ -350,6 +357,7 @@ public void close() { curatedApplication.close(); curatedApplication = null; augmentAction = null; + deploymentProblem.set(null); } finally { if (shutdownThread != null) { try { @@ -422,10 +430,11 @@ public void run() { firstStart(); // doStart(false, Collections.emptySet()); - if (deploymentProblem != null || RuntimeUpdatesProcessor.INSTANCE.getCompileProblem() != null) { + if (deploymentProblem.get() != null || RuntimeUpdatesProcessor.INSTANCE.getCompileProblem() != null) { if (context.isAbortOnFailedStart()) { - Throwable throwable = deploymentProblem == null ? RuntimeUpdatesProcessor.INSTANCE.getCompileProblem() - : deploymentProblem; + Throwable throwable = deploymentProblem.get() == null + ? RuntimeUpdatesProcessor.INSTANCE.getCompileProblem() + : deploymentProblem.get(); throw (throwable instanceof RuntimeException ? (RuntimeException) throwable : new RuntimeException(throwable)); @@ -483,4 +492,14 @@ public boolean test(String s) { }).produces(ApplicationClassPredicateBuildItem.class).build(); } } + + private static class TestExitCodeHandler implements Consumer { + + private static final TestExitCodeHandler INSTANCE = new TestExitCodeHandler(); + + @Override + public void accept(Integer exitCode) { + // do nothing + } + } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedRemoteDevModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedRemoteDevModeMain.java index a1e691f0748ed..17d4b5e547fb8 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedRemoteDevModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedRemoteDevModeMain.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; @@ -57,7 +58,7 @@ public class IsolatedRemoteDevModeMain implements BiConsumer hotReplacementSetups = new ArrayList<>(); - static volatile Throwable deploymentProblem; + private AtomicReference deploymentProblem = new AtomicReference<>(); static volatile RemoteDevClient remoteDevClient; static volatile Closeable remoteDevClientSession; private static volatile CuratedApplication curatedApplication; @@ -99,7 +100,7 @@ private synchronized JarResult generateApplication() { curatedApplication.getApplicationModel(), null); return start.getJar(); } catch (Throwable t) { - deploymentProblem = t; + deploymentProblem.set(t); log.error("Failed to generate Quarkus application", t); return null; } @@ -137,7 +138,7 @@ public void accept(DevModeContext.ModuleInfo moduleInfo, String s) { public byte[] apply(String s, byte[] bytes) { return ClassTransformingBuildStep.transform(s, bytes); } - }, null); + }, null, deploymentProblem); for (HotReplacementSetup service : ServiceLoader.load(HotReplacementSetup.class, curatedApplication.getOrCreateBaseRuntimeClassLoader())) { @@ -189,6 +190,7 @@ public void close() { } } } finally { + deploymentProblem.set(null); curatedApplication.close(); } @@ -248,7 +250,7 @@ public void run() { } private Closeable doConnect() { - return remoteDevClient.sendConnectRequest(new RemoteDevState(currentHashes, deploymentProblem), + return remoteDevClient.sendConnectRequest(new RemoteDevState(currentHashes, deploymentProblem.get()), new Function, Map>() { @Override public Map apply(Set fileNames) { @@ -283,6 +285,7 @@ private RemoteDevClient.SyncResult runSync() { Set removed = new HashSet<>(); Map changed = new HashMap<>(); try { + deploymentProblem.set(null); boolean scanResult = RuntimeUpdatesProcessor.INSTANCE.doScan(true); if (!scanResult && !copiedStaticResources.isEmpty()) { scanResult = true; @@ -305,7 +308,7 @@ private RemoteDevClient.SyncResult runSync() { currentHashes = newHashes; } } catch (IOException e) { - deploymentProblem = e; + deploymentProblem.set(e); } return new RemoteDevClient.SyncResult() { @Override diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedTestModeMain.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedTestModeMain.java index 1f5b37aa311f0..9fa94f1ff6aec 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedTestModeMain.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/IsolatedTestModeMain.java @@ -36,7 +36,6 @@ public class IsolatedTestModeMain extends IsolatedDevModeMain { private volatile DevModeContext context; private final List hotReplacementSetups = new ArrayList<>(); - static volatile Throwable deploymentProblem; private static volatile CuratedApplication curatedApplication; private static volatile AugmentAction augmentAction; @@ -68,7 +67,7 @@ public void accept(DevModeContext.ModuleInfo moduleInfo, String s) { public byte[] apply(String s, byte[] bytes) { return ClassTransformingBuildStep.transform(s, bytes); } - }, testSupport); + }, testSupport, deploymentProblem); for (HotReplacementSetup service : ServiceLoader.load(HotReplacementSetup.class, curatedApplication.getOrCreateBaseRuntimeClassLoader())) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java index f32d052a25aec..ca8f49634d88a 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java @@ -40,6 +40,7 @@ import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; @@ -91,6 +92,7 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext, Closeable volatile Throwable compileProblem; volatile Throwable testCompileProblem; volatile Throwable hotReloadProblem; + private final AtomicReference deploymentProblem; private volatile Predicate disableInstrumentationForClassPredicate = new AlwaysFalsePredicate<>(); private volatile Predicate disableInstrumentationForIndexPredicate = new AlwaysFalsePredicate<>(); @@ -141,7 +143,7 @@ public RuntimeUpdatesProcessor(Path applicationRoot, DevModeContext context, Qua DevModeType devModeType, BiConsumer, ClassScanResult> restartCallback, BiConsumer copyResourceNotification, BiFunction classTransformers, - TestSupport testSupport) { + TestSupport testSupport, AtomicReference deploymentProblem) { this.applicationRoot = applicationRoot; this.context = context; this.compiler = compiler; @@ -180,6 +182,7 @@ public void testsDisabled() { } }); } + this.deploymentProblem = deploymentProblem; } public TestSupport getTestSupport() { @@ -392,7 +395,7 @@ public List getResourcesDir() { public Throwable getDeploymentProblem() { //we differentiate between these internally, however for the error reporting they are the same return compileProblem != null ? compileProblem - : IsolatedDevModeMain.deploymentProblem != null ? IsolatedDevModeMain.deploymentProblem + : deploymentProblem.get() != null ? deploymentProblem.get() : hotReloadProblem; } @@ -535,7 +538,7 @@ public boolean doScan(boolean userInitiated, boolean forceRestart) { //all broken we just assume the reason that they have refreshed is because they have fixed something //trying to watch all resource files is complex and this is likely a good enough solution for what is already an edge case boolean restartNeeded = !instrumentationChange && (changedClassResults.isChanged() - || (IsolatedDevModeMain.deploymentProblem != null && userInitiated) || fileRestartNeeded); + || (deploymentProblem.get() != null && userInitiated) || fileRestartNeeded); if (restartNeeded) { String changeString = changedFilesForRestart.stream().map(Path::getFileName).map(Object::toString) .collect(Collectors.joining(", ")); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java b/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java index fa2d8b32c4255..48c18f868b7b2 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java @@ -225,6 +225,7 @@ public static void run(Application application, Class Date: Mon, 8 Jul 2024 17:11:08 +0200 Subject: [PATCH 62/94] Clean up configuration examples in Hibernate ORM extension guide 1. Use callouts, not comments. 2. Use links to other guides. 3. Explain the "why" where necessary. 4. Fix weird mix of default and `base` datasource in database multitenancy example. --- docs/src/main/asciidoc/hibernate-orm.adoc | 85 ++++++++++++----------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index 5b2a0d63c3db8..0b90403a4d70a 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -84,15 +84,15 @@ then add the relevant configuration properties in `{config-file}`. [source,properties] .Example `{config-file}` ---- -# datasource configuration -quarkus.datasource.db-kind = postgresql +quarkus.datasource.db-kind = postgresql <1> quarkus.datasource.username = hibernate quarkus.datasource.password = hibernate quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/hibernate_db -# drop and create the database at startup (use `update` to only update the schema) -quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.hibernate-orm.database.generation=drop-and-create <2> ---- +<1> xref:datasource.adoc[Configure the datasource]. +<2> Drop and create the database at startup (use `update` to only update the schema). Note that these configuration properties are not the same ones as in your typical Hibernate ORM configuration file. They will often map to Hibernate ORM configuration properties but could have different names and don't necessarily map 1:1 to each other. @@ -1127,32 +1127,37 @@ In general, it is not possible to use the Hibernate ORM database generation feat Therefore, you have to disable it, and you need to make sure that the tables are created per schema. The following setup will use the xref:flyway.adoc[Flyway] extension to achieve this goal. +[[schema-approach]] ==== SCHEMA approach The same data source will be used for all tenants and a schema has to be created for every tenant inside that data source. -CAUTION: Some databases like MariaDB/MySQL do not support database schemas. In these cases you have to use the DATABASE approach below. + +CAUTION: Some databases like MariaDB/MySQL do not support database schemas. In these cases you have to use the <>. [source,properties] ---- -# Disable generation -quarkus.hibernate-orm.database.generation=none +quarkus.hibernate-orm.database.generation=none <1> -# Enable SCHEMA approach and use default datasource -quarkus.hibernate-orm.multitenant=SCHEMA -# You could use a non-default datasource by using the following setting -# quarkus.hibernate-orm.datasource=other +quarkus.hibernate-orm.multitenant=SCHEMA <2> -# The default data source used for all tenant schemas -quarkus.datasource.db-kind=postgresql +quarkus.datasource.db-kind=postgresql <3> quarkus.datasource.username=quarkus_test quarkus.datasource.password=quarkus_test quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test -# Enable Flyway configuration to create schemas -quarkus.flyway.schemas=base,mycompany +quarkus.flyway.schemas=base,mycompany <4> quarkus.flyway.locations=classpath:schema quarkus.flyway.migrate-at-start=true ---- +<1> Disable schema generation, because it is not supported by Hibernate ORM for schema multi-tenancy. +We'll use Flyway instead, see further down. +<2> Enable schema multi-tenancy. ++ +We use the default datasource here, but could use a named datasource if we wanted to, +by following instructions <>. +<3> xref:datasource.adoc[Configure the datasource]. +<4> Configure xref:flyway.adoc[Flyway] for database initialization, +because schema generation by Hibernate ORM is not supported in this case. Here is an example of the Flyway SQL (`V1.0.0__create_fruits.sql`) to be created in the configured folder `src/main/resources/schema`. @@ -1181,44 +1186,48 @@ INSERT INTO mycompany.known_fruits(id, name) VALUES (2, 'Apricots'); INSERT INTO mycompany.known_fruits(id, name) VALUES (3, 'Blackberries'); ---- - - +[[database-approach]] ==== DATABASE approach For every tenant you need to create a named data source with the same identifier that is returned by the `TenantResolver`. [source,properties] ---- -# Disable generation -quarkus.hibernate-orm.database.generation=none +quarkus.hibernate-orm.database.generation=none <1> -# Enable DATABASE approach -quarkus.hibernate-orm.multitenant=DATABASE +quarkus.hibernate-orm.multitenant=DATABASE <2> # Default tenant 'base' -quarkus.datasource.base.db-kind=postgresql +quarkus.datasource.base.db-kind=postgresql <3> quarkus.datasource.base.username=quarkus_test quarkus.datasource.base.password=quarkus_test quarkus.datasource.base.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test +quarkus.flyway.base.locations=classpath:database/base <4> +quarkus.flyway.base.migrate-at-start=true # Tenant 'mycompany' -quarkus.datasource.mycompany.db-kind=postgresql +quarkus.datasource.mycompany.db-kind=postgresql <5> quarkus.datasource.mycompany.username=mycompany quarkus.datasource.mycompany.password=mycompany quarkus.datasource.mycompany.jdbc.url=jdbc:postgresql://localhost:5433/mycompany - -# Flyway configuration for the default datasource -quarkus.flyway.locations=classpath:database/default -quarkus.flyway.migrate-at-start=true - -# Flyway configuration for the mycompany datasource -quarkus.flyway.mycompany.locations=classpath:database/mycompany +quarkus.flyway.mycompany.locations=classpath:database/mycompany <6> quarkus.flyway.mycompany.migrate-at-start=true ---- +<1> Disable schema generation, because it is not supported by Hibernate ORM for database multi-tenancy. +We'll use Flyway instead, see further down. +<2> Enable database multi-tenancy. +<3> xref:datasource.adoc[Configure the datasource] for one tenant, `base`. +<4> Configure xref:flyway.adoc[Flyway] for database initialization for tenant `base`, +because schema generation by Hibernate ORM is not supported in this case. +<5> xref:datasource.adoc[Configure the datasource] for another tenant. ++ +There could be more tenants, but here we're stopping at two. +<6> Configure xref:flyway.adoc[Flyway] for database initialization for tenant `mycompany`, +because schema generation by Hibernate ORM is not supported in this case. Following are examples of the Flyway SQL files to be created in the configured folder `src/main/resources/database`. -Default schema (`src/main/resources/database/default/V1.0.0__create_fruits.sql`): +Schema for tenant `base` (`src/main/resources/database/base/V1.0.0__create_fruits.sql`): [source,sql] ---- @@ -1234,7 +1243,7 @@ INSERT INTO known_fruits(id, name) VALUES (2, 'Apple'); INSERT INTO known_fruits(id, name) VALUES (3, 'Banana'); ---- -Mycompany schema (`src/main/resources/database/mycompany/V1.0.0__create_fruits.sql`): +Schema for tenant `mycompany` (`src/main/resources/database/mycompany/V1.0.0__create_fruits.sql`): [source,sql] ---- @@ -1250,8 +1259,7 @@ INSERT INTO known_fruits(id, name) VALUES (2, 'Apricots'); INSERT INTO known_fruits(id, name) VALUES (3, 'Blackberries'); ---- - - +[[discriminator-approach]] ==== DISCRIMINATOR approach The default data source will be used for all tenants. All entities defining a field annotated with `@TenantId` will have that field populated automatically, and will get filtered automatically in queries. @@ -1259,16 +1267,15 @@ The default data source will be used for all tenants. All entities defining a fi [source,properties] ---- -# Enable DISCRIMINATOR approach -quarkus.hibernate-orm.multitenant=DISCRIMINATOR +quarkus.hibernate-orm.multitenant=DISCRIMINATOR <1> -# The default data source used for all tenant schemas -quarkus.datasource.db-kind=postgresql +quarkus.datasource.db-kind=postgresql <2> quarkus.datasource.username=quarkus_test quarkus.datasource.password=quarkus_test quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test ---- - +<1> Enable discriminator multi-tenancy. +<2> xref:datasource.adoc[Configure the datasource]. === Programmatically Resolving Tenants Connections From 81ecc46938397a63625d3b727675de8317b10846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 8 Jul 2024 17:37:09 +0200 Subject: [PATCH 63/94] Clarify documentation of Hibernate ORM dialects wrt database multi-tenancy --- docs/src/main/asciidoc/hibernate-orm.adoc | 47 +++++++++++++++---- .../HibernateOrmConfigPersistenceUnit.java | 26 +++++----- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index 0b90403a4d70a..a323321d9dc2e 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -262,6 +262,23 @@ quarkus.hibernate-orm.dialect=Cockroach <2> Since we're targeting CockroachDB here, we're passing the CockroachDB version, not the PostgreSQL version. <2> Set the Hibernate ORM dialect. +[[hibernate-dialect-varying-database]] +== Varying database + +When enabling <>, +Hibernate ORM will use multiple datasources at runtime for the same persistence unit, +and by default Quarkus cannot tell which datasource is going to be used, +so it will not be able to detect a dialect to use in Hibernate ORM. + +For that reason, when enabling <>, +it is recommended to explicitly point the Hibernate ORM configuration to one datasource +among those that will be used at runtime, e.g. with `quarkus.hibernate-orm.datasource=base` +(`base` being the name of a datasource). + +When doing so, Quarkus will infer the database version and (if possible) dialect from that datasource. +For unsupported databases, you may still need to set the Hibernate ORM dialect explicitly, +as explained in <>. + [[hibernate-configuration-properties]] === Hibernate ORM configuration properties @@ -1191,38 +1208,52 @@ INSERT INTO mycompany.known_fruits(id, name) VALUES (3, 'Blackberries'); For every tenant you need to create a named data source with the same identifier that is returned by the `TenantResolver`. +[CAUTION] +==== +// Related to https://github.com/quarkusio/quarkus/issues/11861 +With this approach, all datasources used by the same persistence unit +are assumed to point to a database of the same vendor (same `db-kind`) and version. + +Mismatches will not be detected, and may result in unpredictable behavior. +==== + [source,properties] ---- quarkus.hibernate-orm.database.generation=none <1> quarkus.hibernate-orm.multitenant=DATABASE <2> +quarkus.hibernate-orm.datasource=base <3> # Default tenant 'base' -quarkus.datasource.base.db-kind=postgresql <3> +quarkus.datasource.base.db-kind=postgresql <4> quarkus.datasource.base.username=quarkus_test quarkus.datasource.base.password=quarkus_test quarkus.datasource.base.jdbc.url=jdbc:postgresql://localhost:5432/quarkus_test -quarkus.flyway.base.locations=classpath:database/base <4> +quarkus.flyway.base.locations=classpath:database/base <5> quarkus.flyway.base.migrate-at-start=true # Tenant 'mycompany' -quarkus.datasource.mycompany.db-kind=postgresql <5> +quarkus.datasource.mycompany.db-kind=postgresql <6> quarkus.datasource.mycompany.username=mycompany quarkus.datasource.mycompany.password=mycompany quarkus.datasource.mycompany.jdbc.url=jdbc:postgresql://localhost:5433/mycompany -quarkus.flyway.mycompany.locations=classpath:database/mycompany <6> +quarkus.flyway.mycompany.locations=classpath:database/mycompany <7> quarkus.flyway.mycompany.migrate-at-start=true ---- <1> Disable schema generation, because it is not supported by Hibernate ORM for database multi-tenancy. We'll use Flyway instead, see further down. <2> Enable database multi-tenancy. -<3> xref:datasource.adoc[Configure the datasource] for one tenant, `base`. -<4> Configure xref:flyway.adoc[Flyway] for database initialization for tenant `base`, +<3> Select a datasource for the persistence unit. ++ +This is only to allow Quarkus to determine the Hibernate ORM dialect to use; +see <> for details. +<4> xref:datasource.adoc[Configure the datasource] for one tenant, `base`. +<5> Configure xref:flyway.adoc[Flyway] for database initialization for tenant `base`, because schema generation by Hibernate ORM is not supported in this case. -<5> xref:datasource.adoc[Configure the datasource] for another tenant. +<6> xref:datasource.adoc[Configure the datasource] for another tenant. + There could be more tenants, but here we're stopping at two. -<6> Configure xref:flyway.adoc[Flyway] for database initialization for tenant `mycompany`, +<7> Configure xref:flyway.adoc[Flyway] for database initialization for tenant `mycompany`, because schema generation by Hibernate ORM is not supported in this case. Following are examples of the Flyway SQL files to be created in the configured folder `src/main/resources/database`. diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java index e6c30584166c6..dd91316bffb0e 100644 --- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmConfigPersistenceUnit.java @@ -278,6 +278,18 @@ interface HibernateOrmConfigPersistenceUnitDialect { /** * Name of the Hibernate ORM dialect. * + * For xref:datasource.adoc#extensions-and-database-drivers-reference[supported databases], + * this property does not need to be set explicitly: + * it is selected automatically based on the datasource, + * and configured using the xref:datasource.adoc#quarkus-datasource_quarkus.datasource.db-version[DB version set on the + * datasource] + * to benefit from the best performance and latest features. + * + * If your database does not have a corresponding Quarkus extension, + * you *will* need to set this property explicitly. + * In that case, keep in mind that the JDBC driver and Hibernate ORM dialect + * may not work properly in GraalVM native executables. + * * For built-in dialects, the expected value is one of the names * in the link:{hibernate-orm-dialect-docs-url}[official list of dialects], * *without* the `Dialect` suffix, @@ -286,20 +298,6 @@ interface HibernateOrmConfigPersistenceUnitDialect { * For third-party dialects, the expected value is the fully-qualified class name, * for example `com.acme.hibernate.AcmeDbDialect`. * - * Setting the dialect directly is only recommended as a last resort: - * most popular databases have a corresponding Quarkus extension, - * allowing Quarkus to select the dialect automatically, - * in which case you do not need to set the dialect at all, - * though you may want to set - * xref:datasource.adoc#quarkus-datasource_quarkus.datasource.db-version[`quarkus.datasource.db-version`] as - * high as possible - * to benefit from the best performance and latest features. - * - * If your database does not have a corresponding Quarkus extension, - * you will need to set the dialect directly. - * In that case, keep in mind that the JDBC driver and Hibernate ORM dialect - * may not work properly in GraalVM native executables. - * * @asciidoclet */ @WithParentName From c9060fef45d353fba05c35178ba6e5e0aaf2fb50 Mon Sep 17 00:00:00 2001 From: Peter Palaga Date: Tue, 9 Jul 2024 16:08:24 +0200 Subject: [PATCH 64/94] Add class level JavaDoc to BytecodeTransformerBuildItem --- .../builditem/BytecodeTransformerBuildItem.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/BytecodeTransformerBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/BytecodeTransformerBuildItem.java index ec968f494b8b0..ea79ca227b8bf 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/BytecodeTransformerBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/BytecodeTransformerBuildItem.java @@ -8,6 +8,15 @@ import io.quarkus.builder.item.MultiBuildItem; +/** + * Transform a class using ASM {@link ClassVisitor}. Note that the transformation is performed after assembling the + * index and thus the changes won't be visible to any processor steps relying on the index. + *

+ * You may consider using {@code io.quarkus.arc.deployment.AnnotationsTransformerBuildItem} if your transformation + * should be visible for Arc. See also + * I Need To + * Transform Annotation Metadata section of Quarkus CDI integration guide. + */ public final class BytecodeTransformerBuildItem extends MultiBuildItem { final String classToTransform; From 30af1a6bffe303d275c6f03acb958109c5436bd2 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 9 Jul 2024 11:30:59 +0200 Subject: [PATCH 65/94] Scheduler: fix Trigger#getNextFireTime() for cron-based jobs - fixes #41717 --- .../ScheduledMethodTimeZoneTest.java | 2 +- .../timezone/TriggerNextFireTimeZoneTest.java | 74 +++++++++++++++ .../timezone/TriggerPrevFireTimeZoneTest.java | 93 +++++++++++++++++++ .../quarkus/scheduler/ScheduledExecution.java | 9 ++ .../java/io/quarkus/scheduler/Trigger.java | 12 +++ .../ScheduledMethodTimeZoneTest.java | 3 +- .../timezone/TriggerNextFireTimeZoneTest.java | 74 +++++++++++++++ .../timezone/TriggerPrevFireTimeZoneTest.java | 93 +++++++++++++++++++ .../scheduler/runtime/SimpleScheduler.java | 23 +++-- 9 files changed, 371 insertions(+), 12 deletions(-) rename extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/{ => timezone}/ScheduledMethodTimeZoneTest.java (98%) create mode 100644 extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/TriggerNextFireTimeZoneTest.java create mode 100644 extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/TriggerPrevFireTimeZoneTest.java rename extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/{ => timezone}/ScheduledMethodTimeZoneTest.java (97%) create mode 100644 extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/TriggerNextFireTimeZoneTest.java create mode 100644 extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/TriggerPrevFireTimeZoneTest.java diff --git a/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/ScheduledMethodTimeZoneTest.java b/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/ScheduledMethodTimeZoneTest.java similarity index 98% rename from extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/ScheduledMethodTimeZoneTest.java rename to extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/ScheduledMethodTimeZoneTest.java index 95d4d47724c53..38b42d218c2a6 100644 --- a/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/ScheduledMethodTimeZoneTest.java +++ b/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/ScheduledMethodTimeZoneTest.java @@ -1,4 +1,4 @@ -package io.quarkus.quartz.test; +package io.quarkus.quartz.test.timezone; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/TriggerNextFireTimeZoneTest.java b/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/TriggerNextFireTimeZoneTest.java new file mode 100644 index 0000000000000..4e588223254a3 --- /dev/null +++ b/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/TriggerNextFireTimeZoneTest.java @@ -0,0 +1,74 @@ +package io.quarkus.quartz.test.timezone; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.scheduler.Scheduled; +import io.quarkus.scheduler.ScheduledExecution; +import io.quarkus.scheduler.Scheduler; +import io.quarkus.scheduler.Trigger; +import io.quarkus.test.QuarkusUnitTest; + +public class TriggerNextFireTimeZoneTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Jobs.class); + }); + + @Inject + Scheduler scheduler; + + @Test + public void testScheduledJobs() throws InterruptedException { + Trigger prague = scheduler.getScheduledJob("prague"); + Trigger boston = scheduler.getScheduledJob("boston"); + Trigger ulaanbaatar = scheduler.getScheduledJob("ulaanbaatar"); + assertNotNull(prague); + assertNotNull(boston); + assertNotNull(ulaanbaatar); + Instant pragueNext = prague.getNextFireTime(); + Instant bostonNext = boston.getNextFireTime(); + Instant ulaanbaatarNext = ulaanbaatar.getNextFireTime(); + assertTime(pragueNext.atZone(ZoneId.of("Europe/Prague"))); + assertTime(bostonNext.atZone(ZoneId.of("America/New_York"))); + assertTime(ulaanbaatarNext.atZone(ZoneId.of("Asia/Ulaanbaatar"))); + } + + private static void assertTime(ZonedDateTime time) { + assertEquals(20, time.getHour()); + assertEquals(30, time.getMinute()); + assertEquals(0, time.getSecond()); + } + + static class Jobs { + + @Scheduled(identity = "prague", cron = "0 30 20 * * ?", timeZone = "Europe/Prague") + void withPragueTimezone(ScheduledExecution execution) { + assertNotEquals(execution.getFireTime(), execution.getScheduledFireTime()); + assertTime(execution.getScheduledFireTime().atZone(ZoneId.of("Europe/Prague"))); + } + + @Scheduled(identity = "boston", cron = "0 30 20 * * ?", timeZone = "America/New_York") + void withBostonTimezone() { + } + + @Scheduled(identity = "ulaanbaatar", cron = "0 30 20 * * ?", timeZone = "Asia/Ulaanbaatar") + void withIstanbulTimezone(ScheduledExecution execution) { + assertTime(execution.getScheduledFireTime().atZone(ZoneId.of("Asia/Ulaanbaatar"))); + } + + } + +} diff --git a/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/TriggerPrevFireTimeZoneTest.java b/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/TriggerPrevFireTimeZoneTest.java new file mode 100644 index 0000000000000..4da6a548d7e43 --- /dev/null +++ b/extensions/quartz/deployment/src/test/java/io/quarkus/quartz/test/timezone/TriggerPrevFireTimeZoneTest.java @@ -0,0 +1,93 @@ +package io.quarkus.quartz.test.timezone; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.scheduler.Scheduled; +import io.quarkus.scheduler.Scheduler; +import io.quarkus.scheduler.Trigger; +import io.quarkus.test.QuarkusUnitTest; + +public class TriggerPrevFireTimeZoneTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + ZonedDateTime now = ZonedDateTime.now(); + ZonedDateTime prague = now.withZoneSameInstant(ZoneId.of("Europe/Prague")); + ZonedDateTime istanbul = now.withZoneSameInstant(ZoneId.of("Europe/Istanbul")); + // For example, the current date-time is 2024-07-09 10:08:00; + // the default time zone is Europe/London + // then the config should look like: + // simpleJobs1.cron=0/1 * 11 * * ? + // simpleJobs2.cron=0/1 * 12 * * ? + String properties = String.format( + "simpleJobs1.cron=0/1 * %s * * ?\n" + + "simpleJobs1.hour=%s\n" + + "simpleJobs2.cron=0/1 * %s * * ?\n" + + "simpleJobs2.hour=%s", + prague.getHour(), prague.getHour(), istanbul.getHour(), istanbul.getHour()); + root.addClasses(Jobs.class) + .addAsResource( + new StringAsset(properties), + "application.properties"); + }); + + @ConfigProperty(name = "simpleJobs1.hour") + int pragueHour; + + @ConfigProperty(name = "simpleJobs2.hour") + int istanbulHour; + + @Inject + Scheduler scheduler; + + @Test + public void testScheduledJobs() throws InterruptedException { + assertTrue(Jobs.PRAGUE_LATCH.await(5, TimeUnit.SECONDS)); + assertTrue(Jobs.ISTANBUL_LATCH.await(5, TimeUnit.SECONDS)); + Trigger prague = scheduler.getScheduledJob("prague"); + Trigger istanbul = scheduler.getScheduledJob("istanbul"); + assertNotNull(prague); + assertNotNull(istanbul); + Instant praguePrev = prague.getPreviousFireTime(); + Instant istanbulPrev = istanbul.getPreviousFireTime(); + assertNotNull(praguePrev); + assertNotNull(istanbulPrev); + assertEquals(praguePrev, istanbulPrev); + assertEquals(pragueHour, praguePrev.atZone(ZoneId.of("Europe/Prague")).getHour()); + assertEquals(istanbulHour, istanbulPrev.atZone(ZoneId.of("Europe/Istanbul")).getHour()); + } + + static class Jobs { + + static final CountDownLatch PRAGUE_LATCH = new CountDownLatch(1); + static final CountDownLatch ISTANBUL_LATCH = new CountDownLatch(1); + + @Scheduled(identity = "prague", cron = "{simpleJobs1.cron}", timeZone = "Europe/Prague") + void withPragueTimezone() { + PRAGUE_LATCH.countDown(); + } + + @Scheduled(identity = "istanbul", cron = "{simpleJobs2.cron}", timeZone = "Europe/Istanbul") + void withIstanbulTimezone() { + ISTANBUL_LATCH.countDown(); + } + + } + +} diff --git a/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/ScheduledExecution.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/ScheduledExecution.java index 5f69240af667c..fda1ac26fec88 100644 --- a/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/ScheduledExecution.java +++ b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/ScheduledExecution.java @@ -14,6 +14,9 @@ public interface ScheduledExecution { Trigger getTrigger(); /** + * The returned {@code Instant} is converted from the date-time in the default timezone. A timezone of a cron-based job + * is not taken into account. + *

* Unlike {@link Trigger#getPreviousFireTime()} this method always returns the same value. * * @return the time the associated trigger was fired @@ -21,6 +24,12 @@ public interface ScheduledExecution { Instant getFireTime(); /** + * If the trigger represents a cron-based job with a timezone, then the returned {@code Instant} takes the timezone into + * account. + *

+ * For example, if there is a job with cron expression {@code 0 30 20 ? * * *} with timezone {@code Europe/Berlin}, + * then the return value looks like {@code 2024-07-08T18:30:00Z}. And {@link Instant#atZone(java.time.ZoneId)} for + * {@code Europe/Berlin} would yield {@code 2024-07-08T20:30+02:00[Europe/Berlin]}. * * @return the time the action was scheduled for */ diff --git a/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Trigger.java b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Trigger.java index c076e5712bc0e..0a5f94d48ffb3 100644 --- a/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Trigger.java +++ b/extensions/scheduler/api/src/main/java/io/quarkus/scheduler/Trigger.java @@ -21,12 +21,24 @@ public interface Trigger { String getId(); /** + * If the trigger represents a cron-based job with a timezone, then the returned {@code Instant} takes the timezone into + * account. + *

+ * For example, if there is a job with cron expression {@code 0 30 20 ? * * *} with timezone {@code Europe/Berlin}, then the + * return value looks like {@code 2024-07-08T18:30:00Z}. And {@link Instant#atZone(java.time.ZoneId)} for + * {@code Europe/Berlin} would yield {@code 2024-07-08T20:30+02:00[Europe/Berlin]}. * * @return the next time at which the trigger is scheduled to fire, or {@code null} if it will not fire again */ Instant getNextFireTime(); /** + * If the trigger represents a cron-based job with a timezone, then the returned {@code Instant} takes the timezone into + * account. + *

+ * For example, if there is a job with cron expression {@code 0 30 20 ? * * *} with timezone {@code Europe/Berlin}, then the + * return value looks like {@code 2024-07-08T18:30:00Z}. And {@link Instant#atZone(java.time.ZoneId)} for + * {@code Europe/Berlin} would yield {@code 2024-07-08T20:30+02:00[Europe/Berlin]}. * * @return the previous time at which the trigger fired, or {@code null} if it has not fired yet */ diff --git a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/ScheduledMethodTimeZoneTest.java b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/ScheduledMethodTimeZoneTest.java similarity index 97% rename from extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/ScheduledMethodTimeZoneTest.java rename to extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/ScheduledMethodTimeZoneTest.java index aba645216812a..9db547b46e7a0 100644 --- a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/ScheduledMethodTimeZoneTest.java +++ b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/ScheduledMethodTimeZoneTest.java @@ -1,4 +1,4 @@ -package io.quarkus.scheduler.test; +package io.quarkus.scheduler.test.timezone; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -41,7 +41,6 @@ public class ScheduledMethodTimeZoneTest { + "simpleJobs2.cron=0/1 * %s * * ?\n" + "simpleJobs2.timeZone=%s", now.getHour(), timeZone, job2Hour, timeZone); - // System.out.println(properties); jar.addClasses(Jobs.class) .addAsResource( new StringAsset(properties), diff --git a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/TriggerNextFireTimeZoneTest.java b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/TriggerNextFireTimeZoneTest.java new file mode 100644 index 0000000000000..d301a0e1cff5d --- /dev/null +++ b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/TriggerNextFireTimeZoneTest.java @@ -0,0 +1,74 @@ +package io.quarkus.scheduler.test.timezone; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.scheduler.Scheduled; +import io.quarkus.scheduler.ScheduledExecution; +import io.quarkus.scheduler.Scheduler; +import io.quarkus.scheduler.Trigger; +import io.quarkus.test.QuarkusUnitTest; + +public class TriggerNextFireTimeZoneTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + root.addClasses(Jobs.class); + }); + + @Inject + Scheduler scheduler; + + @Test + public void testScheduledJobs() throws InterruptedException { + Trigger prague = scheduler.getScheduledJob("prague"); + Trigger boston = scheduler.getScheduledJob("boston"); + Trigger ulaanbaatar = scheduler.getScheduledJob("ulaanbaatar"); + assertNotNull(prague); + assertNotNull(boston); + assertNotNull(ulaanbaatar); + Instant pragueNext = prague.getNextFireTime(); + Instant bostonNext = boston.getNextFireTime(); + Instant ulaanbaatarNext = ulaanbaatar.getNextFireTime(); + assertTime(pragueNext.atZone(ZoneId.of("Europe/Prague"))); + assertTime(bostonNext.atZone(ZoneId.of("America/New_York"))); + assertTime(ulaanbaatarNext.atZone(ZoneId.of("Asia/Ulaanbaatar"))); + } + + private static void assertTime(ZonedDateTime time) { + assertEquals(20, time.getHour()); + assertEquals(30, time.getMinute()); + assertEquals(0, time.getSecond()); + } + + static class Jobs { + + @Scheduled(identity = "prague", cron = "0 30 20 * * ?", timeZone = "Europe/Prague") + void withPragueTimezone(ScheduledExecution execution) { + assertNotEquals(execution.getFireTime(), execution.getScheduledFireTime()); + assertTime(execution.getScheduledFireTime().atZone(ZoneId.of("Europe/Prague"))); + } + + @Scheduled(identity = "boston", cron = "0 30 20 * * ?", timeZone = "America/New_York") + void withBostonTimezone() { + } + + @Scheduled(identity = "ulaanbaatar", cron = "0 30 20 * * ?", timeZone = "Asia/Ulaanbaatar") + void withIstanbulTimezone(ScheduledExecution execution) { + assertTime(execution.getScheduledFireTime().atZone(ZoneId.of("Asia/Ulaanbaatar"))); + } + + } + +} diff --git a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/TriggerPrevFireTimeZoneTest.java b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/TriggerPrevFireTimeZoneTest.java new file mode 100644 index 0000000000000..ed1ef873b77b4 --- /dev/null +++ b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/timezone/TriggerPrevFireTimeZoneTest.java @@ -0,0 +1,93 @@ +package io.quarkus.scheduler.test.timezone; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.scheduler.Scheduled; +import io.quarkus.scheduler.Scheduler; +import io.quarkus.scheduler.Trigger; +import io.quarkus.test.QuarkusUnitTest; + +public class TriggerPrevFireTimeZoneTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot(root -> { + ZonedDateTime now = ZonedDateTime.now(); + ZonedDateTime prague = now.withZoneSameInstant(ZoneId.of("Europe/Prague")); + ZonedDateTime istanbul = now.withZoneSameInstant(ZoneId.of("Europe/Istanbul")); + // For example, the current date-time is 2024-07-09 10:08:00; + // the default time zone is Europe/London + // then the config should look like: + // simpleJobs1.cron=0/1 * 11 * * ? + // simpleJobs2.cron=0/1 * 12 * * ? + String properties = String.format( + "simpleJobs1.cron=0/1 * %s * * ?\n" + + "simpleJobs1.hour=%s\n" + + "simpleJobs2.cron=0/1 * %s * * ?\n" + + "simpleJobs2.hour=%s", + prague.getHour(), prague.getHour(), istanbul.getHour(), istanbul.getHour()); + root.addClasses(Jobs.class) + .addAsResource( + new StringAsset(properties), + "application.properties"); + }); + + @ConfigProperty(name = "simpleJobs1.hour") + int pragueHour; + + @ConfigProperty(name = "simpleJobs2.hour") + int istanbulHour; + + @Inject + Scheduler scheduler; + + @Test + public void testScheduledJobs() throws InterruptedException { + assertTrue(Jobs.PRAGUE_LATCH.await(5, TimeUnit.SECONDS)); + assertTrue(Jobs.ISTANBUL_LATCH.await(5, TimeUnit.SECONDS)); + Trigger prague = scheduler.getScheduledJob("prague"); + Trigger istanbul = scheduler.getScheduledJob("istanbul"); + assertNotNull(prague); + assertNotNull(istanbul); + Instant praguePrev = prague.getPreviousFireTime(); + Instant istanbulPrev = istanbul.getPreviousFireTime(); + assertNotNull(praguePrev); + assertNotNull(istanbulPrev); + assertEquals(praguePrev, istanbulPrev); + assertEquals(pragueHour, praguePrev.atZone(ZoneId.of("Europe/Prague")).getHour()); + assertEquals(istanbulHour, istanbulPrev.atZone(ZoneId.of("Europe/Istanbul")).getHour()); + } + + static class Jobs { + + static final CountDownLatch PRAGUE_LATCH = new CountDownLatch(1); + static final CountDownLatch ISTANBUL_LATCH = new CountDownLatch(1); + + @Scheduled(identity = "prague", cron = "{simpleJobs1.cron}", timeZone = "Europe/Prague") + void withPragueTimezone() { + PRAGUE_LATCH.countDown(); + } + + @Scheduled(identity = "istanbul", cron = "{simpleJobs2.cron}", timeZone = "Europe/Istanbul") + void withIstanbulTimezone() { + ISTANBUL_LATCH.countDown(); + } + + } + +} diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java index 02e2b46e25b14..8a3d77797146e 100644 --- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java +++ b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java @@ -589,28 +589,29 @@ static class CronTrigger extends SimpleTrigger { super(id, start, description); this.cron = cron; this.executionTime = ExecutionTime.forCron(cron); - this.lastFireTime = start; this.gracePeriod = gracePeriod; this.timeZone = timeZone; + // The last fire time stores the zoned time + this.lastFireTime = zoned(start); } @Override public Instant getNextFireTime() { - Optional nextFireTime = executionTime.nextExecution(lastFireTime); - return nextFireTime.isPresent() ? nextFireTime.get().toInstant() : null; + return executionTime.nextExecution(lastFireTime).map(ZonedDateTime::toInstant).orElse(null); } + @Override ZonedDateTime evaluate(ZonedDateTime now) { if (now.isBefore(start)) { return null; } - ZonedDateTime zonedNow = timeZone == null ? now : now.withZoneSameInstant(timeZone); - Optional lastExecution = executionTime.lastExecution(zonedNow); + now = zoned(now); + Optional lastExecution = executionTime.lastExecution(now); if (lastExecution.isPresent()) { ZonedDateTime lastTruncated = lastExecution.get().truncatedTo(ChronoUnit.SECONDS); - if (zonedNow.isAfter(lastTruncated) && lastFireTime.isBefore(lastTruncated)) { + if (now.isAfter(lastTruncated) && lastFireTime.isBefore(lastTruncated)) { LOG.tracef("%s fired, last=%s", this, lastTruncated); - lastFireTime = zonedNow; + lastFireTime = now; return lastTruncated; } } @@ -623,9 +624,9 @@ public boolean isOverdue() { if (now.isBefore(start)) { return false; } - ZonedDateTime zonedNow = timeZone == null ? now : now.withZoneSameInstant(timeZone); + now = zoned(now); Optional nextFireTime = executionTime.nextExecution(lastFireTime); - return nextFireTime.isEmpty() || nextFireTime.get().plus(gracePeriod).isBefore(zonedNow); + return nextFireTime.isEmpty() || nextFireTime.get().plus(gracePeriod).isBefore(now); } @Override @@ -634,6 +635,10 @@ public String toString() { + timeZone + "]"; } + private ZonedDateTime zoned(ZonedDateTime time) { + return timeZone == null ? time : time.withZoneSameInstant(timeZone); + } + } static class SimpleScheduledExecution implements ScheduledExecution { From c58af08ae0ce59a469f3f23991977e31800f1415 Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Tue, 9 Jul 2024 10:23:08 -0500 Subject: [PATCH 66/94] Use `SecureDirectoryStream` to avoid FS problems and fix other minor issues in `IoUtils` Possible fix for #41767. --- .../io/quarkus/bootstrap/util/IoUtils.java | 154 ++++++++++++------ 1 file changed, 102 insertions(+), 52 deletions(-) diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/IoUtils.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/IoUtils.java index 0c4006fc3dc74..18513222b7384 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/IoUtils.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/util/IoUtils.java @@ -1,21 +1,23 @@ package io.quarkus.bootstrap.util; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.StringWriter; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.SecureDirectoryStream; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributes; import java.util.EnumSet; import java.util.Objects; @@ -29,8 +31,6 @@ */ public class IoUtils { - private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; - private static final Path TMP_DIR = Paths.get(PropertyUtils.getProperty("java.io.tmpdir")); private static final Logger log = Logger.getLogger(IoUtils.class); @@ -60,40 +60,36 @@ public static Path mkdirs(Path dir) { return dir; } + /** + * Recursively delete the file or directory given by {@code root}. + * The implementation will attempt to do so in a secure manner. + * Any problems encountered will be logged at {@code DEBUG} level. + * + * @param root the root path (must not be {@code null}) + */ public static void recursiveDelete(Path root) { - log.debugf("Recursively delete directory %s", root); + log.debugf("Recursively delete path %s", root); if (root == null || !Files.exists(root)) { return; } try { - Files.walkFileTree(root, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) - throws IOException { - try { - Files.delete(file); - } catch (IOException ex) { - log.debugf(ex, "Unable to delete file " + file); - } - return FileVisitResult.CONTINUE; + if (Files.isDirectory(root)) { + try (DirectoryStream ds = Files.newDirectoryStream(root)) { + recursiveDelete(ds); } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException e) - throws IOException { - if (e == null) { - try { - Files.delete(dir); - } catch (IOException ex) { - log.debugf(ex, "Unable to delete directory " + dir); - } - return FileVisitResult.CONTINUE; - } else { - // directory iteration failed - throw e; - } + try { + Files.delete(root); + } catch (IOException e) { + log.debugf(e, "Unable to delete directory %s", root); } - }); + } else { + log.debugf("Delete file %s", root); + try { + Files.delete(root); + } catch (IOException e) { + log.debugf(e, "Unable to delete file %s", root); + } + } } catch (IOException e) { log.debugf(e, "Error recursively deleting directory"); } @@ -101,9 +97,10 @@ public FileVisitResult postVisitDirectory(Path dir, IOException e) /** * Creates a new empty directory or empties an existing one. + * Any problems encountered while emptying the directory will be logged at {@code DEBUG} level. * * @param dir directory - * @throws IOException in case of a failure + * @throws IOException if creating or accessing the directory itself fails */ public static void createOrEmptyDir(Path dir) throws IOException { log.debugf("Create or empty directory %s", dir); @@ -113,17 +110,51 @@ public static void createOrEmptyDir(Path dir) throws IOException { Files.createDirectories(dir); return; } - if (!Files.isDirectory(dir)) { - throw new IllegalArgumentException(dir + " is not a directory"); + // recursively delete the *contents* of the directory, if any (keep the directory itself) + try (DirectoryStream ds = Files.newDirectoryStream(dir)) { + recursiveDelete(ds); + } + } + + private static void recursiveDelete(DirectoryStream ds) { + if (ds instanceof SecureDirectoryStream sds) { + // best, fastest, and most likely path for most OSes + recursiveDeleteSecure(sds); + } else { + // this may not work well on e.g. NFS, so we avoid this path if possible + for (Path p : ds) { + recursiveDelete(p); + } } - log.debugf("Iterate over contents of %s to delete its contents", dir); - try (DirectoryStream stream = Files.newDirectoryStream(dir)) { - for (Path p : stream) { - if (Files.isDirectory(p)) { - recursiveDelete(p); - } else { - log.debugf("Delete file %s", p); - Files.delete(p); + } + + private static void recursiveDeleteSecure(SecureDirectoryStream sds) { + for (Path p : sds) { + Path file = p.getFileName(); + BasicFileAttributes attrs; + try { + attrs = sds.getFileAttributeView(file, BasicFileAttributeView.class, LinkOption.NOFOLLOW_LINKS) + .readAttributes(); + } catch (IOException e) { + log.debugf(e, "Unable to query file type of %s", p); + continue; + } + if (attrs.isDirectory()) { + try { + try (SecureDirectoryStream nested = sds.newDirectoryStream(file)) { + recursiveDeleteSecure(nested); + } + sds.deleteDirectory(file); + } catch (IOException e) { + log.debugf(e, "Unable to delete directory %s", p); + } + } else { + // log the whole path, not the file name + log.debugf("Delete file %s", p); + try { + sds.deleteFile(file); + } catch (IOException e) { + log.debugf(e, "Unable to delete file %s", p); } } } @@ -163,24 +194,43 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) return target; } + /** + * Read the contents of a file as a string. + * + * @param file the file to read (must not be {@code null}) + * @return the file content, as a string (not {@code null}) + * @throws IOException if an error occurs when reading the file + * @deprecated Use {@link Files#readString(Path, Charset)} instead. + */ + @Deprecated(forRemoval = true) public static String readFile(Path file) throws IOException { - final char[] charBuffer = new char[DEFAULT_BUFFER_SIZE]; - int n = 0; - final StringWriter output = new StringWriter(); - try (BufferedReader input = Files.newBufferedReader(file)) { - while ((n = input.read(charBuffer)) != -1) { - output.write(charBuffer, 0, n); - } - } - return output.getBuffer().toString(); + return Files.readString(file, StandardCharsets.UTF_8); } + /** + * Copy the input stream to the given output stream. + * Calling this method is identical to calling {@code in.transferTo(out)}. + * + * @param out the output stream (must not be {@code null}) + * @param in the input stream (must not be {@code null}) + * @throws IOException if an error occurs during the copy + * @see InputStream#transferTo(OutputStream) + */ public static void copy(OutputStream out, InputStream in) throws IOException { in.transferTo(out); } + /** + * Write a string to a file using UTF-8 encoding. + * The file will be created if it does not exist, and truncated if it is not empty. + * + * @param file the file to write (must not be {@code null}) + * @param content the string to write to the file (must not be {@code null}) + * @throws IOException if an error occurs when writing the file + */ public static void writeFile(Path file, String content) throws IOException { - Files.write(file, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + Files.writeString(file, content, StandardCharsets.UTF_8, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); } } From 1b88b51aae09307d50a3a794bc55edd3e7af0909 Mon Sep 17 00:00:00 2001 From: Rolfe Dlugy-Hegwer Date: Tue, 9 Jul 2024 12:50:13 -0400 Subject: [PATCH 67/94] Update keycloak version to `25.0.0` on main --- 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-multitenancy.adoc | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/main/asciidoc/security-keycloak-authorization.adoc b/docs/src/main/asciidoc/security-keycloak-authorization.adoc index 94d844883ebc9..a4d34e1407fd5 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 `23.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.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]. 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 67717da4065b1..c4a6a64a9a0e3 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 <> section. 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 `23.0.0` or later. +* Where the `keycloak.version` is set to version `25.0.0` 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 63d99eab3b2fc..f47b743a12086 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 `23.0.0` or later. +where `keycloak.version` is set to `25.0.0` 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 ef1c2287d3694..2cd8cc109f104 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client.adoc @@ -552,7 +552,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 `23.0.0` or later. +Set `{keycloak.version}` to `25.0.0` or later. You can access your Keycloak Server at http://localhost:8180[localhost:8180]. diff --git a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc index 0cca72b4f7dad..d031ecdcd0071 100644 --- a/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-multitenancy.adoc @@ -351,7 +351,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 `23.0.0` or higher. +where `keycloak.version` is set to `25.0.0` or higher. Access your Keycloak server at http://localhost:8180[localhost:8180]. From 8dfb870c6e89ca1ff1c5e526db4f0824bd3e57d0 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 9 Jul 2024 16:47:30 -0300 Subject: [PATCH 68/94] Fix #41789 Add git option to enable longpaths on Windows --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c6e1625f9e3c9..e042ea1b5973a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -233,6 +233,7 @@ If you have not done so on this machine, you need to: * macOS: Use the `Disk Utility.app` to check. It also allows you to create a case-sensitive volume to store your code projects. See this [blog entry](https://karnsonline.com/case-sensitive-apfs/) for more. * Windows: [Enable case sensitive file names per directory](https://learn.microsoft.com/en-us/windows/wsl/case-sensitivity) * Install Git and configure your GitHub access + * Windows: enable longpaths: `git config --global core.longpaths true` * Install Java SDK 17+ (OpenJDK recommended) * Install [GraalVM](https://quarkus.io/guides/building-native-image) * Install platform C developer tools: From 7d874019a21416327cdeaf253470af37ed7352b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 19:57:32 +0000 Subject: [PATCH 69/94] Bump org.assertj:assertj-core from 3.26.0 to 3.26.3 in /devtools/gradle Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.26.0 to 3.26.3. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.26.0...assertj-build-3.26.3) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- devtools/gradle/gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/gradle/gradle/libs.versions.toml b/devtools/gradle/gradle/libs.versions.toml index e613c297de53e..4ac165ca0f938 100644 --- a/devtools/gradle/gradle/libs.versions.toml +++ b/devtools/gradle/gradle/libs.versions.toml @@ -6,7 +6,7 @@ kotlin = "2.0.0" smallrye-config = "3.8.3" junit5 = "5.10.3" -assertj = "3.26.0" +assertj = "3.26.3" [plugins] plugin-publish = { id = "com.gradle.plugin-publish", version.ref = "plugin-publish" } From b5f55088173bdd4ba6c7773c7a350c51ecc7bc8b Mon Sep 17 00:00:00 2001 From: Roberto Balarezo Date: Tue, 9 Jul 2024 14:14:51 -0500 Subject: [PATCH 70/94] Fix NPE when calling a ClientResponseFilter --- .../client/handlers/ClientSetResponseEntityRestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java index cb811043af91b..8e0232603448e 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java @@ -24,7 +24,7 @@ public class ClientSetResponseEntityRestHandler implements ClientRestHandler { @Override public void handle(RestClientRequestContext context) throws Exception { - ClientRequestContextImpl requestContext = context.getClientRequestContext(); + ClientRequestContextImpl requestContext = context.getOrCreateClientRequestContext(); if (context.isCheckSuccessfulFamily()) { StatusType effectiveResponseStatus = determineEffectiveResponseStatus(context, requestContext); if (Response.Status.Family.familyOf(effectiveResponseStatus.getStatusCode()) != Response.Status.Family.SUCCESSFUL) { From b5890b5e007aace0a4e4188699ec6e0c962f7686 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 22:03:07 +0000 Subject: [PATCH 71/94] Bump org.assertj:assertj-core from 3.26.0 to 3.26.3 Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.26.0 to 3.26.3. - [Release notes](https://github.com/assertj/assertj/releases) - [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.26.0...assertj-build-3.26.3) --- updated-dependencies: - dependency-name: org.assertj:assertj-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build-parent/pom.xml | 2 +- independent-projects/arc/pom.xml | 2 +- independent-projects/bootstrap/pom.xml | 2 +- independent-projects/junit5-virtual-threads/pom.xml | 2 +- independent-projects/qute/pom.xml | 2 +- independent-projects/resteasy-reactive/pom.xml | 2 +- independent-projects/tools/pom.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 371a946a9aea2..053deff0e84c4 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -101,7 +101,7 @@ 7.0.1 - 3.26.0 + 3.26.3 3.8.0 7.3.0 diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml index dc4ae1b543f15..f305342687fef 100644 --- a/independent-projects/arc/pom.xml +++ b/independent-projects/arc/pom.xml @@ -50,7 +50,7 @@ 2.6.1 1.6.Final - 3.26.0 + 3.26.3 5.10.3 2.0.0 1.8.1 diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index 9947110925a77..56cf070bcae3f 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -38,7 +38,7 @@ 1.37 - 3.26.0 + 3.26.3 0.9.5 3.6.0.Final 5.10.3 diff --git a/independent-projects/junit5-virtual-threads/pom.xml b/independent-projects/junit5-virtual-threads/pom.xml index d3c57b6cd6c79..875e37d03ee88 100644 --- a/independent-projects/junit5-virtual-threads/pom.xml +++ b/independent-projects/junit5-virtual-threads/pom.xml @@ -46,7 +46,7 @@ 1.11.0 5.10.3 - 3.26.0 + 3.26.3 diff --git a/independent-projects/qute/pom.xml b/independent-projects/qute/pom.xml index e5bc2a45674c3..dbb58c57a40ee 100644 --- a/independent-projects/qute/pom.xml +++ b/independent-projects/qute/pom.xml @@ -39,7 +39,7 @@ UTF-8 5.10.3 - 3.26.0 + 3.26.3 3.2.0 1.8.0 3.6.0.Final diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 4f2d4c0744c21..9f47d79ec5775 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -49,7 +49,7 @@ 1.14.11 5.10.3 3.9.8 - 3.26.0 + 3.26.3 3.6.0.Final 3.0.6.Final 3.0.0 diff --git a/independent-projects/tools/pom.xml b/independent-projects/tools/pom.xml index 28abaed4649c6..6fd63c9de1735 100644 --- a/independent-projects/tools/pom.xml +++ b/independent-projects/tools/pom.xml @@ -48,7 +48,7 @@ 4.4.0 - 3.26.0 + 3.26.3 2.17.2 4.1.0 5.10.3 From c34b6420eca614969666b98a3af01d2f2566e765 Mon Sep 17 00:00:00 2001 From: Bruno Alves Date: Tue, 9 Jul 2024 22:15:40 -0300 Subject: [PATCH 72/94] Add git option to avoid CRLF breaks on Windows --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e042ea1b5973a..8133b71a9f14e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -233,7 +233,9 @@ If you have not done so on this machine, you need to: * macOS: Use the `Disk Utility.app` to check. It also allows you to create a case-sensitive volume to store your code projects. See this [blog entry](https://karnsonline.com/case-sensitive-apfs/) for more. * Windows: [Enable case sensitive file names per directory](https://learn.microsoft.com/en-us/windows/wsl/case-sensitivity) * Install Git and configure your GitHub access - * Windows: enable longpaths: `git config --global core.longpaths true` + * Windows: + * enable longpaths: `git config --global core.longpaths true` + * avoid CRLF breaks: `git config --global core.autocrlf false` * Install Java SDK 17+ (OpenJDK recommended) * Install [GraalVM](https://quarkus.io/guides/building-native-image) * Install platform C developer tools: From d30e0e640ae0fd15fa060d788b4ce93ebaa1a42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Wed, 10 Jul 2024 08:36:07 +0200 Subject: [PATCH 73/94] Quarkus REST - consider endpoint impl. sec. ann. --- .../deployment/ResteasyReactiveProcessor.java | 75 ++-- .../AbstractImplMethodSecuredTest.java | 411 ++++++++++++++++++ ...aultJaxRsDenyAllImplMethodSecuredTest.java | 16 + ...axRsRolesAllowedImplMethodSecuredTest.java | 16 + .../inheritance/ImplMethodSecuredTest.java | 12 + .../inheritance/JsonObjectReader.java | 56 +++ .../inheritance/SecurityAnnotation.java | 77 ++++ .../test/security/inheritance/SubPaths.java | 88 ++++ ...ourceWithPath_OnBase_SecurityOnParent.java | 17 + ...llBaseResourceWithPath_SecurityOnBase.java | 106 +++++ ...eResourceWithPath_SecurityOnInterface.java | 18 + ...ithoutPathExtParentRes_SecurityOnBase.java | 81 ++++ ...tPathExtParentRes_SecurityOnInterface.java | 5 + ...houtPathExtParentRes_SecurityOnParent.java | 6 + ...thoutPathImplInterface_SecurityOnBase.java | 76 ++++ ...PathImplInterface_SecurityOnInterface.java | 6 + ...eResourceWithoutPath_SecurityOnParent.java | 5 + ...nyAllInterfaceWithPath_SecurityOnBase.java | 61 +++ ...InterfaceWithPath_SecurityOnInterface.java | 72 +++ ...AllInterfaceWithPath_SecurityOnParent.java | 50 +++ ...Path_PathOnParent_SecurityOnInterface.java | 44 ++ ...outPath_PathOnParent_SecurityOnParent.java | 30 ++ ...llInterfaceWithoutPath_SecurityOnBase.java | 29 ++ ...erfaceWithoutPath_SecurityOnInterface.java | 41 ++ ...InterfaceWithoutPath_SecurityOnParent.java | 29 ++ ...arentResourceInterface_SecurityOnBase.java | 29 ++ ...ParentResourceWithPath_SecurityOnBase.java | 52 +++ ...tResourceWithPath_SecurityOnInterface.java | 18 + ...rentResourceWithPath_SecurityOnParent.java | 97 +++++ ...thoutPath_PathOnBase_SecurityOnParent.java | 59 +++ ...entResourceWithoutPath_SecurityOnBase.java | 32 ++ ...sourceWithoutPath_SecurityOnInterface.java | 6 + ...tResourceWithoutPath_SecurityOnParent.java | 58 +++ .../ClassDenyAllSubResourceWithoutPath.java | 20 + ...llBaseResourceWithPath_SecurityOnBase.java | 77 ++++ ...eResourceWithPath_SecurityOnInterface.java | 18 + ...ithoutPathExtParentRes_SecurityOnBase.java | 55 +++ ...tPathExtParentRes_SecurityOnInterface.java | 5 + ...houtPathExtParentRes_SecurityOnParent.java | 5 + ...thoutPathImplInterface_SecurityOnBase.java | 54 +++ ...PathImplInterface_SecurityOnInterface.java | 6 + ...ceWithoutPath_OnBase_SecurityOnParent.java | 17 + ...eResourceWithoutPath_SecurityOnParent.java | 5 + ...itAllInterfaceWithPath_SecurityOnBase.java | 48 ++ ...InterfaceWithPath_SecurityOnInterface.java | 54 +++ ...AllInterfaceWithPath_SecurityOnParent.java | 41 ++ ...Path_PathOnParent_SecurityOnInterface.java | 32 ++ ...outPath_PathOnParent_SecurityOnParent.java | 24 + ...llInterfaceWithoutPath_SecurityOnBase.java | 24 + ...erfaceWithoutPath_SecurityOnInterface.java | 31 ++ ...InterfaceWithoutPath_SecurityOnParent.java | 24 + ...arentResourceInterface_SecurityOnBase.java | 24 + ...ParentResourceWithPath_SecurityOnBase.java | 41 ++ ...tResourceWithPath_SecurityOnInterface.java | 18 + ...rentResourceWithPath_SecurityOnParent.java | 69 +++ ...thoutPath_PathOnBase_SecurityOnParent.java | 42 ++ ...entResourceWithoutPath_SecurityOnBase.java | 26 ++ ...sourceWithoutPath_SecurityOnInterface.java | 6 + ...tResourceWithoutPath_SecurityOnParent.java | 42 ++ .../ClassPermitAllSubResourceWithoutPath.java | 20 + ...edBaseResourceWithPath_SecurityOnBase.java | 51 +++ ...eResourceWithPath_SecurityOnInterface.java | 18 + ...BaseResourceWithPath_SecurityOnParent.java | 18 + ...ithoutPathExtParentRes_SecurityOnBase.java | 34 ++ ...tPathExtParentRes_SecurityOnInterface.java | 5 + ...houtPathExtParentRes_SecurityOnParent.java | 6 + ...thoutPathImplInterface_SecurityOnBase.java | 35 ++ ...PathImplInterface_SecurityOnInterface.java | 6 + ...ceWithoutPath_OnBase_SecurityOnParent.java | 17 + ...houtPath_OnInterface_SecurityOnParent.java | 6 + ...lowedInterfaceWithPath_SecurityOnBase.java | 36 ++ ...InterfaceWithPath_SecurityOnInterface.java | 40 ++ ...wedInterfaceWithPath_SecurityOnParent.java | 32 ++ ...Path_PathOnParent_SecurityOnInterface.java | 23 + ...outPath_PathOnParent_SecurityOnParent.java | 19 + ...edInterfaceWithoutPath_SecurityOnBase.java | 19 + ...erfaceWithoutPath_SecurityOnInterface.java | 23 + ...InterfaceWithoutPath_SecurityOnParent.java | 19 + ...arentResourceInterface_SecurityOnBase.java | 19 + ...ParentResourceWithPath_SecurityOnBase.java | 32 ++ ...tResourceWithPath_SecurityOnInterface.java | 18 + ...rentResourceWithPath_SecurityOnParent.java | 45 ++ ...thoutPath_PathOnBase_SecurityOnParent.java | 23 + ...Path_PathOnInterface_SecurityOnParent.java | 29 ++ ...entResourceWithoutPath_SecurityOnBase.java | 20 + ...sourceWithoutPath_SecurityOnInterface.java | 6 + ...tResourceWithoutPath_SecurityOnParent.java | 20 + ...assRolesAllowedSubResourceWithoutPath.java | 20 + .../multiple/pathonbase/BaseResource.java | 83 ++++ .../BaseResource_First_Interface.java | 31 ++ .../BaseResource_Second_Interface.java | 31 ++ .../BaseResource_Third_Interface.java | 50 +++ .../NoAnnotationBaseResourceWithPath.java | 132 ++++++ ...onBaseResourceWithoutPathExtParentRes.java | 105 +++++ ...nBaseResourceWithoutPathImplInterface.java | 110 +++++ .../NoAnnotationInterfaceWithPath.java | 152 +++++++ .../NoAnnotationInterfaceWithoutPath.java | 83 ++++ .../NoAnnotationParentResourceInterface.java | 82 ++++ .../NoAnnotationParentResourceWithPath.java | 142 ++++++ ...NoAnnotationParentResourceWithoutPath.java | 95 ++++ ...arentResourceWithoutPathImplInterface.java | 85 ++++ .../NoAnnotationSubResourceWithoutPath.java | 31 ++ .../security/EagerSecurityContext.java | 21 +- .../security/EagerSecurityHandler.java | 143 +++--- .../EagerSecurityInterceptorHandler.java | 51 +-- .../security/ResourceMethodDescription.java | 31 ++ .../SecurityContextOverrideHandler.java | 22 +- ...erSecurityInterceptorBindingBuildItem.java | 11 + ...erSecurityInterceptorMethodsBuildItem.java | 28 +- .../deployment/HttpSecurityProcessor.java | 8 +- .../processor/ServerEndpointIndexer.java | 103 ++++- .../server/model/ServerResourceMethod.java | 22 + .../spi/ResteasyReactiveResourceInfo.java | 5 + 113 files changed, 4744 insertions(+), 158 deletions(-) create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/AbstractImplMethodSecuredTest.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/DefaultJaxRsDenyAllImplMethodSecuredTest.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/DefaultJaxRsRolesAllowedImplMethodSecuredTest.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/ImplMethodSecuredTest.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/JsonObjectReader.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/SecurityAnnotation.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/SubPaths.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_OnBase_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceInterface_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_PathOnBase_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllSubResourceWithoutPath.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPath_OnBase_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceInterface_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_PathOnBase_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllSubResourceWithoutPath.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPath_OnBase_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPath_OnInterface_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceInterface_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_PathOnBase_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_PathOnInterface_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnBase.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnParent.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedSubResourceWithoutPath.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_First_Interface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_Second_Interface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_Third_Interface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithPath.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithoutPathExtParentRes.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithoutPathImplInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationInterfaceWithPath.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationInterfaceWithoutPath.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithPath.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithoutPath.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithoutPathImplInterface.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationSubResourceWithoutPath.java create mode 100644 extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/ResourceMethodDescription.java diff --git a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index 17f5e2d527b9b..ba6ec3f69909e 100644 --- a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -87,7 +87,6 @@ import org.jboss.resteasy.reactive.common.processor.TargetJavaVersion; import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; -import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationsTransformer; import org.jboss.resteasy.reactive.common.types.AllWriteableMarker; import org.jboss.resteasy.reactive.common.util.Encode; import org.jboss.resteasy.reactive.common.util.types.Types; @@ -104,6 +103,7 @@ import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; import org.jboss.resteasy.reactive.server.model.ServerMethodParameter; import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; +import org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer; import org.jboss.resteasy.reactive.server.processor.generation.converters.GeneratedConverterIndexerExtension; import org.jboss.resteasy.reactive.server.processor.generation.exceptionmappers.ServerExceptionMapperGenerator; import org.jboss.resteasy.reactive.server.processor.generation.injection.TransformedFieldInjectionIndexerExtension; @@ -521,14 +521,6 @@ public void accept(EndpointIndexer.ResourceMethodCallbackEntry entry) { + method.declaringClass() + "[" + method + "]"; - ClassInfo classInfoWithSecurity = consumeStandardSecurityAnnotations(method, - entry.getActualEndpointInfo(), index, c -> c); - if (classInfoWithSecurity != null) { - reflectiveClassBuildItemBuildProducer.produce( - ReflectiveClassBuildItem.builder(entry.getActualEndpointInfo().name().toString()) - .constructors(false).methods().build()); - } - if (!result.getPossibleSubResources().containsKey(method.returnType().name())) { reflectiveHierarchy.produce(ReflectiveHierarchyBuildItem .builder(method.returnType()) @@ -1520,21 +1512,42 @@ MethodScannerBuildItem integrateEagerSecurity(Capabilities capabilities, Combine @Override public List scan(MethodInfo method, ClassInfo actualEndpointClass, Map methodContext) { - if (applySecurityInterceptors && interceptedMethods.contains(method)) { - return List.of(EagerSecurityInterceptorHandler.Customizer.newInstance(), - EagerSecurityHandler.Customizer.newInstance(false)); - } else { - return List.of(newEagerSecurityHandlerCustomizerInstance(method, actualEndpointClass, index, - withDefaultSecurityCheck)); + var endpointImpl = ServerEndpointIndexer.findEndpointImplementation(method, actualEndpointClass, index); + if (applySecurityInterceptors) { + boolean isMethodIntercepted = interceptedMethods.containsKey(endpointImpl); + if (isMethodIntercepted) { + return createEagerSecCustomizerWithInterceptor(interceptedMethods, endpointImpl, method, endpointImpl, + withDefaultSecurityCheck); + } else { + isMethodIntercepted = interceptedMethods.containsKey(method); + if (isMethodIntercepted && !endpointImpl.equals(method)) { + return createEagerSecCustomizerWithInterceptor(interceptedMethods, method, method, endpointImpl, + withDefaultSecurityCheck); + } + } } + return List.of(newEagerSecurityHandlerCustomizerInstance(method, endpointImpl, withDefaultSecurityCheck)); } }); } - private HandlerChainCustomizer newEagerSecurityHandlerCustomizerInstance(MethodInfo method, ClassInfo actualEndpointClass, - IndexView index, boolean withDefaultSecurityCheck) { - if (withDefaultSecurityCheck - || consumeStandardSecurityAnnotations(method, actualEndpointClass, index, (c) -> c) != null) { + private static List createEagerSecCustomizerWithInterceptor( + Map interceptedMethods, MethodInfo method, MethodInfo originalMethod, MethodInfo endpointImpl, + boolean withDefaultSecurityCheck) { + var requiresSecurityCheck = interceptedMethods.get(method); + final HandlerChainCustomizer eagerSecCustomizer; + if (requiresSecurityCheck) { + eagerSecCustomizer = EagerSecurityHandler.Customizer.newInstance(false); + } else { + eagerSecCustomizer = newEagerSecurityHandlerCustomizerInstance(originalMethod, endpointImpl, + withDefaultSecurityCheck); + } + return List.of(EagerSecurityInterceptorHandler.Customizer.newInstance(), eagerSecCustomizer); + } + + private static HandlerChainCustomizer newEagerSecurityHandlerCustomizerInstance(MethodInfo method, MethodInfo endpointImpl, + boolean withDefaultSecurityCheck) { + if (withDefaultSecurityCheck || consumesStandardSecurityAnnotations(method, endpointImpl)) { return EagerSecurityHandler.Customizer.newInstance(false); } return EagerSecurityHandler.Customizer.newInstance(true); @@ -1602,19 +1615,19 @@ void registerSecurityInterceptors(Capabilities capabilities, } } - private T consumeStandardSecurityAnnotations(MethodInfo methodInfo, ClassInfo classInfo, IndexView index, - Function function) { - if (SecurityTransformerUtils.hasStandardSecurityAnnotation(methodInfo)) { - return function.apply(methodInfo.declaringClass()); - } - ClassInfo c = classInfo; - while (c.superName() != null) { - if (SecurityTransformerUtils.hasStandardSecurityAnnotation(c)) { - return function.apply(c); - } - c = index.getClassByName(c.superName()); + private static boolean consumesStandardSecurityAnnotations(MethodInfo methodInfo, MethodInfo endpointImpl) { + // invoked method + if (consumesStandardSecurityAnnotations(endpointImpl)) { + return true; } - return null; + + // fallback to original behavior + return !endpointImpl.equals(methodInfo) && consumesStandardSecurityAnnotations(methodInfo); + } + + private static boolean consumesStandardSecurityAnnotations(MethodInfo methodInfo) { + return SecurityTransformerUtils.hasStandardSecurityAnnotation(methodInfo) + || SecurityTransformerUtils.hasStandardSecurityAnnotation(methodInfo.declaringClass()); } private Optional getAppPath(Optional newPropertyValue) { diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/AbstractImplMethodSecuredTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/AbstractImplMethodSecuredTest.java new file mode 100644 index 0000000000000..696ce00670025 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/AbstractImplMethodSecuredTest.java @@ -0,0 +1,411 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SecurityAnnotation.METHOD_ROLES_ALLOWED; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SecurityAnnotation.NONE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SecurityAnnotation.PATH_SEPARATOR; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.FIRST_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.MULTIPLE_INHERITANCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SECOND_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SECURED_SUB_RESOURCE_ENDPOINT_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.THIRD_INTERFACE; +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import io.quarkus.security.test.utils.TestIdentityController; +import io.quarkus.security.test.utils.TestIdentityProvider; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +/** + * Tests that implementation method is always secured when a standard security annotation is on a class + * or on a class method or when additional method security (like the default JAX-RS security) is in place. + */ +public abstract class AbstractImplMethodSecuredTest { + + protected static QuarkusUnitTest getRunner() { + return getRunner(""); + } + + protected static QuarkusUnitTest getRunner(String applicationProperties) { + return new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addPackage("io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation") + .addPackage("io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed") + .addPackage("io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall") + .addPackage("io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall") + .addPackage("io.quarkus.resteasy.reactive.server.test.security.inheritance.multiple.pathonbase") + .addClasses(TestIdentityProvider.class, TestIdentityController.class, SecurityAnnotation.class, + SubPaths.class, JsonObjectReader.class) + .addAsResource(new StringAsset(applicationProperties + System.lineSeparator()), + "application.properties")); + } + + @BeforeAll + public static void setupUsers() { + TestIdentityController.resetRoles() + .add("admin", "admin", "admin") + .add("user", "user", "user"); + } + + protected boolean denyAllUnannotated() { + return false; + } + + protected String roleRequiredForUnannotatedEndpoint() { + return null; + } + + private void assertPath(String basePath, Object securityAnnotationObj, String classSecurityOn) { + assertPath(basePath, toSecurityAnnotation(securityAnnotationObj), classSecurityOn); + } + + private void assertSecuredSubResourcePath(String basePath) { + + // sub resource locator is not secured, e.g. @Path("sub") public SubResource subResource() { ... } + var path = NONE.assemblePath(basePath) + SECURED_SUB_RESOURCE_ENDPOINT_PATH; + var methodSubPath = NONE.methodSubPath(basePath) + SECURED_SUB_RESOURCE_ENDPOINT_PATH; + + boolean defJaxRsSecurity = denyAllUnannotated() || roleRequiredForUnannotatedEndpoint() != null; + final SecurityAnnotation securityAnnotation; + if (defJaxRsSecurity) { + // subresource locator is not secured, therefore default JAX-RS security wins + securityAnnotation = NONE; + } else { + // sub resource endpoint itself has RolesAllowed, e.g. @RolesAllowed @Path("endpoint") String endpoint() { ... } + securityAnnotation = METHOD_ROLES_ALLOWED; + } + + assertPath(path, methodSubPath, securityAnnotation); + } + + private void assertPath(String basePath, SecurityAnnotation securityAnnotation, String classSecurityOn) { + var path = securityAnnotation.assemblePath(basePath, classSecurityOn); + var methodSubPath = securityAnnotation.methodSubPath(basePath, classSecurityOn); + assertPath(path, methodSubPath, securityAnnotation); + } + + private void assertPath(String basePath, SecurityAnnotation securityAnnotation) { + var path = securityAnnotation.assemblePath(basePath); + var methodSubPath = securityAnnotation.methodSubPath(basePath); + assertPath(path, methodSubPath, securityAnnotation); + } + + private void assertPath(String path, String methodSubPath, SecurityAnnotation securityAnnotation) { + var invalidPayload = "}{\"simple\": \"obj\"}"; + var validPayload = "{\"simple\": \"obj\"}"; + + boolean defJaxRsSecurity = denyAllUnannotated() || roleRequiredForUnannotatedEndpoint() != null; + boolean endpointSecuredWithDefJaxRsSec = defJaxRsSecurity && !securityAnnotation.hasSecurityAnnotation(); + boolean endpointSecured = endpointSecuredWithDefJaxRsSec || securityAnnotation.endpointSecured(); + + // test anonymous - for secured endpoints: unauthenticated + if (endpointSecured) { + given().contentType(ContentType.JSON).body(invalidPayload).post(path).then().statusCode(401); + } else { + given().contentType(ContentType.JSON).body(validPayload).post(path).then().statusCode(200).body(is(methodSubPath)); + } + + // test user - for secured endpoints: unauthorized + if (endpointSecured) { + given().contentType(ContentType.JSON).body(invalidPayload).auth().preemptive().basic("user", "user").post(path) + .then().statusCode(403); + } else { + given().contentType(ContentType.JSON).body(validPayload).auth().preemptive().basic("user", "user").post(path).then() + .statusCode(200).body(is(methodSubPath)); + } + + // test admin - for secured endpoints: authorized + boolean denyAccess = securityAnnotation.denyAll() || (endpointSecuredWithDefJaxRsSec && denyAllUnannotated()); + if (denyAccess) { + given().contentType(ContentType.JSON).body(invalidPayload).auth().preemptive().basic("admin", "admin").post(path) + .then().statusCode(403); + } else { + given().contentType(ContentType.JSON).body(invalidPayload).auth().preemptive().basic("admin", "admin").post(path) + .then().statusCode(500); + given().contentType(ContentType.JSON).body(validPayload).auth().preemptive().basic("admin", "admin").post(path) + .then().statusCode(200).body(is(methodSubPath)); + } + } + + private static void assertNotFound(String basePath) { + var path = NONE.assembleNotFoundPath(basePath); + // this assures that not-tested scenarios are simply not supported by RESTEasy + // should this assertion fail, we need to assure implementation method is secured + given().contentType(ContentType.JSON).body("{\"simple\": \"obj\"}").post(path).then().statusCode(404); + } + + private static SecurityAnnotation toSecurityAnnotation(Object securityAnnotationObj) { + // we use Object due to @EnumSource class loading problems + return SecurityAnnotation.valueOf(securityAnnotationObj.toString()); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnInterface_ImplOnBaseResource_ImplMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + + IMPL_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnResource_ImplOnBaseResource_ImplMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + + IMPL_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @Test + public void test_ClassPathOnParentResource_ImplOnBaseResource_ImplMetWithPath() { + var resourceSubPath = CLASS_PATH_ON_PARENT_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + + IMPL_METHOD_WITH_PATH; + assertNotFound(resourceSubPath); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnInterface_ImplOnBaseResource_InterfaceMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnResource_ImplOnBaseResource_InterfaceMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnParentResource_ImplOnBaseResource_InterfaceMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_PARENT_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @Test + public void test_ClassPathOnInterface_ImplOnBaseResource_ParentMetWithPath() { + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + + PARENT_METHOD_WITH_PATH; + assertNotFound(resourceSubPath); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnResource_ImplOnBaseResource_ParentMetWithPath(Object securityAnnotationObj) { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + var resourceSubPath = CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + + PARENT_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnParentResource_ImplOnBaseResource_ParentMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_PARENT_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + + PARENT_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface(Object securityAnnotationObj) { + // test subresource locator defined on an interface + // @Path("i") + // public interface I { + // @Path("sub") + // @RolesAllowed("admin") + // default SubResource subResource() { + // return new SubResource(); + // } + // } + + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_INTERFACE); + } + + @Test + public void test_ClassPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_SecurityInsideSub() { + // HINT: test security is inside sub resource on an endpoint method + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE; + assertSecuredSubResourcePath(resourceSubPath); + assertSecuredSubResourcePath(resourceSubPath); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnInterface_SubDeclaredOnInterface_SubImplOnParent(Object securityAnnotationObj) { + // HINT: test security for '@Path("sub") SubResource subResource' but not inside endpoints 'SubResource' itself + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_PARENT); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnResource_SubDeclaredOnBase_SubImplOnBase(Object securityAnnotationObj) { + // HINT: test security for '@Path("sub") SubResource subResource' but not inside endpoints 'SubResource' itself + var resourceSubPath = CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_RESOURCE + + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnParentResource_SubDeclaredOnParent_SubImplOnParent(Object securityAnnotationObj) { + // HINT: test security for '@Path("sub") SubResource subResource' but not inside endpoints 'SubResource' itself + var resourceSubPath = CLASS_PATH_ON_PARENT_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_PARENT_RESOURCE + + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_PARENT); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnParentResource_SubDeclaredOnParent_SubImplOnBase(Object securityAnnotationObj) { + // HINT: test security for '@Path("sub") SubResource subResource' but not inside endpoints 'SubResource' itself + var resourceSubPath = CLASS_PATH_ON_PARENT_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_PARENT_RESOURCE + + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_BASE); + } + + @Test + public void test_ClassPathOnInterface_ImplOnParentResource_ImplMetWithPath() { + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH; + assertNotFound(resourceSubPath); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnResource_ImplOnParentResource_ImplMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + + IMPL_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_PARENT); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnParentResource_ImplOnParentResource_ImplMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_PARENT_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + + IMPL_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_PARENT); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnInterface_ImplOnParentResource_InterfaceMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_PARENT); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnResource_ImplOnParentResource_InterfaceMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_PARENT); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnParentResource_ImplOnParentResource_InterfaceMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_PARENT_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_PARENT); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnInterface_ImplOnInterface_ImplMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_INTERFACE + PATH_SEPARATOR + CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + + IMPL_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_INTERFACE); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnResource_ImplOnInterface_ImplMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + + IMPL_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_INTERFACE); + } + + @EnumSource(SecurityAnnotation.class) + @ParameterizedTest + public void test_ClassPathOnParentResource_ImplOnInterface_ImplMetWithPath(Object securityAnnotationObj) { + var resourceSubPath = CLASS_PATH_ON_PARENT_RESOURCE + PATH_SEPARATOR + CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + + IMPL_METHOD_WITH_PATH; + assertPath(resourceSubPath, securityAnnotationObj, CLASS_SECURITY_ON_INTERFACE); + } + + @Test + public void test_MultipleInheritance_ClassPathOnBase_ImplOnBase_ImplWithPath() { + var resourceSubPath = MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + MULTIPLE_INHERITANCE + + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH; + assertPath(resourceSubPath, METHOD_ROLES_ALLOWED); + assertPath(resourceSubPath, NONE); + } + + @Test + public void test_MultipleInheritance_ClassPathOnBase_ImplOnBase_FirstInterface_InterfaceMethodWithPath() { + var resourceSubPath = MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + MULTIPLE_INHERITANCE + + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + SECOND_INTERFACE + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, METHOD_ROLES_ALLOWED); + assertPath(resourceSubPath, NONE); + } + + @Test + public void test_MultipleInheritance_ClassPathOnBase_ImplOnBase_SecondInterface_InterfaceMethodWithPath() { + var resourceSubPath = MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + MULTIPLE_INHERITANCE + + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + FIRST_INTERFACE + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, METHOD_ROLES_ALLOWED); + assertPath(resourceSubPath, NONE); + } + + @Test + public void test_MultipleInheritance_ClassPathOnBase_ImplOnBase_ThirdInterface_InterfaceMethodWithPath() { + var resourceSubPath = MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + MULTIPLE_INHERITANCE + + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, METHOD_ROLES_ALLOWED); + assertPath(resourceSubPath, NONE); + } + + @Test + public void test_MultipleInheritance_ClassPathOnBase_ImplOnInterface_ThirdInterface_InterfaceMethodWithPath() { + var resourceSubPath = MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + PATH_SEPARATOR + MULTIPLE_INHERITANCE + + CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH; + assertPath(resourceSubPath, METHOD_ROLES_ALLOWED); + assertPath(resourceSubPath, NONE); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/DefaultJaxRsDenyAllImplMethodSecuredTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/DefaultJaxRsDenyAllImplMethodSecuredTest.java new file mode 100644 index 0000000000000..2b8a9ed6ef7aa --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/DefaultJaxRsDenyAllImplMethodSecuredTest.java @@ -0,0 +1,16 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class DefaultJaxRsDenyAllImplMethodSecuredTest extends AbstractImplMethodSecuredTest { + + @RegisterExtension + static QuarkusUnitTest runner = getRunner("quarkus.security.jaxrs.deny-unannotated-endpoints=true"); + + @Override + protected boolean denyAllUnannotated() { + return true; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/DefaultJaxRsRolesAllowedImplMethodSecuredTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/DefaultJaxRsRolesAllowedImplMethodSecuredTest.java new file mode 100644 index 0000000000000..f8e5c75099fc0 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/DefaultJaxRsRolesAllowedImplMethodSecuredTest.java @@ -0,0 +1,16 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class DefaultJaxRsRolesAllowedImplMethodSecuredTest extends AbstractImplMethodSecuredTest { + + @RegisterExtension + static QuarkusUnitTest runner = getRunner("quarkus.security.jaxrs.default-roles-allowed=admin"); + + @Override + protected String roleRequiredForUnannotatedEndpoint() { + return "admin"; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/ImplMethodSecuredTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/ImplMethodSecuredTest.java new file mode 100644 index 0000000000000..58eb93333f515 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/ImplMethodSecuredTest.java @@ -0,0 +1,12 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class ImplMethodSecuredTest extends AbstractImplMethodSecuredTest { + + @RegisterExtension + static QuarkusUnitTest runner = getRunner(); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/JsonObjectReader.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/JsonObjectReader.java new file mode 100644 index 0000000000000..116d80955d9b4 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/JsonObjectReader.java @@ -0,0 +1,56 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; +import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyReader; +import org.jboss.resteasy.reactive.server.spi.ServerRequestContext; + +import io.vertx.core.json.JsonObject; + +@Provider +@Consumes(MediaType.APPLICATION_JSON) +public class JsonObjectReader implements ServerMessageBodyReader { + + @Override + public boolean isReadable(Class type, Type genericType, ResteasyReactiveResourceInfo lazyMethod, MediaType mediaType) { + return true; + } + + @Override + public JsonObject readFrom(Class type, Type genericType, MediaType mediaType, ServerRequestContext context) + throws WebApplicationException, IOException { + return readFrom(context.getInputStream()); + } + + @Override + public boolean isReadable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + return true; + } + + @Override + public JsonObject readFrom(Class aClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap multivaluedMap, InputStream inputStream) + throws IOException, WebApplicationException { + return readFrom(inputStream); + } + + private JsonObject readFrom(InputStream inputStream) { + try { + String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + return new JsonObject(json); + } catch (Exception e) { + throw new RuntimeException("Unable to parse JsonObject.", e); + } + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/SecurityAnnotation.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/SecurityAnnotation.java new file mode 100644 index 0000000000000..1f873567f7b02 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/SecurityAnnotation.java @@ -0,0 +1,77 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance; + +import jakarta.ws.rs.Path; + +public enum SecurityAnnotation { + NONE(SubPaths.NO_SECURITY_ANNOTATION, false, null, false), + METHOD_ROLES_ALLOWED(SubPaths.METHOD_ROLES_ALLOWED, false, "admin", false), + METHOD_DENY_ALL(SubPaths.METHOD_DENY_ALL, true, null, false), + METHOD_PERMIT_ALL(SubPaths.METHOD_PERMIT_ALL, false, null, false), + CLASS_ROLES_ALLOWED(SubPaths.CLASS_ROLES_ALLOWED, false, "admin", true), + CLASS_DENY_ALL(SubPaths.CLASS_DENY_ALL, true, null, true), + CLASS_PERMIT_ALL(SubPaths.CLASS_PERMIT_ALL, false, null, true), + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL(SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL, false, null, true), + // class is annotated with the @DenyAll, but method level annotation must have priority, therefore we set denyAll=false + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED(SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED, false, "admin", true), + CLASS_DENY_ALL_METHOD_PERMIT_ALL(SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL, false, null, true); + + static final String PATH_SEPARATOR = "/"; + + private final SubPaths.SubPath subPath; + private final String allowedRole; + private final boolean isClassSecurityAnnotation; + private final boolean denyAll; + + SecurityAnnotation(SubPaths.SubPath subPath, boolean denyAll, String allowedRole, boolean isClassSecurityAnnotation) { + this.subPath = subPath; + this.denyAll = denyAll; + this.allowedRole = allowedRole; + this.isClassSecurityAnnotation = isClassSecurityAnnotation; + } + + private String toSecurityAnnInfix(String classSecurityOn) { + return isClassSecurityAnnotation ? classSecurityOn : ""; + } + + boolean hasSecurityAnnotation() { + return this != NONE; + } + + boolean denyAll() { + return denyAll; + } + + boolean endpointSecured() { + return denyAll || allowedRole != null; + } + + /** + * @param basePath path common for all {@link this} annotations + * @param classSecurityOn whether class-level annotation is on interface, parent or base + * @return request path + */ + String assemblePath(String basePath, String classSecurityOn) { + return subPath.classSubPathPrefix() + toSecurityAnnInfix(classSecurityOn) + basePath + subPath.methodSubPath(); + } + + String assemblePath(String basePath) { + return subPath.classSubPathPrefix() + basePath + subPath.methodSubPath(); + } + + String assembleNotFoundPath(String basePath) { + return subPath.classSubPathPrefix() + basePath; + } + + /** + * @return endpoint method-level {@link Path#value()} + */ + String methodSubPath(String basePath, String classSecurityOn) { + var path = assemblePath(basePath, classSecurityOn); + return path.substring(path.indexOf(PATH_SEPARATOR, 1) + 1); + } + + String methodSubPath(String basePath) { + var path = assemblePath(basePath); + return path.substring(path.indexOf(PATH_SEPARATOR, 1) + 1); + } +} \ No newline at end of file diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/SubPaths.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/SubPaths.java new file mode 100644 index 0000000000000..5b59022976c3a --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/SubPaths.java @@ -0,0 +1,88 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance; + +public interface SubPaths { + + record SubPath(String classSubPathPrefix, String methodSubPath) { + } + + String CLASS_PATH_ON_INTERFACE = "class-path-on-interface"; + String CLASS_PATH_ON_RESOURCE = "class-path-on-resource"; + String CLASS_PATH_ON_PARENT_RESOURCE = "class-path-on-parent-resource"; + + String CLASS_SECURITY_ON_BASE = "class-security-on-base-"; + String CLASS_SECURITY_ON_PARENT = "class-security-on-parent-"; + String CLASS_SECURITY_ON_INTERFACE = "class-security-on-interface-"; + + String IMPL_ON_BASE = "/impl-on-base-resource"; + String IMPL_ON_PARENT = "/impl-on-parent-resource"; + /** + * Interface that sits on the top of a resource class hierarchy. + */ + String IMPL_ON_INTERFACE = "/impl-on-interface"; + String SUB_DECLARED_ON = "/sub-resource-declared-on-"; + + /** + * Following 3 constants refer to where method like {@code @Path("sub") SubResource subResource} with JAX-RS + * sub-resource declaring annotations are declared. + */ + String SUB_DECLARED_ON_INTERFACE = SUB_DECLARED_ON + "interface"; + String SUB_DECLARED_ON_BASE = SUB_DECLARED_ON + "base"; + String SUB_DECLARED_ON_PARENT = SUB_DECLARED_ON + "parent"; + + String SECURED_SUB_RESOURCE_ENDPOINT_PATH = "/secured"; + + /** + * Following 3 constants refer to where method like {@code @Override SubResource subResource() { return new SubResource(); + * }} + * is implemented. That is whether actually invoked sub-resource endpoint is placed on a base, parent or an interface. + */ + String SUB_IMPL_ON_BASE = "/sub-impl-on-base"; + String SUB_IMPL_ON_PARENT = "/sub-impl-on-parent"; + String SUB_IMPL_ON_INTERFACE = "/sub-impl-on-interface"; + + String IMPL_METHOD_WITH_PATH = "/impl-met-with-path"; + String PARENT_METHOD_WITH_PATH = "/parent-met-with-path"; + String INTERFACE_METHOD_WITH_PATH = "/interface-met-with-path"; + + String CLASS_NO_ANNOTATION_PREFIX = "/class-no-annotation-"; + String CLASS_ROLES_ALLOWED_PREFIX = "/class-roles-allowed-"; + String CLASS_DENY_ALL_PREFIX = "/class-deny-all-"; + String CLASS_PERMIT_ALL_PREFIX = "/class-permit-all-"; + + String NO_SECURITY_ANNOTATION_PATH = "/no-security-annotation"; + String METHOD_ROLES_ALLOWED_PATH = "/method-roles-allowed"; + String METHOD_DENY_ALL_PATH = "/method-deny-all"; + String METHOD_PERMIT_ALL_PATH = "/method-permit-all"; + String CLASS_ROLES_ALLOWED_PATH = "/class-roles-allowed"; + String CLASS_DENY_ALL_PATH = "/class-deny-all"; + String CLASS_PERMIT_ALL_PATH = "/class-permit-all"; + String CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH = "/class-deny-all-method-roles-allowed"; + String CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH = "/class-deny-all-method-permit-all"; + String CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH = "/class-permit-all-method-permit-all"; + + String MULTIPLE_INHERITANCE = "multiple-inheritance-"; + + /** + * Interface implemented by a base/parent resource. + */ + String FIRST_INTERFACE = "/first-interface"; + /** + * Interface that extends {@link #FIRST_INTERFACE}. + */ + String SECOND_INTERFACE = "/second-interface"; + /** + * Interface that extends {@link #SECOND_INTERFACE}. + */ + String THIRD_INTERFACE = "/third-interface"; + + SubPath NO_SECURITY_ANNOTATION = new SubPath(CLASS_NO_ANNOTATION_PREFIX, NO_SECURITY_ANNOTATION_PATH); + SubPath METHOD_ROLES_ALLOWED = new SubPath(CLASS_NO_ANNOTATION_PREFIX, METHOD_ROLES_ALLOWED_PATH); + SubPath METHOD_DENY_ALL = new SubPath(CLASS_NO_ANNOTATION_PREFIX, METHOD_DENY_ALL_PATH); + SubPath METHOD_PERMIT_ALL = new SubPath(CLASS_NO_ANNOTATION_PREFIX, METHOD_PERMIT_ALL_PATH); + SubPath CLASS_ROLES_ALLOWED = new SubPath(CLASS_ROLES_ALLOWED_PREFIX, CLASS_ROLES_ALLOWED_PATH); + SubPath CLASS_DENY_ALL = new SubPath(CLASS_DENY_ALL_PREFIX, CLASS_DENY_ALL_PATH); + SubPath CLASS_PERMIT_ALL = new SubPath(CLASS_PERMIT_ALL_PREFIX, CLASS_PERMIT_ALL_PATH); + SubPath CLASS_DENY_ALL_METHOD_ROLES_ALLOWED = new SubPath(CLASS_DENY_ALL_PREFIX, CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH); + SubPath CLASS_DENY_ALL_METHOD_PERMIT_ALL = new SubPath(CLASS_DENY_ALL_PREFIX, CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH); + SubPath CLASS_PERMIT_ALL_METHOD_PERMIT_ALL = new SubPath(CLASS_PERMIT_ALL_PREFIX, CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH); +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_OnBase_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_OnBase_SecurityOnParent.java new file mode 100644 index 0000000000000..bf4c06dcb594f --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_OnBase_SecurityOnParent.java @@ -0,0 +1,17 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_RESOURCE) +public class ClassDenyAllBaseResourceWithPath_OnBase_SecurityOnParent + extends ClassDenyAllParentResourceWithoutPath_PathOnBase_SecurityOnParent { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..9e272d62cf534 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_SecurityOnBase.java @@ -0,0 +1,106 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@DenyAll +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_RESOURCE) +public class ClassDenyAllBaseResourceWithPath_SecurityOnBase extends ClassDenyAllParentResourceWithoutPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_ClassDenyAllPath(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_ClassDenyAllMethodPermitAllPath(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_ClassDenyAllMethodRolesAllowedPath(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @PermitAll + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_PATH) + public ClassDenyAllSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_ClassDenyAll() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + public ClassDenyAllSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_ClassDenyAllMethodPermitAll() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH); + } + + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + public ClassDenyAllSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_ClassDenyAllMethodRolesAllowed() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..8f8357a914421 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithPath_SecurityOnInterface.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_RESOURCE) +public class ClassDenyAllBaseResourceWithPath_SecurityOnInterface + extends ClassDenyAllParentResourceWithoutPath_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnBase.java new file mode 100644 index 0000000000000..b8b89e479d9d3 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnBase.java @@ -0,0 +1,81 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; + +import io.vertx.core.json.JsonObject; + +@DenyAll +public class ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnBase + extends ClassDenyAllParentResourceWithPath_SecurityOnBase { + + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed( + JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassDenyAll() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_PATH); + } + + @PermitAll + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassDenyAllMethodPermitAll() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH); + } + + @RolesAllowed("admin") + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassDenyAllMethodRolesAllowed() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java new file mode 100644 index 0000000000000..35d7af12b55d9 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java @@ -0,0 +1,5 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +public class ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface + extends ClassDenyAllParentResourceWithPath_SecurityOnInterface { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnParent.java new file mode 100644 index 0000000000000..b897716a955c8 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnParent.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +public class ClassDenyAllBaseResourceWithoutPathExtParentRes_SecurityOnParent + extends ClassDenyAllParentResourceWithPath_SecurityOnParent { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnBase.java new file mode 100644 index 0000000000000..7be4efeda9d90 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnBase.java @@ -0,0 +1,76 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; + +import io.vertx.core.json.JsonObject; + +@DenyAll +public class ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnBase + implements ClassDenyAllInterfaceWithPath_SecurityOnBase { + + @Override + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @PermitAll + @Override + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @PermitAll + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassDenyAll() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_PATH); + } + + @PermitAll + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassDenyAllMethodPermitAll() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH); + } + + @RolesAllowed("admin") + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassDenyAllMethodRolesAllowed() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnInterface.java new file mode 100644 index 0000000000000..fb3a57ee88616 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnInterface.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +// must always be here as interface needs an implementing class, otherwise is ignored +public class ClassDenyAllBaseResourceWithoutPathImplInterface_SecurityOnInterface + implements ClassDenyAllInterfaceWithPath_SecurityOnInterface { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPath_SecurityOnParent.java new file mode 100644 index 0000000000000..6695cecdbe129 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllBaseResourceWithoutPath_SecurityOnParent.java @@ -0,0 +1,5 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +public class ClassDenyAllBaseResourceWithoutPath_SecurityOnParent + extends ClassDenyAllParentResourceWithoutPath_SecurityOnParent { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..f7a34dc71ee52 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnBase.java @@ -0,0 +1,61 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_INTERFACE) +public interface ClassDenyAllInterfaceWithPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_PATH) + ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassDenyAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassDenyAllMethodPermitAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassDenyAllMethodRolesAllowed(); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..e16e6a8eecebd --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnInterface.java @@ -0,0 +1,72 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_INTERFACE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@DenyAll +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_INTERFACE) +public interface ClassDenyAllInterfaceWithPath_SecurityOnInterface { + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + CLASS_DENY_ALL_PATH) + default ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_ClassDenyAll() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_INTERFACE + CLASS_DENY_ALL_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + default ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_ClassDenyAllMethodPermitAll() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_INTERFACE + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH); + } + + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + default ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_ClassDenyAllMethodRolesAllowed() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_INTERFACE + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH); + } + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnParent.java new file mode 100644 index 0000000000000..ca5356357dcc9 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithPath_SecurityOnParent.java @@ -0,0 +1,50 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_INTERFACE) +public interface ClassDenyAllInterfaceWithPath_SecurityOnParent { + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + CLASS_DENY_ALL_PATH) + ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassDenyAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassDenyAllMethodPermitAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassDenyAllMethodRolesAllowed(); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java new file mode 100644 index 0000000000000..07b3e167b29da --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java @@ -0,0 +1,44 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@DenyAll +public interface ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_ClassDenyAllMethodRolesAllowed( + JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnParent.java new file mode 100644 index 0000000000000..bf98bbbd9cc60 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnParent.java @@ -0,0 +1,30 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnParent { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnBase.java new file mode 100644 index 0000000000000..3f8606bd00861 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnBase.java @@ -0,0 +1,29 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassDenyAllInterfaceWithoutPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..ab1a58ca44f5c --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnInterface.java @@ -0,0 +1,41 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@DenyAll +public interface ClassDenyAllInterfaceWithoutPath_SecurityOnInterface { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnParent.java new file mode 100644 index 0000000000000..abeede37b851b --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllInterfaceWithoutPath_SecurityOnParent.java @@ -0,0 +1,29 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassDenyAllInterfaceWithoutPath_SecurityOnParent { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceInterface_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceInterface_SecurityOnBase.java new file mode 100644 index 0000000000000..81d466dd71d84 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceInterface_SecurityOnBase.java @@ -0,0 +1,29 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassDenyAllParentResourceInterface_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..c1d0c7f073f07 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnBase.java @@ -0,0 +1,52 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassDenyAllParentResourceWithPath_SecurityOnBase + implements ClassDenyAllParentResourceInterface_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassDenyAllMethodPermitAll( + JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassDenyAllMethodRolesAllowed( + JsonObject array); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + CLASS_DENY_ALL_PATH) + public abstract ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassDenyAll(); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + public abstract ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassDenyAllMethodPermitAll(); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + public abstract ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassDenyAllMethodRolesAllowed(); +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..0fbd10b5c7237 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnInterface.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassDenyAllParentResourceWithPath_SecurityOnInterface + implements ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnParent.java new file mode 100644 index 0000000000000..94e4a87e4d7f5 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithPath_SecurityOnParent.java @@ -0,0 +1,97 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@DenyAll +@Path(CLASS_DENY_ALL_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassDenyAllParentResourceWithPath_SecurityOnParent + implements ClassDenyAllInterfaceWithoutPath_PathOnParent_SecurityOnParent { + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + CLASS_DENY_ALL_PATH) + public ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParent_ClassDenyAll() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + CLASS_DENY_ALL_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + public ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParent_ClassDenyAllMethodPermitAll() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH); + } + + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + public ClassDenyAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParent_ClassDenyAllMethodRolesAllowed() { + return new ClassDenyAllSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH); + } + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @RolesAllowed("admin") + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed( + JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_PathOnBase_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_PathOnBase_SecurityOnParent.java new file mode 100644 index 0000000000000..e91ada6657001 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_PathOnBase_SecurityOnParent.java @@ -0,0 +1,59 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@DenyAll +public abstract class ClassDenyAllParentResourceWithoutPath_PathOnBase_SecurityOnParent + implements ClassDenyAllInterfaceWithoutPath_SecurityOnParent { + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } + + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnBase.java new file mode 100644 index 0000000000000..69baeb44a2ffd --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnBase.java @@ -0,0 +1,32 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public abstract class ClassDenyAllParentResourceWithoutPath_SecurityOnBase + implements ClassDenyAllInterfaceWithoutPath_SecurityOnBase { + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassDenyAll(JsonObject array); + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassDenyAllMethodPermitAll( + JsonObject array); + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassDenyAllMethodRolesAllowed( + JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..3f6e4071fb194 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnInterface.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +public abstract class ClassDenyAllParentResourceWithoutPath_SecurityOnInterface + implements ClassDenyAllInterfaceWithoutPath_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnParent.java new file mode 100644 index 0000000000000..9dce001e56bc2 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllParentResourceWithoutPath_SecurityOnParent.java @@ -0,0 +1,58 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; + +import io.vertx.core.json.JsonObject; + +@DenyAll +public abstract class ClassDenyAllParentResourceWithoutPath_SecurityOnParent + implements ClassDenyAllInterfaceWithPath_SecurityOnParent { + + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassDenyAll() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + CLASS_DENY_ALL_PATH); + } + + @PermitAll + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassDenyAllMethodPermitAll() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH); + } + + @RolesAllowed("admin") + @Override + public ClassDenyAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassDenyAllMethodRolesAllowed() { + return new ClassDenyAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH); + } + + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassDenyAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassDenyAllMethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_DENY_ALL_METHOD_ROLES_ALLOWED_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllSubResourceWithoutPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllSubResourceWithoutPath.java new file mode 100644 index 0000000000000..9cd6ecb11d6ae --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classdenyall/ClassDenyAllSubResourceWithoutPath.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classdenyall; + +import jakarta.ws.rs.POST; + +import io.vertx.core.json.JsonObject; + +public class ClassDenyAllSubResourceWithoutPath { + + private final String subResourcePath; + + public ClassDenyAllSubResourceWithoutPath(String subResourcePath) { + this.subResourcePath = subResourcePath; + } + + @POST + public String post(JsonObject array) { + return subResourcePath; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..03b26d6c04588 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithPath_SecurityOnBase.java @@ -0,0 +1,77 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.PermitAll; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@PermitAll +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_RESOURCE) +public class ClassPermitAllBaseResourceWithPath_SecurityOnBase extends ClassPermitAllParentResourceWithoutPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_ClassPermitAllPath(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_ClassPermitAllMethodPermitAllPath(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_PATH) + public ClassPermitAllSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_ClassPermitAll() { + return new ClassPermitAllSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + public ClassPermitAllSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_ClassPermitAllMethodPermitAll() { + return new ClassPermitAllSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..9840d54373cf4 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithPath_SecurityOnInterface.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_RESOURCE) +public class ClassPermitAllBaseResourceWithPath_SecurityOnInterface + extends ClassPermitAllParentResourceWithoutPath_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnBase.java new file mode 100644 index 0000000000000..960f4388fb3e6 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnBase.java @@ -0,0 +1,55 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.PermitAll; + +import io.vertx.core.json.JsonObject; + +@PermitAll +public class ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnBase + extends ClassPermitAllParentResourceWithPath_SecurityOnBase { + + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public ClassPermitAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassPermitAll() { + return new ClassPermitAllSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_PATH); + } + + @PermitAll + @Override + public ClassPermitAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassPermitAllMethodPermitAll() { + return new ClassPermitAllSubResourceWithoutPath(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java new file mode 100644 index 0000000000000..683659e1fe37b --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java @@ -0,0 +1,5 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +public class ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnInterface + extends ClassPermitAllParentResourceWithPath_SecurityOnInterface { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnParent.java new file mode 100644 index 0000000000000..8866b4075f938 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnParent.java @@ -0,0 +1,5 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +public class ClassPermitAllBaseResourceWithoutPathExtParentRes_SecurityOnParent + extends ClassPermitAllParentResourceWithPath_SecurityOnParent { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnBase.java new file mode 100644 index 0000000000000..1f7f8919c172d --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnBase.java @@ -0,0 +1,54 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.PermitAll; + +import io.vertx.core.json.JsonObject; + +@PermitAll +public class ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnBase + implements ClassPermitAllInterfaceWithPath_SecurityOnBase { + + @Override + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassPermitAll() { + return new ClassPermitAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_PATH); + } + + @PermitAll + @Override + public ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassPermitAllMethodPermitAll() { + return new ClassPermitAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnInterface.java new file mode 100644 index 0000000000000..97527ba1daeac --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnInterface.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +// must always be here as interface needs an implementing class, otherwise is ignored +public class ClassPermitAllBaseResourceWithoutPathImplInterface_SecurityOnInterface + implements ClassPermitAllInterfaceWithPath_SecurityOnInterface { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPath_OnBase_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPath_OnBase_SecurityOnParent.java new file mode 100644 index 0000000000000..38e65f19d42ca --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPath_OnBase_SecurityOnParent.java @@ -0,0 +1,17 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_RESOURCE) +public class ClassPermitAllBaseResourceWithoutPath_OnBase_SecurityOnParent + extends ClassPermitAllParentResourceWithoutPath_PathOnBase_SecurityOnParent { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPath_SecurityOnParent.java new file mode 100644 index 0000000000000..9f1dd4791ae9a --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllBaseResourceWithoutPath_SecurityOnParent.java @@ -0,0 +1,5 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +public class ClassPermitAllBaseResourceWithoutPath_SecurityOnParent + extends ClassPermitAllParentResourceWithoutPath_SecurityOnParent { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..d33be2713903f --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnBase.java @@ -0,0 +1,48 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_INTERFACE) +public interface ClassPermitAllInterfaceWithPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_PATH) + ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassPermitAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassPermitAllMethodPermitAll(); +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..91948c6e72353 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnInterface.java @@ -0,0 +1,54 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_INTERFACE; + +import jakarta.annotation.security.PermitAll; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@PermitAll +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_INTERFACE) +public interface ClassPermitAllInterfaceWithPath_SecurityOnInterface { + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + CLASS_PERMIT_ALL_PATH) + default ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_ClassPermitAll() { + return new ClassPermitAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_INTERFACE + CLASS_PERMIT_ALL_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + default ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_ClassPermitAllMethodPermitAll() { + return new ClassPermitAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_INTERFACE + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH); + } + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnParent.java new file mode 100644 index 0000000000000..84a26c5dd2078 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithPath_SecurityOnParent.java @@ -0,0 +1,41 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_INTERFACE) +public interface ClassPermitAllInterfaceWithPath_SecurityOnParent { + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + CLASS_PERMIT_ALL_PATH) + ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassPermitAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassPermitAllMethodPermitAll(); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java new file mode 100644 index 0000000000000..030361db73e1c --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java @@ -0,0 +1,32 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; + +import jakarta.annotation.security.PermitAll; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@PermitAll +public interface ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_ClassPermitAllMethodPermitAll( + JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnParent.java new file mode 100644 index 0000000000000..5c510227a4385 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnParent.java @@ -0,0 +1,24 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnParent { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnBase.java new file mode 100644 index 0000000000000..0f279ac25e745 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnBase.java @@ -0,0 +1,24 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassPermitAllInterfaceWithoutPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..60e5b15135469 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnInterface.java @@ -0,0 +1,31 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; + +import jakarta.annotation.security.PermitAll; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@PermitAll +public interface ClassPermitAllInterfaceWithoutPath_SecurityOnInterface { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnParent.java new file mode 100644 index 0000000000000..b915da867fffc --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllInterfaceWithoutPath_SecurityOnParent.java @@ -0,0 +1,24 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassPermitAllInterfaceWithoutPath_SecurityOnParent { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceInterface_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceInterface_SecurityOnBase.java new file mode 100644 index 0000000000000..a0f17b02bde29 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceInterface_SecurityOnBase.java @@ -0,0 +1,24 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassPermitAllParentResourceInterface_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..a9c404924ba99 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnBase.java @@ -0,0 +1,41 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassPermitAllParentResourceWithPath_SecurityOnBase + implements ClassPermitAllParentResourceInterface_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassPermitAllMethodPermitAll( + JsonObject array); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_PATH) + public abstract ClassPermitAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassPermitAll(); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + public abstract ClassPermitAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBase_ClassPermitAllMethodPermitAll(); +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..add1cbbd5ed24 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnInterface.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassPermitAllParentResourceWithPath_SecurityOnInterface + implements ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnParent.java new file mode 100644 index 0000000000000..35665a7bda1f3 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithPath_SecurityOnParent.java @@ -0,0 +1,69 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.PermitAll; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@PermitAll +@Path(CLASS_PERMIT_ALL_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassPermitAllParentResourceWithPath_SecurityOnParent + implements ClassPermitAllInterfaceWithoutPath_PathOnParent_SecurityOnParent { + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + CLASS_PERMIT_ALL_PATH) + public ClassPermitAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParent_ClassPermitAll() { + return new ClassPermitAllSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + CLASS_PERMIT_ALL_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + public ClassPermitAllSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParent_ClassPermitAllMethodPermitAll() { + return new ClassPermitAllSubResourceWithoutPath(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + + SUB_IMPL_ON_PARENT + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH); + } + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll( + JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_PathOnBase_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_PathOnBase_SecurityOnParent.java new file mode 100644 index 0000000000000..a715de44641e3 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_PathOnBase_SecurityOnParent.java @@ -0,0 +1,42 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.annotation.security.PermitAll; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@PermitAll +public abstract class ClassPermitAllParentResourceWithoutPath_PathOnBase_SecurityOnParent + implements ClassPermitAllInterfaceWithoutPath_SecurityOnParent { + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnBase.java new file mode 100644 index 0000000000000..859be9c57786b --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnBase.java @@ -0,0 +1,26 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public abstract class ClassPermitAllParentResourceWithoutPath_SecurityOnBase + implements ClassPermitAllInterfaceWithoutPath_SecurityOnBase { + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassPermitAll(JsonObject array); + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassPermitAllMethodPermitAll( + JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..0618a94e56b9e --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnInterface.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +public abstract class ClassPermitAllParentResourceWithoutPath_SecurityOnInterface + implements ClassPermitAllInterfaceWithoutPath_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnParent.java new file mode 100644 index 0000000000000..168ac6dd945dd --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllParentResourceWithoutPath_SecurityOnParent.java @@ -0,0 +1,42 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.PermitAll; + +import io.vertx.core.json.JsonObject; + +@PermitAll +public abstract class ClassPermitAllParentResourceWithoutPath_SecurityOnParent + implements ClassPermitAllInterfaceWithPath_SecurityOnParent { + + @Override + public ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassPermitAll() { + return new ClassPermitAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + CLASS_PERMIT_ALL_PATH); + } + + @PermitAll + @Override + public ClassPermitAllSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassPermitAllMethodPermitAll() { + return new ClassPermitAllSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH); + } + + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassPermitAllMethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_PERMIT_ALL_METHOD_PERMIT_ALL_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllSubResourceWithoutPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllSubResourceWithoutPath.java new file mode 100644 index 0000000000000..39adbb89f718b --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classpermitall/ClassPermitAllSubResourceWithoutPath.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classpermitall; + +import jakarta.ws.rs.POST; + +import io.vertx.core.json.JsonObject; + +public class ClassPermitAllSubResourceWithoutPath { + + private final String subResourcePath; + + public ClassPermitAllSubResourceWithoutPath(String subResourcePath) { + this.subResourcePath = subResourcePath; + } + + @POST + public String post(JsonObject array) { + return subResourcePath; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..c719605cbd0ac --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnBase.java @@ -0,0 +1,51 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@RolesAllowed("admin") +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_RESOURCE) +public class ClassRolesAllowedBaseResourceWithPath_SecurityOnBase + extends ClassRolesAllowedParentResourceWithoutPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_ClassRolesAllowedPath(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_ROLES_ALLOWED_PATH) + public ClassRolesAllowedSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_ClassRolesAllowed() { + return new ClassRolesAllowedSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + CLASS_ROLES_ALLOWED_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..d25424b67c671 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnInterface.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_RESOURCE) +public class ClassRolesAllowedBaseResourceWithPath_SecurityOnInterface + extends ClassRolesAllowedParentResourceWithoutPath_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnParent.java new file mode 100644 index 0000000000000..37d0b6cbfd5b1 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithPath_SecurityOnParent.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_RESOURCE) +public class ClassRolesAllowedBaseResourceWithPath_SecurityOnParent + extends ClassRolesAllowedParentResourceWithoutPath_SecurityOnParent { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnBase.java new file mode 100644 index 0000000000000..a65c5cebbf49d --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnBase.java @@ -0,0 +1,34 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.RolesAllowed; + +import io.vertx.core.json.JsonObject; + +@RolesAllowed("admin") +public class ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnBase + extends ClassRolesAllowedParentResourceWithPath_SecurityOnBase { + + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + + @Override + public ClassRolesAllowedSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_ClassRolesAllowed() { + return new ClassRolesAllowedSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + CLASS_ROLES_ALLOWED_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java new file mode 100644 index 0000000000000..9114037a2f445 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnInterface.java @@ -0,0 +1,5 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +public class ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnInterface + extends ClassRolesAllowedParentResourceWithPath_SecurityOnInterface { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnParent.java new file mode 100644 index 0000000000000..d515c5dcbaada --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnParent.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +public class ClassRolesAllowedBaseResourceWithoutPathExtParentRes_SecurityOnParent + extends ClassRolesAllowedParentResourceWithPath_SecurityOnParent { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnBase.java new file mode 100644 index 0000000000000..279978230e72b --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnBase.java @@ -0,0 +1,35 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.RolesAllowed; + +import io.vertx.core.json.JsonObject; + +@RolesAllowed("admin") +public class ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnBase + implements ClassRolesAllowedInterfaceWithPath_SecurityOnBase { + + @Override + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + + @Override + public ClassRolesAllowedSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassRolesAllowed() { + return new ClassRolesAllowedSubResourceWithoutPath( + CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_BASE + CLASS_ROLES_ALLOWED_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnInterface.java new file mode 100644 index 0000000000000..356a2a6cc3491 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnInterface.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +// must always be here as interface needs an implementing class, otherwise is ignored +public class ClassRolesAllowedBaseResourceWithoutPathImplInterface_SecurityOnInterface + implements ClassRolesAllowedInterfaceWithPath_SecurityOnInterface { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPath_OnBase_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPath_OnBase_SecurityOnParent.java new file mode 100644 index 0000000000000..97ba87896da25 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPath_OnBase_SecurityOnParent.java @@ -0,0 +1,17 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_RESOURCE) +public class ClassRolesAllowedBaseResourceWithoutPath_OnBase_SecurityOnParent + extends ClassRolesAllowedParentResourceWithoutPath_PathOnBase_SecurityOnParent { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPath_OnInterface_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPath_OnInterface_SecurityOnParent.java new file mode 100644 index 0000000000000..90918462b55b8 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedBaseResourceWithoutPath_OnInterface_SecurityOnParent.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +public class ClassRolesAllowedBaseResourceWithoutPath_OnInterface_SecurityOnParent + extends ClassRolesAllowedParentResourceWithoutPath_PathOnInterface_SecurityOnParent { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..67505331550dc --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnBase.java @@ -0,0 +1,36 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_INTERFACE) +public interface ClassRolesAllowedInterfaceWithPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_ClassRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + CLASS_ROLES_ALLOWED_PATH) + ClassRolesAllowedSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_ClassRolesAllowed(); +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..d13487fe82145 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnInterface.java @@ -0,0 +1,40 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_INTERFACE; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@RolesAllowed("admin") +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_INTERFACE) +public interface ClassRolesAllowedInterfaceWithPath_SecurityOnInterface { + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + CLASS_ROLES_ALLOWED_PATH) + default ClassRolesAllowedSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_ClassRolesAllowed() { + return new ClassRolesAllowedSubResourceWithoutPath( + CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_INTERFACE + CLASS_ROLES_ALLOWED_PATH); + } + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnParent.java new file mode 100644 index 0000000000000..92d69b6001782 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithPath_SecurityOnParent.java @@ -0,0 +1,32 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_INTERFACE) +public interface ClassRolesAllowedInterfaceWithPath_SecurityOnParent { + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + CLASS_ROLES_ALLOWED_PATH) + ClassRolesAllowedSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassRolesAllowed(); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java new file mode 100644 index 0000000000000..a8d03bd65bf45 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnInterface.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@RolesAllowed("admin") +public interface ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnInterface { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnParent.java new file mode 100644 index 0000000000000..a3bc5ebbcd188 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnParent.java @@ -0,0 +1,19 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnParent { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnBase.java new file mode 100644 index 0000000000000..4ab1356a2850c --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnBase.java @@ -0,0 +1,19 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassRolesAllowedInterfaceWithoutPath_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..aba095ba096a0 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnInterface.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@RolesAllowed("admin") +public interface ClassRolesAllowedInterfaceWithoutPath_SecurityOnInterface { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnParent.java new file mode 100644 index 0000000000000..0ecd888de0327 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedInterfaceWithoutPath_SecurityOnParent.java @@ -0,0 +1,19 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassRolesAllowedInterfaceWithoutPath_SecurityOnParent { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceInterface_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceInterface_SecurityOnBase.java new file mode 100644 index 0000000000000..d45cc4b5a3686 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceInterface_SecurityOnBase.java @@ -0,0 +1,19 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface ClassRolesAllowedParentResourceInterface_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnBase.java new file mode 100644 index 0000000000000..77d440a21ed7a --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnBase.java @@ -0,0 +1,32 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_BASE + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassRolesAllowedParentResourceWithPath_SecurityOnBase + implements ClassRolesAllowedParentResourceInterface_SecurityOnBase { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_ClassRolesAllowed(JsonObject array); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + CLASS_ROLES_ALLOWED_PATH) + public abstract ClassRolesAllowedSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_ClassRolesAllowed(); +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..9bd36d426e487 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnInterface.java @@ -0,0 +1,18 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_INTERFACE; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_INTERFACE + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassRolesAllowedParentResourceWithPath_SecurityOnInterface + implements ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnParent.java new file mode 100644 index 0000000000000..0ae13565bb527 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithPath_SecurityOnParent.java @@ -0,0 +1,45 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_SECURITY_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@RolesAllowed("admin") +@Path(CLASS_ROLES_ALLOWED_PREFIX + CLASS_SECURITY_ON_PARENT + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class ClassRolesAllowedParentResourceWithPath_SecurityOnParent + implements ClassRolesAllowedInterfaceWithoutPath_PathOnParent_SecurityOnParent { + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + CLASS_ROLES_ALLOWED_PATH) + public ClassRolesAllowedSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParentResource_ClassRolesAllowed() { + return new ClassRolesAllowedSubResourceWithoutPath(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + + SUB_IMPL_ON_PARENT + CLASS_ROLES_ALLOWED_PATH); + } + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_PathOnBase_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_PathOnBase_SecurityOnParent.java new file mode 100644 index 0000000000000..7ef14717b020d --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_PathOnBase_SecurityOnParent.java @@ -0,0 +1,23 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@RolesAllowed("admin") +public abstract class ClassRolesAllowedParentResourceWithoutPath_PathOnBase_SecurityOnParent { + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_PathOnInterface_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_PathOnInterface_SecurityOnParent.java new file mode 100644 index 0000000000000..cf36aa0bbcc0b --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_PathOnInterface_SecurityOnParent.java @@ -0,0 +1,29 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.RolesAllowed; + +import io.vertx.core.json.JsonObject; + +@RolesAllowed("admin") +public abstract class ClassRolesAllowedParentResourceWithoutPath_PathOnInterface_SecurityOnParent + implements ClassRolesAllowedInterfaceWithPath_SecurityOnParent { + + @Override + public ClassRolesAllowedSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_ClassRolesAllowed() { + return new ClassRolesAllowedSubResourceWithoutPath( + CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + CLASS_ROLES_ALLOWED_PATH); + } + + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnBase.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnBase.java new file mode 100644 index 0000000000000..d5b143d93be28 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnBase.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public abstract class ClassRolesAllowedParentResourceWithoutPath_SecurityOnBase + implements ClassRolesAllowedInterfaceWithoutPath_SecurityOnBase { + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_ClassRolesAllowed(JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnInterface.java new file mode 100644 index 0000000000000..73c1125d0672d --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnInterface.java @@ -0,0 +1,6 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +public abstract class ClassRolesAllowedParentResourceWithoutPath_SecurityOnInterface + implements ClassRolesAllowedInterfaceWithoutPath_SecurityOnInterface { + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnParent.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnParent.java new file mode 100644 index 0000000000000..d481ebca9cddb --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedParentResourceWithoutPath_SecurityOnParent.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; + +import jakarta.annotation.security.RolesAllowed; + +import io.vertx.core.json.JsonObject; + +@RolesAllowed("admin") +public abstract class ClassRolesAllowedParentResourceWithoutPath_SecurityOnParent + implements ClassRolesAllowedInterfaceWithoutPath_SecurityOnParent { + + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_ClassRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + CLASS_ROLES_ALLOWED_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedSubResourceWithoutPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedSubResourceWithoutPath.java new file mode 100644 index 0000000000000..223ea305c2998 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/classrolesallowed/ClassRolesAllowedSubResourceWithoutPath.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.classrolesallowed; + +import jakarta.ws.rs.POST; + +import io.vertx.core.json.JsonObject; + +public class ClassRolesAllowedSubResourceWithoutPath { + + private final String subResourcePath; + + public ClassRolesAllowedSubResourceWithoutPath(String subResourcePath) { + this.subResourcePath = subResourcePath; + } + + @POST + public String post(JsonObject array) { + return subResourcePath; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource.java new file mode 100644 index 0000000000000..d327817d23fb6 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource.java @@ -0,0 +1,83 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.multiple.pathonbase; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_NO_ANNOTATION_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.FIRST_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.MULTIPLE_INHERITANCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SECOND_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.THIRD_INTERFACE; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +@Path(CLASS_NO_ANNOTATION_PREFIX + MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE) +public class BaseResource implements BaseResource_First_Interface { + + @POST + @RolesAllowed("admin") + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + public String multipleInheritance_ClassPathOnBase_ImplOnBase_ImplWithPath_MethodRolesAllowed(JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH; + } + + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + public String multipleInheritance_ClassPathOnBase_ImplOnBase_ImplWithPath_NoAnnotation(JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String multipleInheritance_ClassPathOnBase_ImplOnBase_FirstInterface_InterfaceMethodWithPath_MethodRolesAllowed( + JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + FIRST_INTERFACE + INTERFACE_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH; + } + + @Override + public String multipleInheritance_ClassPathOnBase_ImplOnBase_FirstInterface_InterfaceMethodWithPath_NoAnnotation( + JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + FIRST_INTERFACE + INTERFACE_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String multipleInheritance_ClassPathOnBase_ImplOnBase_SecondInterface_InterfaceMethodWithPath_MethodRolesAllowed( + JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + SECOND_INTERFACE + INTERFACE_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH; + } + + @Override + public String multipleInheritance_ClassPathOnBase_ImplOnBase_SecondInterface_InterfaceMethodWithPath_NoAnnotation( + JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + SECOND_INTERFACE + INTERFACE_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String multipleInheritance_ClassPathOnBase_ImplOnBase_ThirdInterface_InterfaceMethodWithPath_MethodRolesAllowed( + JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH; + } + + @Override + public String multipleInheritance_ClassPathOnBase_ImplOnBase_ThirdInterface_InterfaceMethodWithPath_NoAnnotation( + JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_First_Interface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_First_Interface.java new file mode 100644 index 0000000000000..881ea5bd44121 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_First_Interface.java @@ -0,0 +1,31 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.multiple.pathonbase; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.FIRST_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.MULTIPLE_INHERITANCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface BaseResource_First_Interface + extends BaseResource_Second_Interface { + + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + FIRST_INTERFACE + INTERFACE_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH) + String multipleInheritance_ClassPathOnBase_ImplOnBase_FirstInterface_InterfaceMethodWithPath_MethodRolesAllowed( + JsonObject array); + + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + FIRST_INTERFACE + INTERFACE_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH) + String multipleInheritance_ClassPathOnBase_ImplOnBase_FirstInterface_InterfaceMethodWithPath_NoAnnotation( + JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_Second_Interface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_Second_Interface.java new file mode 100644 index 0000000000000..b9becf5b3963e --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_Second_Interface.java @@ -0,0 +1,31 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.multiple.pathonbase; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.MULTIPLE_INHERITANCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SECOND_INTERFACE; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface BaseResource_Second_Interface + extends BaseResource_Third_Interface { + + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + SECOND_INTERFACE + INTERFACE_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH) + String multipleInheritance_ClassPathOnBase_ImplOnBase_SecondInterface_InterfaceMethodWithPath_MethodRolesAllowed( + JsonObject array); + + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + SECOND_INTERFACE + INTERFACE_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH) + String multipleInheritance_ClassPathOnBase_ImplOnBase_SecondInterface_InterfaceMethodWithPath_NoAnnotation( + JsonObject array); + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_Third_Interface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_Third_Interface.java new file mode 100644 index 0000000000000..c7a7a43202ac5 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/multiple/pathonbase/BaseResource_Third_Interface.java @@ -0,0 +1,50 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.multiple.pathonbase; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.MULTIPLE_INHERITANCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.THIRD_INTERFACE; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface BaseResource_Third_Interface { + + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH) + String multipleInheritance_ClassPathOnBase_ImplOnBase_ThirdInterface_InterfaceMethodWithPath_MethodRolesAllowed( + JsonObject array); + + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH) + String multipleInheritance_ClassPathOnBase_ImplOnBase_ThirdInterface_InterfaceMethodWithPath_NoAnnotation( + JsonObject array); + + @RolesAllowed("admin") + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH) + default String multipleInheritance_ClassPathOnBase_ImplOnInterface_ThirdInterface_InterfaceMethodWithPath_MethodRolesAllowed( + JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH + + METHOD_ROLES_ALLOWED_PATH; + } + + @POST + @Path(MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH) + default String multipleInheritance_ClassPathOnBase_ImplOnInterface_ThirdInterface_InterfaceMethodWithPath_NoAnnotation( + JsonObject array) { + return MULTIPLE_INHERITANCE + CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + THIRD_INTERFACE + INTERFACE_METHOD_WITH_PATH + + NO_SECURITY_ANNOTATION_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithPath.java new file mode 100644 index 0000000000000..2abb17d1d62fb --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithPath.java @@ -0,0 +1,132 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_NO_ANNOTATION_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_NO_ANNOTATION_PREFIX + CLASS_PATH_ON_RESOURCE) +public class NoAnnotationBaseResourceWithPath extends NoAnnotationParentResourceWithoutPath { + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + @POST + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_NoAnnotation(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @Override + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + @POST + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + @POST + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + @POST + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_RolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_DenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_PermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_RolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_DenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_PermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + NO_SECURITY_ANNOTATION_PATH) + public NoAnnotationSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_NoSecurityAnnotation() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + NO_SECURITY_ANNOTATION_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + METHOD_PERMIT_ALL_PATH) + public NoAnnotationSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_MethodPermitAll() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + METHOD_PERMIT_ALL_PATH); + } + + @DenyAll + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + METHOD_DENY_ALL_PATH) + public NoAnnotationSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_MethodDenyAll() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + METHOD_DENY_ALL_PATH); + } + + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + METHOD_ROLES_ALLOWED_PATH) + public NoAnnotationSubResourceWithoutPath classPathOnResource_SubDeclaredOnBase_SubImplOnBase_MethodRolesAllowed() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_RESOURCE + SUB_DECLARED_ON_BASE + SUB_IMPL_ON_BASE + METHOD_ROLES_ALLOWED_PATH); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithoutPathExtParentRes.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithoutPathExtParentRes.java new file mode 100644 index 0000000000000..64d509db40b60 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithoutPathExtParentRes.java @@ -0,0 +1,105 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_NO_ANNOTATION_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public class NoAnnotationBaseResourceWithoutPathExtParentRes extends NoAnnotationParentResourceWithPath { + + @Override + @Path(CLASS_NO_ANNOTATION_PREFIX + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + @POST + public String get_ClassPathOnParentResource_ImplOnBase_ImplMethodWithPath_NoAnnotation(JsonObject array) { + return CLASS_NO_ANNOTATION_PREFIX + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_NoAnnotation(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @Override + public NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_NoSecurityAnnotation() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + NO_SECURITY_ANNOTATION_PATH); + } + + @PermitAll + @Override + public NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_MethodPermitAll() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + METHOD_PERMIT_ALL_PATH); + } + + @DenyAll + @Override + public NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_MethodDenyAll() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + METHOD_DENY_ALL_PATH); + } + + @RolesAllowed("admin") + @Override + public NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_MethodRolesAllowed() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + METHOD_ROLES_ALLOWED_PATH); + } + + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithoutPathImplInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithoutPathImplInterface.java new file mode 100644 index 0000000000000..150696c26f5eb --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationBaseResourceWithoutPathImplInterface.java @@ -0,0 +1,110 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public class NoAnnotationBaseResourceWithoutPathImplInterface extends NoAnnotationParentResourceWithoutPathImplInterface { + + @Override + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_NoAnnotation(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + public String classPathOnInterface_ImplOnBase_ImplMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_NoAnnotation(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @Override + public NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_NoSecurityAnnotation() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_BASE + NO_SECURITY_ANNOTATION_PATH); + } + + @PermitAll + @Override + public NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_MethodPermitAll() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_BASE + METHOD_PERMIT_ALL_PATH); + } + + @DenyAll + @Override + public NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_MethodDenyAll() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_BASE + METHOD_DENY_ALL_PATH); + } + + @RolesAllowed("admin") + @Override + public NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_MethodRolesAllowed() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_BASE + METHOD_ROLES_ALLOWED_PATH); + } + + @Override + public String classPathOnInterface_ImplOnBase_ParentMethodWithPath_NoSecurityAnnotation(JsonObject array) { + throw new IllegalStateException("RESTEasy didn't support this endpoint in past"); + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationInterfaceWithPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationInterfaceWithPath.java new file mode 100644 index 0000000000000..24dab9dcdfbcd --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationInterfaceWithPath.java @@ -0,0 +1,152 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_NO_ANNOTATION_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_NO_ANNOTATION_PREFIX + CLASS_PATH_ON_INTERFACE) +public interface NoAnnotationInterfaceWithPath { + + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_NoAnnotation(JsonObject array); + + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_MethodRolesAllowed(JsonObject array); + + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_MethodDenyAll(JsonObject array); + + String classPathOnInterface_ImplOnBase_ImplMethodWithPath_MethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_NoAnnotation(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_MethodDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnBase_InterfaceMethodWithPath_MethodPermitAll(JsonObject array); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + NO_SECURITY_ANNOTATION_PATH) + default NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_NoSecurityAnnotation() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_INTERFACE + NO_SECURITY_ANNOTATION_PATH); + } + + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + METHOD_ROLES_ALLOWED_PATH) + default NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_MethodRolesAllowed() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_INTERFACE + METHOD_ROLES_ALLOWED_PATH); + } + + @DenyAll + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + METHOD_DENY_ALL_PATH) + default NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_MethodDenyAll() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + METHOD_DENY_ALL_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + METHOD_PERMIT_ALL_PATH) + default NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnInterface_MethodPermitAll() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_INTERFACE + METHOD_PERMIT_ALL_PATH); + } + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + NO_SECURITY_ANNOTATION_PATH) + NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_NoSecurityAnnotation(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + METHOD_PERMIT_ALL_PATH) + NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_MethodPermitAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + METHOD_DENY_ALL_PATH) + NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_MethodDenyAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_BASE + METHOD_ROLES_ALLOWED_PATH) + NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnBase_MethodRolesAllowed(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + NO_SECURITY_ANNOTATION_PATH) + NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_NoSecurityAnnotation(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + METHOD_PERMIT_ALL_PATH) + NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_MethodPermitAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + METHOD_DENY_ALL_PATH) + NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_MethodDenyAll(); + + @Path(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + SUB_IMPL_ON_PARENT + METHOD_ROLES_ALLOWED_PATH) + NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_MethodRolesAllowed(); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_NoSecurityAnnotation(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_MethodDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_MethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @DenyAll + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + default String classPathOnInterface_ImplOnInterface_ImplMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationInterfaceWithoutPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationInterfaceWithoutPath.java new file mode 100644 index 0000000000000..c143a6daec45b --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationInterfaceWithoutPath.java @@ -0,0 +1,83 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface NoAnnotationInterfaceWithoutPath { + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_NoSecurityAnnotation(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_RolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_DenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + String classPathOnResource_ImplOnBase_InterfaceMethodWithPath_PermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_NoAnnotation(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_MethodDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_MethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @DenyAll + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @POST + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + default String classPathOnResource_ImplOnInterface_ImplMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceInterface.java new file mode 100644 index 0000000000000..6ea136bdb8a9a --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceInterface.java @@ -0,0 +1,82 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public interface NoAnnotationParentResourceInterface { + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_NoSecurityAnnotation(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_MethodDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + String classPathOnParentResource_ImplOnBase_InterfaceMethodWithPath_MethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_NoSecurityAnnotation(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_MethodPermitAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_MethodDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @PermitAll + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @DenyAll + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + default String classPathOnParentResource_ImplOnInterface_ImplMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_INTERFACE + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithPath.java new file mode 100644 index 0000000000000..1c2aa3b92fdac --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithPath.java @@ -0,0 +1,142 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_NO_ANNOTATION_PREFIX; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_PARENT_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.vertx.core.json.JsonObject; + +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Path(CLASS_NO_ANNOTATION_PREFIX + CLASS_PATH_ON_PARENT_RESOURCE) +public abstract class NoAnnotationParentResourceWithPath implements NoAnnotationParentResourceInterface { + + public String get_ClassPathOnParentResource_ImplOnBase_ImplMethodWithPath_NoAnnotation(JsonObject array) { + throw new IllegalStateException("Implementation should had been invoked"); + } + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_NoAnnotation(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_MethodRolesAllowed(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_MethodDenyAll(JsonObject array); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + public abstract String classPathOnParentResource_ImplOnBase_ParentMethodWithPath_MethodPermitAll(JsonObject array); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + NO_SECURITY_ANNOTATION_PATH) + public NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParentResource_NoSecurityAnnotation() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + + SUB_IMPL_ON_PARENT + NO_SECURITY_ANNOTATION_PATH); + } + + @PermitAll + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + METHOD_PERMIT_ALL_PATH) + public NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParentResource_MethodPermitAll() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + METHOD_PERMIT_ALL_PATH); + } + + @DenyAll + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + METHOD_DENY_ALL_PATH) + public NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParentResource_MethodDenyAll() { + return new NoAnnotationSubResourceWithoutPath( + CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + METHOD_DENY_ALL_PATH); + } + + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_PARENT + METHOD_ROLES_ALLOWED_PATH) + public NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnParentResource_MethodRolesAllowed() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + + SUB_IMPL_ON_PARENT + METHOD_ROLES_ALLOWED_PATH); + } + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + NO_SECURITY_ANNOTATION_PATH) + public abstract NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_NoSecurityAnnotation(); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + METHOD_PERMIT_ALL_PATH) + public abstract NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_MethodPermitAll(); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + METHOD_DENY_ALL_PATH) + public abstract NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_MethodDenyAll(); + + @Path(CLASS_PATH_ON_PARENT_RESOURCE + SUB_DECLARED_ON_PARENT + SUB_IMPL_ON_BASE + METHOD_ROLES_ALLOWED_PATH) + public abstract NoAnnotationSubResourceWithoutPath classPathOnParentResource_SubDeclaredOnParent_SubImplOnBaseResource_MethodRolesAllowed(); + + @POST + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @POST + @PermitAll + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @POST + @DenyAll + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @POST + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + public String classPathOnParentResource_ImplOnParent_ImplMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnParentResource_ImplOnParent_InterfaceMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_PARENT_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithoutPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithoutPath.java new file mode 100644 index 0000000000000..f2ce03626f1fb --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithoutPath.java @@ -0,0 +1,95 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_RESOURCE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public abstract class NoAnnotationParentResourceWithoutPath implements NoAnnotationInterfaceWithoutPath { + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_NoSecurityAnnotation(JsonObject array); + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_RolesAllowed(JsonObject array); + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_DenyAll(JsonObject array); + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + @POST + public abstract String test_ClassPathOnResource_ImplOnBase_ParentMethodWithPath_PermitAll(JsonObject array); + + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Path(CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH) + @POST + public String test_ClassPathOnResource_ImplOnParent_ImplMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_NoAnnotation(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } + + @DenyAll + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnResource_ImplOnParent_InterfaceMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_RESOURCE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + public String get_ClassPathOnResource_ImplOnBase_ImplMethodWithPath_MethodRolesAllowed(JsonObject array) { + // hint: purpose of this method is to ensure that existence of overridden parent method + // has no effect on a secured method (like: correct secured resource method is identified) + throw new IllegalStateException("Implementation should be used"); + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithoutPathImplInterface.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithoutPathImplInterface.java new file mode 100644 index 0000000000000..b09ee0649c292 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationParentResourceWithoutPathImplInterface.java @@ -0,0 +1,85 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.CLASS_PATH_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_BASE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.IMPL_ON_PARENT; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.INTERFACE_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_DENY_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_PERMIT_ALL_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.METHOD_ROLES_ALLOWED_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.NO_SECURITY_ANNOTATION_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.PARENT_METHOD_WITH_PATH; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_DECLARED_ON_INTERFACE; +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SUB_IMPL_ON_PARENT; + +import jakarta.annotation.security.DenyAll; +import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public abstract class NoAnnotationParentResourceWithoutPathImplInterface implements NoAnnotationInterfaceWithPath { + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_BASE + PARENT_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + public abstract String classPathOnInterface_ImplOnBase_ParentMethodWithPath_NoSecurityAnnotation(JsonObject array); + + @Override + public NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_NoSecurityAnnotation() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + NO_SECURITY_ANNOTATION_PATH); + } + + @PermitAll + @Override + public NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_MethodPermitAll() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + METHOD_PERMIT_ALL_PATH); + } + + @DenyAll + @Override + public NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_MethodDenyAll() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + METHOD_DENY_ALL_PATH); + } + + @RolesAllowed("admin") + @Override + public NoAnnotationSubResourceWithoutPath classPathOnInterface_SubDeclaredOnInterface_SubImplOnParent_MethodRolesAllowed() { + return new NoAnnotationSubResourceWithoutPath(CLASS_PATH_ON_INTERFACE + SUB_DECLARED_ON_INTERFACE + + SUB_IMPL_ON_PARENT + METHOD_ROLES_ALLOWED_PATH); + } + + @POST + @Path(CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH) + public String classPathOnInterface_ImplOnParent_ImplMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + IMPL_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_NoSecurityAnnotation(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + NO_SECURITY_ANNOTATION_PATH; + } + + @DenyAll + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_MethodDenyAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_DENY_ALL_PATH; + } + + @PermitAll + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_MethodPermitAll(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_PERMIT_ALL_PATH; + } + + @RolesAllowed("admin") + @Override + public String classPathOnInterface_ImplOnParent_InterfaceMethodWithPath_MethodRolesAllowed(JsonObject array) { + return CLASS_PATH_ON_INTERFACE + IMPL_ON_PARENT + INTERFACE_METHOD_WITH_PATH + METHOD_ROLES_ALLOWED_PATH; + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationSubResourceWithoutPath.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationSubResourceWithoutPath.java new file mode 100644 index 0000000000000..79e5262467976 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/inheritance/noclassannotation/NoAnnotationSubResourceWithoutPath.java @@ -0,0 +1,31 @@ +package io.quarkus.resteasy.reactive.server.test.security.inheritance.noclassannotation; + +import static io.quarkus.resteasy.reactive.server.test.security.inheritance.SubPaths.SECURED_SUB_RESOURCE_ENDPOINT_PATH; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import io.vertx.core.json.JsonObject; + +public class NoAnnotationSubResourceWithoutPath { + + private final String subResourcePath; + + public NoAnnotationSubResourceWithoutPath(String subResourcePath) { + this.subResourcePath = subResourcePath; + } + + @POST + public String post(JsonObject array) { + return subResourcePath; + } + + @RolesAllowed("admin") + @Path(SECURED_SUB_RESOURCE_ENDPOINT_PATH) + @POST + public String securedPost(JsonObject array) { + return subResourcePath + SECURED_SUB_RESOURCE_ENDPOINT_PATH; + } + +} diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityContext.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityContext.java index e94d113d32991..b75ac82fd32c9 100644 --- a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityContext.java +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityContext.java @@ -16,9 +16,7 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; -import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveResourceInfo; -import io.quarkus.arc.Arc; import io.quarkus.arc.InjectableInstance; import io.quarkus.runtime.ShutdownEvent; import io.quarkus.runtime.StartupEvent; @@ -30,13 +28,10 @@ import io.quarkus.security.spi.runtime.AuthorizationFailureEvent; import io.quarkus.security.spi.runtime.AuthorizationSuccessEvent; import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; -import io.quarkus.security.spi.runtime.MethodDescription; -import io.quarkus.security.spi.runtime.SecurityCheckStorage; import io.quarkus.security.spi.runtime.SecurityEventHelper; import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.quarkus.vertx.http.runtime.HttpConfiguration; import io.quarkus.vertx.http.runtime.security.AbstractPathMatchingHttpSecurityPolicy; -import io.quarkus.vertx.http.runtime.security.EagerSecurityInterceptorStorage; import io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy; import io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.DefaultAuthorizationRequestContext; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; @@ -51,9 +46,7 @@ public class EagerSecurityContext { final AbstractPathMatchingHttpSecurityPolicy jaxRsPathMatchingPolicy; final SecurityEventHelper eventHelper; final InjectableInstance identityAssociation; - final EagerSecurityInterceptorStorage interceptorStorage; final AuthorizationController authorizationController; - final SecurityCheckStorage securityCheckStorage; final boolean doNotRunPermissionSecurityCheck; final boolean isProactiveAuthDisabled; @@ -61,14 +54,11 @@ public class EagerSecurityContext { @ConfigProperty(name = "quarkus.security.events.enabled") boolean securityEventsEnabled, Event authorizationSuccessEvent, BeanManager beanManager, InjectableInstance identityAssociation, AuthorizationController authorizationController, - SecurityCheckStorage securityCheckStorage, HttpConfiguration httpConfig, BlockingSecurityExecutor blockingExecutor, + HttpConfiguration httpConfig, BlockingSecurityExecutor blockingExecutor, HttpBuildTimeConfig buildTimeConfig, Instance installedPolicies) { - var interceptorStorageHandle = Arc.container().instance(EagerSecurityInterceptorStorage.class); - this.interceptorStorage = interceptorStorageHandle.isAvailable() ? interceptorStorageHandle.get() : null; this.isProactiveAuthDisabled = !buildTimeConfig.auth.proactive; this.identityAssociation = identityAssociation; this.authorizationController = authorizationController; - this.securityCheckStorage = securityCheckStorage; this.eventHelper = new SecurityEventHelper<>(authorizationSuccessEvent, authorizationFailureEvent, AUTHORIZATION_SUCCESS, AUTHORIZATION_FAILURE, beanManager, securityEventsEnabled); var jaxRsPathMatchingPolicy = new AbstractPathMatchingHttpSecurityPolicy(httpConfig.auth.permissions, @@ -87,8 +77,8 @@ public class EagerSecurityContext { void initSingleton(@Observes StartupEvent event) { // intention here is to initialize this instance during app startup and make it accessible as singleton to // all the security ServerRestHandler instances, so that they don't need to access it via CDI programmatically - // and write to a volatile variable during the request; the EagerSecurityHandler is created for each secured - // endpoint, so there can be a lot of them + // and write to a volatile variable during the request; the EagerSecurityHandler is created for each + // endpoint (in case there is HTTP permission configured), so there can be a lot of them instance = this; } @@ -176,9 +166,4 @@ public SecurityIdentity apply(SecurityCheckWithIdentity checkWithIdentity) { } }); } - - static MethodDescription lazyMethodToMethodDescription(ResteasyReactiveResourceInfo lazyMethod) { - return new MethodDescription(lazyMethod.getActualDeclaringClassName(), - lazyMethod.getName(), MethodDescription.typesAsStrings(lazyMethod.getParameterTypes())); - } } diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java index 27da712905fe3..2b1a28d5ea50a 100644 --- a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java @@ -1,9 +1,7 @@ package io.quarkus.resteasy.reactive.server.runtime.security; import static io.quarkus.resteasy.reactive.server.runtime.StandardSecurityCheckInterceptor.STANDARD_SECURITY_CHECK_INTERCEPTOR; -import static io.quarkus.resteasy.reactive.server.runtime.security.EagerSecurityContext.lazyMethodToMethodDescription; -import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Map; @@ -17,12 +15,14 @@ import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; +import io.quarkus.arc.Arc; import io.quarkus.security.UnauthorizedException; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.spi.runtime.AuthorizationFailureEvent; import io.quarkus.security.spi.runtime.AuthorizationSuccessEvent; import io.quarkus.security.spi.runtime.MethodDescription; import io.quarkus.security.spi.runtime.SecurityCheck; +import io.quarkus.security.spi.runtime.SecurityCheckStorage; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.subscription.UniSubscriber; @@ -31,22 +31,21 @@ public class EagerSecurityHandler implements ServerRestHandler { - private static final SecurityCheck NULL_SENTINEL = new SecurityCheck() { - @Override - public void apply(SecurityIdentity identity, Method method, Object[] parameters) { - - } - - @Override - public void apply(SecurityIdentity identity, MethodDescription method, Object[] parameters) { - - } - }; - private final boolean onlyCheckForHttpPermissions; - private volatile SecurityCheck check; - - public EagerSecurityHandler(boolean onlyCheckForHttpPermissions) { - this.onlyCheckForHttpPermissions = onlyCheckForHttpPermissions; + /** + * Used when no endpoint security checks were detected, no default Jakarta REST security is in place, and + * we have this handler in place for whether Jakarta REST specific HTTP Permissions are required + * is determined when runtime config is available. + */ + private static final EagerSecurityHandler HTTP_PERMS_ONLY = new EagerSecurityHandler(null, false, null); + + private final SecurityCheck check; + private final boolean isDefaultJaxRsSecCheck; + private final MethodDescription invokedMethodDesc; + + private EagerSecurityHandler(SecurityCheck check, boolean isDefaultJaxRsSecCheck, MethodDescription invokedMethodDesc) { + this.check = check; + this.isDefaultJaxRsSecCheck = isDefaultJaxRsSecCheck; + this.invokedMethodDesc = invokedMethodDesc; } @Override @@ -55,14 +54,26 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti return; } - var securityCheck = getSecurityCheck(requestContext); + if (isDefaultJaxRsSecCheck && isRequestAlreadyChecked(requestContext)) { + // default Jakarta REST security is applied on subresource locators + // this ensures it's not reapplied on subresource endpoints + return; + } + + final Function> checkRequiringIdentity; + if (check == null) { + checkRequiringIdentity = null; + } else { + checkRequiringIdentity = getSecurityCheck(requestContext, check, invokedMethodDesc); + } + final Uni check; - if (securityCheck == null) { + if (checkRequiringIdentity == null) { if (EagerSecurityContext.instance.doNotRunPermissionSecurityCheck) { - // no check + // either permit all security check or no check at all return; } else { - // only permission check + // only HTTP permission check check = Uni.createFrom().deferred(new Supplier>() { @Override public Uni get() { @@ -72,10 +83,10 @@ public Uni get() { } } else { if (EagerSecurityContext.instance.doNotRunPermissionSecurityCheck) { - // only security check - check = EagerSecurityContext.instance.getDeferredIdentity().chain(securityCheck); + // only security check that requires identity + check = EagerSecurityContext.instance.getDeferredIdentity().chain(checkRequiringIdentity); } else { - // both security check and permission check + // both security check that requires identity and HTTP permission check check = EagerSecurityContext.instance.getDeferredIdentity() .flatMap(new Function>() { @Override @@ -83,7 +94,7 @@ public Uni apply(SecurityIdentity securityIdentity) { return EagerSecurityContext.instance.getPermissionCheck(requestContext, securityIdentity); } }) - .chain(securityCheck); + .chain(checkRequiringIdentity); } } @@ -107,30 +118,13 @@ public void onFailure(Throwable failure) { }); } - private Function> getSecurityCheck(ResteasyReactiveRequestContext requestContext) { - if (this.onlyCheckForHttpPermissions || this.check == NULL_SENTINEL) { - return null; - } - SecurityCheck check = this.check; - MethodDescription methodDescription = lazyMethodToMethodDescription(requestContext.getTarget().getLazyMethod()); - if (check == null) { - check = EagerSecurityContext.instance.securityCheckStorage.getSecurityCheck(methodDescription); - if (check == null) { - if (EagerSecurityContext.instance.securityCheckStorage.getDefaultSecurityCheck() == null - || isRequestAlreadyChecked(requestContext)) { - check = NULL_SENTINEL; - } else { - check = EagerSecurityContext.instance.securityCheckStorage.getDefaultSecurityCheck(); - } - } - this.check = check; - } - if (check == NULL_SENTINEL) { - return null; - } - + /** + * @return null if the check permits all requests, otherwise fun that requires identity to perform check + */ + private static Function> getSecurityCheck(ResteasyReactiveRequestContext requestContext, + SecurityCheck check, MethodDescription invokedMethodDesc) { if (check.isPermitAll()) { - preventRepeatedSecurityChecks(requestContext, methodDescription); + preventRepeatedSecurityChecks(requestContext, invokedMethodDesc); if (EagerSecurityContext.instance.eventHelper.fireEventOnSuccess()) { requestContext.requireCDIRequestScope(); @@ -144,11 +138,10 @@ private Function> getSecurityCheck(ResteasyReactiveRequ } EagerSecurityContext.instance.eventHelper.fireSuccessEvent(new AuthorizationSuccessEvent(identity, - check.getClass().getName(), createEventPropsWithRoutingCtx(requestContext), methodDescription)); + check.getClass().getName(), createEventPropsWithRoutingCtx(requestContext), invokedMethodDesc)); } return null; } else { - SecurityCheck theCheck = check; return new Function>() { @Override public Uni apply(SecurityIdentity securityIdentity) { @@ -159,7 +152,7 @@ public Uni apply(SecurityIdentity securityIdentity) { EagerSecurityContext.instance.identityAssociation.get().setIdentity(securityIdentity); } - if (theCheck.requiresMethodArguments()) { + if (check.requiresMethodArguments()) { // if security check requires method arguments, we can't perform it now // however we only allow to pass authenticated requests to avoid security risks if (securityIdentity == null || securityIdentity.isAnonymous()) { @@ -167,16 +160,16 @@ public Uni apply(SecurityIdentity securityIdentity) { if (EagerSecurityContext.instance.eventHelper.fireEventOnFailure()) { EagerSecurityContext.instance.eventHelper .fireFailureEvent(new AuthorizationFailureEvent(securityIdentity, unauthorizedException, - theCheck.getClass().getName(), createEventPropsWithRoutingCtx(requestContext), - methodDescription)); + check.getClass().getName(), createEventPropsWithRoutingCtx(requestContext), + invokedMethodDesc)); } throw unauthorizedException; } // security check will be performed by CDI interceptor return Uni.createFrom().nullItem(); } else { - preventRepeatedSecurityChecks(requestContext, methodDescription); - var checkResult = theCheck.nonBlockingApply(securityIdentity, methodDescription, + preventRepeatedSecurityChecks(requestContext, invokedMethodDesc); + var checkResult = check.nonBlockingApply(securityIdentity, invokedMethodDesc, requestContext.getParameters()); if (EagerSecurityContext.instance.eventHelper.fireEventOnFailure()) { checkResult = checkResult @@ -186,8 +179,8 @@ public Uni apply(SecurityIdentity securityIdentity) { public void accept(Throwable throwable) { EagerSecurityContext.instance.eventHelper .fireFailureEvent(new AuthorizationFailureEvent( - securityIdentity, throwable, theCheck.getClass().getName(), - createEventPropsWithRoutingCtx(requestContext), methodDescription)); + securityIdentity, throwable, check.getClass().getName(), + createEventPropsWithRoutingCtx(requestContext), invokedMethodDesc)); } }); } @@ -198,8 +191,8 @@ public void accept(Throwable throwable) { public void run() { EagerSecurityContext.instance.eventHelper.fireSuccessEvent( new AuthorizationSuccessEvent(securityIdentity, - theCheck.getClass().getName(), - createEventPropsWithRoutingCtx(requestContext), methodDescription)); + check.getClass().getName(), + createEventPropsWithRoutingCtx(requestContext), invokedMethodDesc)); } }); } @@ -244,7 +237,35 @@ public static HandlerChainCustomizer newInstance(boolean onlyCheckForHttpPermiss public List handlers(Phase phase, ResourceClass resourceClass, ServerResourceMethod serverResourceMethod) { if (phase == Phase.AFTER_MATCH) { - return Collections.singletonList(new EagerSecurityHandler(onlyCheckForHttpPermissions())); + if (onlyCheckForHttpPermissions()) { + return Collections.singletonList(HTTP_PERMS_ONLY); + } + + boolean isDefaultJaxRsSecCheck = false; + var desc = ResourceMethodDescription.of(serverResourceMethod); + var checkStorage = Arc.container().instance(SecurityCheckStorage.class).get(); + + var check = checkStorage.getSecurityCheck(desc.invokedMethodDesc()); + if (check == null && desc.fallbackMethodDesc() != null) { + check = checkStorage.getSecurityCheck(desc.fallbackMethodDesc()); + } + if (check == null) { + check = checkStorage.getDefaultSecurityCheck(); + isDefaultJaxRsSecCheck = true; + } + + if (check == null) { + throw new IllegalStateException( + """ + Security annotation placed on resource method '%s#%s' wasn't detected by Quarkus during the build time. + Please report issue in Quarkus project. + """ + .formatted(desc.invokedMethodDesc().getClassName(), + desc.invokedMethodDesc().getMethodName())); + } + + return Collections + .singletonList(new EagerSecurityHandler(check, isDefaultJaxRsSecCheck, desc.invokedMethodDesc())); } return Collections.emptyList(); } diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityInterceptorHandler.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityInterceptorHandler.java index 6570c4ea403bb..92f43df67f578 100644 --- a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityInterceptorHandler.java +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityInterceptorHandler.java @@ -1,7 +1,5 @@ package io.quarkus.resteasy.reactive.server.runtime.security; -import static io.quarkus.resteasy.reactive.server.runtime.security.EagerSecurityContext.lazyMethodToMethodDescription; - import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -12,8 +10,9 @@ import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; -import io.quarkus.security.spi.runtime.MethodDescription; +import io.quarkus.arc.Arc; import io.quarkus.security.spi.runtime.SecurityCheck; +import io.quarkus.vertx.http.runtime.security.EagerSecurityInterceptorStorage; import io.vertx.ext.web.RoutingContext; /** @@ -22,36 +21,16 @@ */ public class EagerSecurityInterceptorHandler implements ServerRestHandler { - private static final Consumer NULL_SENTINEL = new Consumer() { - @Override - public void accept(RoutingContext routingContext) { - - } - }; - private volatile Consumer interceptor; + private final Consumer interceptor; - private EagerSecurityInterceptorHandler() { + private EagerSecurityInterceptorHandler(Consumer interceptor) { + this.interceptor = interceptor; } @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { // right now we do apply security interceptors even when authorization is disabled (for example for tests), as // even though you don't want to run security checks, you still might want to authenticate (access identity) - - if (interceptor == NULL_SENTINEL) { - return; - } - - if (interceptor == null) { - MethodDescription methodDescription = lazyMethodToMethodDescription(requestContext.getTarget().getLazyMethod()); - interceptor = EagerSecurityContext.instance.interceptorStorage.getInterceptor(methodDescription); - - if (interceptor == null) { - interceptor = NULL_SENTINEL; - return; - } - } - interceptor.accept(requestContext.unwrap(RoutingContext.class)); } @@ -65,7 +44,25 @@ public static HandlerChainCustomizer newInstance() { public List handlers(Phase phase, ResourceClass resourceClass, ServerResourceMethod serverResourceMethod) { if (phase == Phase.AFTER_MATCH) { - return Collections.singletonList(new EagerSecurityInterceptorHandler()); + + var desc = ResourceMethodDescription.of(serverResourceMethod); + var interceptorStorage = Arc.container().instance(EagerSecurityInterceptorStorage.class).get(); + var interceptor = interceptorStorage.getInterceptor(desc.invokedMethodDesc()); + if (interceptor == null && desc.fallbackMethodDesc() != null) { + interceptor = interceptorStorage.getInterceptor(desc.fallbackMethodDesc()); + } + + if (interceptor == null) { + throw new IllegalStateException( + """ + Security annotation placed on resource method '%s#%s' wasn't detected by Quarkus during the build time. + Please report issue in Quarkus project. + """ + .formatted(desc.invokedMethodDesc().getClassName(), + desc.invokedMethodDesc().getMethodName())); + } + + return Collections.singletonList(new EagerSecurityInterceptorHandler(interceptor)); } return Collections.emptyList(); } diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/ResourceMethodDescription.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/ResourceMethodDescription.java new file mode 100644 index 0000000000000..c7fb73a68cc93 --- /dev/null +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/ResourceMethodDescription.java @@ -0,0 +1,31 @@ +package io.quarkus.resteasy.reactive.server.runtime.security; + +import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; + +import io.quarkus.security.spi.runtime.MethodDescription; + +/** + * @param invokedMethodDesc description of actually invoked method (method on which CDI interceptors are applied) + * @param fallbackMethodDesc description that we used in the past; not null when different to {@code invokedMethodDesc} + */ +record ResourceMethodDescription(MethodDescription invokedMethodDesc, MethodDescription fallbackMethodDesc) { + + static ResourceMethodDescription of(ServerResourceMethod method) { + return new ResourceMethodDescription( + createMethodDescription(method, method.getActualDeclaringClassName()), + createMethodDescription(method, method.getClassDeclMethodThatHasJaxRsEndpointDefiningAnn())); + } + + private static MethodDescription createMethodDescription(ServerResourceMethod method, String clazz) { + if (clazz == null) { + return null; + } + String[] paramTypes = new String[method.getParameters().length]; + for (int i = 0; i < method.getParameters().length; i++) { + paramTypes[i] = method.getParameters()[i].declaredUnresolvedType; + } + + return new MethodDescription(clazz, method.getName(), paramTypes); + } + +} diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java index c526bc1b7846f..e2a978c5a9cfa 100644 --- a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java @@ -16,8 +16,6 @@ import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; -import io.quarkus.arc.Arc; -import io.quarkus.arc.InjectableInstance; import io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveSecurityContext; import io.quarkus.security.credential.Credential; import io.quarkus.security.identity.CurrentIdentityAssociation; @@ -28,7 +26,10 @@ public class SecurityContextOverrideHandler implements ServerRestHandler { - private volatile InjectableInstance currentIdentityAssociation; + private static final SecurityContextOverrideHandler INSTANCE = new SecurityContextOverrideHandler(); + + private SecurityContextOverrideHandler() { + } @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { @@ -46,10 +47,9 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti private void updateIdentity(ResteasyReactiveRequestContext requestContext, SecurityContext modified) { requestContext.requireCDIRequestScope(); - InjectableInstance instance = getCurrentIdentityAssociation(); - if (instance.isResolvable()) { + if (EagerSecurityContext.instance.identityAssociation.isResolvable()) { RoutingContext routingContext = requestContext.unwrap(RoutingContext.class); - CurrentIdentityAssociation currentIdentityAssociation = instance.get(); + CurrentIdentityAssociation currentIdentityAssociation = EagerSecurityContext.instance.identityAssociation.get(); Uni oldIdentity = currentIdentityAssociation.getDeferredIdentity(); currentIdentityAssociation.setIdentity(oldIdentity.map(new Function() { @Override @@ -119,20 +119,12 @@ public Uni checkPermission(Permission permission) { } } - private InjectableInstance getCurrentIdentityAssociation() { - InjectableInstance identityAssociation = this.currentIdentityAssociation; - if (identityAssociation == null) { - return this.currentIdentityAssociation = Arc.container().select(CurrentIdentityAssociation.class); - } - return identityAssociation; - } - public static class Customizer implements HandlerChainCustomizer { @Override public List handlers(Phase phase, ResourceClass resourceClass, ServerResourceMethod serverResourceMethod) { if (phase == Phase.AFTER_PRE_MATCH) { - return Collections.singletonList(new SecurityContextOverrideHandler()); + return Collections.singletonList(INSTANCE); } return Collections.emptyList(); } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/EagerSecurityInterceptorBindingBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/EagerSecurityInterceptorBindingBuildItem.java index 9cb335d53b863..e6870df3e4511 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/EagerSecurityInterceptorBindingBuildItem.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/EagerSecurityInterceptorBindingBuildItem.java @@ -27,6 +27,11 @@ public final class EagerSecurityInterceptorBindingBuildItem extends MultiBuildIt private final DotName[] annotationBindings; private final Function> interceptorCreator; private final Map bindingToValue; + /** + * If this interceptor is always accompanied by {@link io.quarkus.security.spi.runtime.SecurityCheck}. + * For example, we know that endpoint annotated with {@link HttpAuthenticationMechanism} is always secured. + */ + private final boolean requiresSecurityCheck; /** * @@ -38,6 +43,7 @@ public EagerSecurityInterceptorBindingBuildItem(Function> interceptorCreator, @@ -45,6 +51,7 @@ public EagerSecurityInterceptorBindingBuildItem(Function> bindingValueToInterceptedMethods, - DotName interceptorBinding) { + DotName interceptorBinding, boolean requiresSecurityCheck) { this.bindingValueToInterceptedMethods = Map.copyOf(bindingValueToInterceptedMethods); this.interceptorBinding = interceptorBinding; + this.requiresSecurityCheck = requiresSecurityCheck; } private Stream interceptedMethods() { return bindingValueToInterceptedMethods.values().stream().flatMap(Collection::stream); } - public static List collectInterceptedMethods(List items) { - return items.stream().flatMap(EagerSecurityInterceptorMethodsBuildItem::interceptedMethods).toList(); + public static Map collectInterceptedMethods(List items) { + Map result = new HashMap<>(); + for (var item : items) { + item.interceptedMethods().forEach(mi -> { + if (result.containsKey(mi)) { + var requiresCheck = result.get(mi); + if (!requiresCheck && item.requiresSecurityCheck) { + result.put(mi, true); + } + } else { + result.put(mi, item.requiresSecurityCheck); + } + }); + } + return result; } - } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java index 6c77581e02424..9d27aac812e25 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpSecurityProcessor.java @@ -331,6 +331,11 @@ void collectInterceptedMethods(CombinedIndexBuildItem indexBuildItem, List interceptorBindings, BuildProducer methodsProducer) { if (!interceptorBindings.isEmpty()) { + Map bindingToRequiresSecCheckFlag = interceptorBindings.stream() + .flatMap(ib -> Arrays + .stream(ib.getAnnotationBindings()) + .map(b -> Map.entry(b, ib.requiresSecurityCheck()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); var index = indexBuildItem.getIndex(); Map> cache = new HashMap<>(); Map>> result = new HashMap<>(); @@ -338,7 +343,8 @@ void collectInterceptedMethods(CombinedIndexBuildItem indexBuildItem, addInterceptedEndpoints(interceptorBindings, index, AnnotationTarget.Kind.CLASS, result, cache); if (!result.isEmpty()) { result.forEach((annotationBinding, bindingValueToInterceptedMethods) -> methodsProducer.produce( - new EagerSecurityInterceptorMethodsBuildItem(bindingValueToInterceptedMethods, annotationBinding))); + new EagerSecurityInterceptorMethodsBuildItem(bindingValueToInterceptedMethods, annotationBinding, + bindingToRequiresSecCheckFlag.get(annotationBinding)))); } } } diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java index 07fb3fba149d4..07ebfc324d5d9 100644 --- a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java @@ -25,7 +25,9 @@ import java.io.File; import java.io.InputStream; +import java.lang.reflect.Modifier; import java.nio.file.Path; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -34,6 +36,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Set; import java.util.function.Supplier; import java.util.regex.PatternSyntaxException; @@ -183,10 +186,108 @@ protected ServerResourceMethod createResourceMethod(MethodInfo methodInfo, Class } } serverResourceMethod.setHandlerChainCustomizers(methodCustomizers); - serverResourceMethod.setActualDeclaringClassName(methodInfo.declaringClass().name().toString()); + + var actualDeclaringClassName = findActualDeclaringClassName(methodInfo, actualEndpointClass); + serverResourceMethod.setActualDeclaringClassName(actualDeclaringClassName); + var classDeclMethodThatHasJaxRsEndpointDefiningAnn = methodInfo.declaringClass().name().toString(); + if (!actualDeclaringClassName.equals(classDeclMethodThatHasJaxRsEndpointDefiningAnn)) { + serverResourceMethod + .setClassDeclMethodThatHasJaxRsEndpointDefiningAnn(classDeclMethodThatHasJaxRsEndpointDefiningAnn); + } + return serverResourceMethod; } + private String findActualDeclaringClassName(MethodInfo methodInfo, ClassInfo actualEndpointClass) { + return findEndpointImplementation(methodInfo, actualEndpointClass, index).declaringClass().name().toString(); + } + + /** + * Aim here is to find a method that actually returns endpoint response. + * We can receive method with similar signature several times here, only differing in the modifiers (abstract etc.). + * However, {@code actualEndpointClass} will change. + * For example once from the interface with JAX-RS endpoint defining annotations and also from implementors. + * + * @return method that returns endpoint response + */ + public static MethodInfo findEndpointImplementation(MethodInfo methodInfo, ClassInfo actualEndpointClass, IndexView index) { + // provided that 'actualEndpointClass' is requested from CDI via InstanceHandler factory + // we know that this class resolution must be unambiguous: + // 1. go down - find exactly one non-abstract class + ClassInfo clazz = null; + if (actualEndpointClass.isInterface()) { + for (var implementor : index.getAllKnownImplementors(actualEndpointClass.name())) { + if (!implementor.isInterface() && !implementor.isAbstract()) { + if (clazz == null) { + clazz = implementor; + // keep going to recognize if there is more than one non-abstract implementor + } else { + // resolution is not unambiguous, this at least make behavior deterministic + clazz = actualEndpointClass; + break; + } + } + } + } else { + for (var subClass : index.getAllKnownSubclasses(actualEndpointClass.name())) { + if (!subClass.isAbstract()) { + if (clazz == null) { + clazz = subClass; + // keep going to recognize if there is more than one non-abstract subclass + } else { + // resolution is not unambiguous, this at least make behavior deterministic + clazz = actualEndpointClass; + break; + } + } + } + } + if (clazz == null) { + clazz = actualEndpointClass; + } + + // 2. go up - first impl. going up is the one invoked on the endpoint instance + Queue defaultInterfaceMethods = new ArrayDeque<>(); + do { + // is non-abstract method declared on this class? + var method = clazz.method(methodInfo.name(), methodInfo.parameterTypes()); + if (method != null && !Modifier.isAbstract(method.flags())) { + return method; + } + + var interfaceWithImplMethod = findInterfaceDefaultMethod(clazz, methodInfo, index); + if (interfaceWithImplMethod != null) { + // class methods override default interface methods -> check parent first + defaultInterfaceMethods.add(interfaceWithImplMethod); + } + + if (clazz.superName() != null && !clazz.superName().equals(ResteasyReactiveDotNames.OBJECT)) { + clazz = index.getClassByName(clazz.superName()); + } else { + break; + } + } while (clazz != null); + if (!defaultInterfaceMethods.isEmpty()) { + return defaultInterfaceMethods.peek(); + } + + // 3. fallback to original behavior + return methodInfo; + } + + private static MethodInfo findInterfaceDefaultMethod(ClassInfo clazz, MethodInfo methodInfo, IndexView index) { + for (DotName interfaceName : clazz.interfaceNames()) { + var interfaceClass = index.getClassByName(interfaceName); + if (interfaceClass != null) { + var intMethod = interfaceClass.method(methodInfo.name(), methodInfo.parameterTypes()); + if (intMethod != null && intMethod.isDefault() && Modifier.isPublic(intMethod.flags())) { + return intMethod; + } + } + } + return null; + } + @Override protected boolean handleBeanParam(ClassInfo actualEndpointInfo, Type paramType, MethodParameter[] methodParameters, int i, Set fileFormNames) { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ServerResourceMethod.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ServerResourceMethod.java index e56dd9e8836f3..06f619193e9d5 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ServerResourceMethod.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/model/ServerResourceMethod.java @@ -19,6 +19,7 @@ public class ServerResourceMethod extends ResourceMethod { private List handlerChainCustomizers = new ArrayList<>(); private ParameterExtractor customerParameterExtractor; private String actualDeclaringClassName; + private String classDeclMethodThatHasJaxRsEndpointDefiningAnn; public ServerResourceMethod() { } @@ -79,4 +80,25 @@ public String getActualDeclaringClassName() { public void setActualDeclaringClassName(String actualDeclaringClassName) { this.actualDeclaringClassName = actualDeclaringClassName; } + + /** + * Returns a declaring class name of a resource method annotated with Jakarta REST endpoint defining annotations. + * This class can be different to {@link #getActualDeclaringClassName()} when this method is overridden on subclasses, + * or when method-level {@link jakarta.ws.rs.Path} is defined on non-default interface method. + * + * @return declaring class name if different to {@link #getActualDeclaringClassName()} or null + */ + public String getClassDeclMethodThatHasJaxRsEndpointDefiningAnn() { + return classDeclMethodThatHasJaxRsEndpointDefiningAnn; + } + + /** + * Sets a declaring class name of a resource method annotated with Jakarta REST endpoint defining annotations. + * Should only be set when the name is different to {@link #getActualDeclaringClassName()}. + * + * @param classDeclMethodThatHasJaxRsEndpointDefiningAnn class name + */ + public void setClassDeclMethodThatHasJaxRsEndpointDefiningAnn(String classDeclMethodThatHasJaxRsEndpointDefiningAnn) { + this.classDeclMethodThatHasJaxRsEndpointDefiningAnn = classDeclMethodThatHasJaxRsEndpointDefiningAnn; + } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ResteasyReactiveResourceInfo.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ResteasyReactiveResourceInfo.java index 014668cab4ebe..b370a8cf5a309 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ResteasyReactiveResourceInfo.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ResteasyReactiveResourceInfo.java @@ -132,6 +132,11 @@ public String getMethodId() { return methodId; } + /** + * @return declaring class of a method that returns endpoint response + * @deprecated if you need the method, please open an issue so that we can document and test your use case + */ + @Deprecated public String getActualDeclaringClassName() { return actualDeclaringClassName; } From ef216fe565f812329edda98b0577ac1eaea36ac6 Mon Sep 17 00:00:00 2001 From: Marco Bungart Date: Tue, 9 Jul 2024 20:42:08 +0200 Subject: [PATCH 74/94] maven-compiler-plugin: Use true instead of -parameters since https://issues.apache.org/jira/browse/MCOMPILER-413 has been closed --- docs/src/main/asciidoc/building-my-first-extension.adoc | 5 +---- extensions/tls-registry/cli/pom.xml | 4 +--- .../code/extension-base/java/pom.tpl.qute.xml | 4 +--- .../codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml | 4 +--- .../QuarkusCodestartGenerationTest/generateDefault/pom.xml | 4 +--- .../generateMavenWithCustomDep/pom.xml | 4 +--- .../testMavenContent/pom.xml | 4 +--- .../database-generator/pom.xml | 4 +--- .../expected/new-extension-current-directory-project/pom.xml | 4 +--- .../expected/new-extension-project/my-ext/pom.xml | 4 +--- .../projects/codegen-config-factory/pom.xml | 4 +--- .../test/resources-filtered/projects/config-tracking/pom.xml | 4 +--- .../resources-filtered/projects/extension-codestart/pom.xml | 4 +--- .../projects/extension-removed-resources/extension/pom.xml | 4 +--- .../projects/extension-removed-resources/resources/pom.xml | 4 +--- .../projects/extension-removed-resources/runner/pom.xml | 4 +--- .../extension-removed-resources/service-loader/pom.xml | 4 +--- .../extension-removed-resources/subatomic-provider/pom.xml | 4 +--- .../extension-removed-resources/supersonic-provider/pom.xml | 4 +--- .../projects/extension-test-with-no-main/pom.xml | 4 +--- .../projects/external-reloadable-artifacts/app/pom.xml | 4 +--- .../external-reloadable-artifacts/external-lib/pom.xml | 4 +--- .../projects/ignore-entries-uber-jar/pom.xml | 4 +--- .../resources-filtered/projects/modules-in-profiles/pom.xml | 4 +--- .../projects/multi-build-mode-parallel/module-1/pom.xml | 4 +--- .../projects/multi-build-mode-parallel/module-2/pom.xml | 4 +--- .../resources-filtered/projects/multi-build-mode/pom.xml | 4 +--- .../test/resources-filtered/projects/multijar-module/pom.xml | 4 +--- .../projects/multimodule-classpath/pom.xml | 4 +--- .../projects/multimodule-revision-prop/pom.xml | 4 +--- .../projects/multimodule-root-no-src/pom.xml | 4 +--- .../src/test/resources-filtered/projects/multimodule/pom.xml | 4 +--- .../projects/native-agent-integration/pom.xml | 4 +--- .../resources-filtered/projects/native-image-app/pom.xml | 4 +--- .../resources-filtered/projects/no-resource-root/pom.xml | 4 +--- .../resources-filtered/projects/pom-in-target-dir/pom.xml | 4 +--- .../projects/project-with-extension/pom.xml | 4 +--- .../resources-filtered/projects/property-expansion/pom.xml | 4 +--- .../resources-filtered/projects/property-overrides/pom.xml | 4 +--- .../src/test/resources-filtered/projects/proto-gen/pom.xml | 4 +--- .../projects/quarkus-index-dependencies-groupid/pom.xml | 4 +--- .../projects/quarkus-index-dependencies/pom.xml | 4 +--- .../projects/quarkus.package.output-directory/pom.xml | 4 +--- .../test/resources-filtered/projects/reactive-routes/pom.xml | 4 +--- .../projects/rest-client-custom-headers-extension/pom.xml | 4 +--- .../resources-filtered/projects/rr-with-json-logging/pom.xml | 4 +--- .../projects/test-module-dependency/pom.xml | 4 +--- .../projects/test-plugin-classpath-config/module-a/pom.xml | 4 +--- .../projects/test-plugin-classpath-config/module-b/pom.xml | 4 +--- .../projects/test-plugin-classpath-config/runner/pom.xml | 4 +--- .../resources-filtered/projects/test-source-sets/pom.xml | 4 +--- .../test/resources-filtered/projects/uberjar-check/pom.xml | 4 +--- .../projects/uberjar-maven-plugin-config/pom.xml | 4 +--- .../quarkus-my-quarkiverse-ext_pom.xml | 4 +--- .../testCreateStandaloneExtension/my-org-my-own-ext_pom.xml | 4 +--- .../project-using-test-callback-from-extension/pom.xml | 4 +--- .../projects/project-using-test-parameter-injection/pom.xml | 4 +--- .../pom.xml | 4 +--- .../project-using-test-template-from-extension/pom.xml | 4 +--- integration-tests/virtual-threads/pom.xml | 4 +--- 60 files changed, 60 insertions(+), 181 deletions(-) diff --git a/docs/src/main/asciidoc/building-my-first-extension.adoc b/docs/src/main/asciidoc/building-my-first-extension.adoc index ef66e3d96ade5..79c5cc7a03b9e 100644 --- a/docs/src/main/asciidoc/building-my-first-extension.adoc +++ b/docs/src/main/asciidoc/building-my-first-extension.adoc @@ -219,9 +219,7 @@ Your extension is a multi-module project. So let's start by checking out the par maven-compiler-plugin ${compiler-plugin.version} - - -parameters - + true @@ -234,7 +232,6 @@ Your extension is a multi-module project. So let's start by checking out the par <2> Quarkus requires a recent version of the Maven compiler plugin supporting the annotationProcessorPaths configuration. <3> The `quarkus-bom` aligns your dependencies with those used by Quarkus during the augmentation phase. <4> Quarkus requires these configs to run tests properly. -<5> Setting the `parameters` flag this way works around https://issues.apache.org/jira/browse/MCOMPILER-413[MCOMPILER-413]. ==== The Deployment module diff --git a/extensions/tls-registry/cli/pom.xml b/extensions/tls-registry/cli/pom.xml index 0d4c342a3b5a3..063e7df687d66 100644 --- a/extensions/tls-registry/cli/pom.xml +++ b/extensions/tls-registry/cli/pom.xml @@ -37,9 +37,7 @@ org.apache.maven.plugins maven-compiler-plugin - - -parameters - + true diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/extension-base/java/pom.tpl.qute.xml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/extension-base/java/pom.tpl.qute.xml index e69db96309167..20b16bad542d2 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/extension-base/java/pom.tpl.qute.xml +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus-extension/code/extension-base/java/pom.tpl.qute.xml @@ -113,9 +113,7 @@ maven-compiler-plugin $\{compiler-plugin.version} - - -parameters - + true {/if} diff --git a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml index 3d32afccb8efd..9ec868e3d27dc 100644 --- a/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml +++ b/independent-projects/tools/base-codestarts/src/main/resources/codestarts/quarkus/buildtool/maven/base/pom.tpl.qute.xml @@ -152,9 +152,7 @@ maven-compiler-plugin $\{compiler-plugin.version} - - -parameters - + true diff --git a/independent-projects/tools/devtools-testing/src/test/resources/__snapshots__/QuarkusCodestartGenerationTest/generateDefault/pom.xml b/independent-projects/tools/devtools-testing/src/test/resources/__snapshots__/QuarkusCodestartGenerationTest/generateDefault/pom.xml index 5eb35bfd30a83..9a085564a290f 100644 --- a/independent-projects/tools/devtools-testing/src/test/resources/__snapshots__/QuarkusCodestartGenerationTest/generateDefault/pom.xml +++ b/independent-projects/tools/devtools-testing/src/test/resources/__snapshots__/QuarkusCodestartGenerationTest/generateDefault/pom.xml @@ -63,9 +63,7 @@ maven-compiler-plugin ${compiler-plugin.version} - - -parameters - + true diff --git a/independent-projects/tools/devtools-testing/src/test/resources/__snapshots__/QuarkusCodestartGenerationTest/generateMavenWithCustomDep/pom.xml b/independent-projects/tools/devtools-testing/src/test/resources/__snapshots__/QuarkusCodestartGenerationTest/generateMavenWithCustomDep/pom.xml index 4479635a0f786..18bf1bcaa7d4c 100644 --- a/independent-projects/tools/devtools-testing/src/test/resources/__snapshots__/QuarkusCodestartGenerationTest/generateMavenWithCustomDep/pom.xml +++ b/independent-projects/tools/devtools-testing/src/test/resources/__snapshots__/QuarkusCodestartGenerationTest/generateMavenWithCustomDep/pom.xml @@ -78,9 +78,7 @@ maven-compiler-plugin ${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/devtools/src/test/resources/__snapshots__/KotlinSerializationCodestartTest/testMavenContent/pom.xml b/integration-tests/devtools/src/test/resources/__snapshots__/KotlinSerializationCodestartTest/testMavenContent/pom.xml index a0b307335b6ba..4b32362f47d80 100644 --- a/integration-tests/devtools/src/test/resources/__snapshots__/KotlinSerializationCodestartTest/testMavenContent/pom.xml +++ b/integration-tests/devtools/src/test/resources/__snapshots__/KotlinSerializationCodestartTest/testMavenContent/pom.xml @@ -92,9 +92,7 @@ maven-compiler-plugin ${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/pom.xml b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/pom.xml index 099c1bbfdbdca..0d373322a9011 100644 --- a/integration-tests/hibernate-orm-compatibility-5.6/database-generator/pom.xml +++ b/integration-tests/hibernate-orm-compatibility-5.6/database-generator/pom.xml @@ -67,9 +67,7 @@ maven-compiler-plugin ${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/expected/new-extension-current-directory-project/pom.xml b/integration-tests/maven/src/test/resources-filtered/expected/new-extension-current-directory-project/pom.xml index a3aa84bdee7ae..06e3b726f2358 100644 --- a/integration-tests/maven/src/test/resources-filtered/expected/new-extension-current-directory-project/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/expected/new-extension-current-directory-project/pom.xml @@ -42,9 +42,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/expected/new-extension-project/my-ext/pom.xml b/integration-tests/maven/src/test/resources-filtered/expected/new-extension-project/my-ext/pom.xml index a3aa84bdee7ae..06e3b726f2358 100644 --- a/integration-tests/maven/src/test/resources-filtered/expected/new-extension-project/my-ext/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/expected/new-extension-project/my-ext/pom.xml @@ -42,9 +42,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/codegen-config-factory/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/codegen-config-factory/pom.xml index 6916e694ca2cb..72e06cbfa9982 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/codegen-config-factory/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/codegen-config-factory/pom.xml @@ -67,9 +67,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/config-tracking/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/config-tracking/pom.xml index 2292abbebe470..7a3ddb7283149 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/config-tracking/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/config-tracking/pom.xml @@ -59,9 +59,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-codestart/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-codestart/pom.xml index 2931882b75b03..4e61ca542adc7 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/extension-codestart/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-codestart/pom.xml @@ -41,9 +41,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/extension/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/extension/pom.xml index 48a1816dc5aa5..65eda7f3ae601 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/extension/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/extension/pom.xml @@ -26,9 +26,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/resources/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/resources/pom.xml index e6a25f61dc158..b114c3dcf3956 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/resources/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/resources/pom.xml @@ -14,9 +14,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/runner/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/runner/pom.xml index ee473270c7259..84a6373dc2569 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/runner/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/runner/pom.xml @@ -47,9 +47,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/service-loader/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/service-loader/pom.xml index 95b25018b5ada..d5c1c7a54531b 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/service-loader/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/service-loader/pom.xml @@ -14,9 +14,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/subatomic-provider/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/subatomic-provider/pom.xml index f826901cf5ff9..ecdf6f0ab4092 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/subatomic-provider/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/subatomic-provider/pom.xml @@ -21,9 +21,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/supersonic-provider/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/supersonic-provider/pom.xml index 1eb6f96c04979..13b64853e3ff1 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/supersonic-provider/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-removed-resources/supersonic-provider/pom.xml @@ -21,9 +21,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/pom.xml index 72ebb3986f96e..49379ba29013e 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/pom.xml @@ -66,9 +66,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/external-reloadable-artifacts/app/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/external-reloadable-artifacts/app/pom.xml index 628ff8a19002f..5e01ea409f93d 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/external-reloadable-artifacts/app/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/external-reloadable-artifacts/app/pom.xml @@ -51,9 +51,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/external-reloadable-artifacts/external-lib/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/external-reloadable-artifacts/external-lib/pom.xml index cdd4d67673eac..e5d147eb8c276 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/external-reloadable-artifacts/external-lib/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/external-reloadable-artifacts/external-lib/pom.xml @@ -21,9 +21,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/ignore-entries-uber-jar/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/ignore-entries-uber-jar/pom.xml index 5fbf239542146..214e9a9036ea4 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/ignore-entries-uber-jar/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/ignore-entries-uber-jar/pom.xml @@ -45,9 +45,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/modules-in-profiles/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/modules-in-profiles/pom.xml index 1b4f9a9a3d2e7..23586bff5934a 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/modules-in-profiles/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/modules-in-profiles/pom.xml @@ -29,9 +29,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode-parallel/module-1/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode-parallel/module-1/pom.xml index 1ee66c5e58117..a1a72ba50de63 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode-parallel/module-1/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode-parallel/module-1/pom.xml @@ -19,9 +19,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode-parallel/module-2/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode-parallel/module-2/pom.xml index 89739aef3cc1b..6a452a5eb967c 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode-parallel/module-2/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode-parallel/module-2/pom.xml @@ -19,9 +19,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode/pom.xml index 655d20cb1d3c1..073e50a523a37 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/multi-build-mode/pom.xml @@ -65,9 +65,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/multijar-module/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/multijar-module/pom.xml index 7f9e82a9fbd4a..32b1d1dbebeee 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/multijar-module/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/multijar-module/pom.xml @@ -35,9 +35,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/multimodule-classpath/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/multimodule-classpath/pom.xml index 298165afd7821..98053830144e0 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/multimodule-classpath/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/multimodule-classpath/pom.xml @@ -33,9 +33,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/multimodule-revision-prop/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/multimodule-revision-prop/pom.xml index ef843750dca80..c89be6679a74d 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/multimodule-revision-prop/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/multimodule-revision-prop/pom.xml @@ -33,9 +33,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/multimodule-root-no-src/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/multimodule-root-no-src/pom.xml index 7288fb6ea06d6..1fef17d3fc669 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/multimodule-root-no-src/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/multimodule-root-no-src/pom.xml @@ -32,9 +32,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/multimodule/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/multimodule/pom.xml index 7288fb6ea06d6..1fef17d3fc669 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/multimodule/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/multimodule/pom.xml @@ -32,9 +32,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/native-agent-integration/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/native-agent-integration/pom.xml index d89154e0c4c31..7c00080373e7d 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/native-agent-integration/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/native-agent-integration/pom.xml @@ -50,9 +50,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/native-image-app/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/native-image-app/pom.xml index 7e8892cc066de..73ae150d0b1ce 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/native-image-app/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/native-image-app/pom.xml @@ -39,9 +39,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/no-resource-root/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/no-resource-root/pom.xml index fd0f8375667a8..bd89b92fbb69b 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/no-resource-root/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/no-resource-root/pom.xml @@ -40,9 +40,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/pom-in-target-dir/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/pom-in-target-dir/pom.xml index f045b59ab6618..cd8d0d067a275 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/pom-in-target-dir/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/pom-in-target-dir/pom.xml @@ -81,9 +81,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/project-with-extension/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/project-with-extension/pom.xml index 7e7459518dbb8..b4a41a6c51afb 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/project-with-extension/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/project-with-extension/pom.xml @@ -33,9 +33,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/property-expansion/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/property-expansion/pom.xml index 7e80f2c20470e..2e927f4e562dd 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/property-expansion/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/property-expansion/pom.xml @@ -60,9 +60,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/property-overrides/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/property-overrides/pom.xml index 1c12730c26749..196f9e6f902bc 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/property-overrides/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/property-overrides/pom.xml @@ -33,9 +33,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/proto-gen/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/proto-gen/pom.xml index 605fa3d101138..980dce8315e67 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/proto-gen/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/proto-gen/pom.xml @@ -54,9 +54,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/quarkus-index-dependencies-groupid/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/quarkus-index-dependencies-groupid/pom.xml index 57ae7bd9c0638..dda31b4f2630a 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/quarkus-index-dependencies-groupid/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/quarkus-index-dependencies-groupid/pom.xml @@ -30,9 +30,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/quarkus-index-dependencies/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/quarkus-index-dependencies/pom.xml index 57ae7bd9c0638..dda31b4f2630a 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/quarkus-index-dependencies/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/quarkus-index-dependencies/pom.xml @@ -30,9 +30,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/quarkus.package.output-directory/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/quarkus.package.output-directory/pom.xml index ab765e2b64080..ff5e577c36711 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/quarkus.package.output-directory/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/quarkus.package.output-directory/pom.xml @@ -51,9 +51,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/reactive-routes/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/reactive-routes/pom.xml index 973db66b1ece6..c499c0b96458d 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/reactive-routes/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/reactive-routes/pom.xml @@ -65,9 +65,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/rest-client-custom-headers-extension/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/rest-client-custom-headers-extension/pom.xml index caaf8861c1e61..3f6fcc0fc063a 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/rest-client-custom-headers-extension/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/rest-client-custom-headers-extension/pom.xml @@ -30,9 +30,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/rr-with-json-logging/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/rr-with-json-logging/pom.xml index cb1b31e8183b4..7f75f4ab13136 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/rr-with-json-logging/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/rr-with-json-logging/pom.xml @@ -68,9 +68,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-module-dependency/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-module-dependency/pom.xml index 5a617c756a0ec..addc36ab3851d 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/test-module-dependency/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-module-dependency/pom.xml @@ -33,9 +33,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/pom.xml index 5b868862ce80a..d4f669ec29313 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-a/pom.xml @@ -20,9 +20,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/pom.xml index 1b81d81df76a3..5cf20b55e26c9 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/module-b/pom.xml @@ -14,9 +14,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/pom.xml index 7677b2261b975..3a52ff2411f59 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-plugin-classpath-config/runner/pom.xml @@ -47,9 +47,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/test-source-sets/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/test-source-sets/pom.xml index 76879035d0574..00618d461f23e 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/test-source-sets/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/test-source-sets/pom.xml @@ -131,9 +131,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/uberjar-check/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/uberjar-check/pom.xml index 2e33bfb998c5f..ebe9179598d81 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/uberjar-check/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/uberjar-check/pom.xml @@ -58,9 +58,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources-filtered/projects/uberjar-maven-plugin-config/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/uberjar-maven-plugin-config/pom.xml index db5028c50e482..69bb10aa635e6 100644 --- a/integration-tests/maven/src/test/resources-filtered/projects/uberjar-maven-plugin-config/pom.xml +++ b/integration-tests/maven/src/test/resources-filtered/projects/uberjar-maven-plugin-config/pom.xml @@ -53,9 +53,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateQuarkiverseExtension/quarkus-my-quarkiverse-ext_pom.xml b/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateQuarkiverseExtension/quarkus-my-quarkiverse-ext_pom.xml index e591f173fef92..79a649df6b461 100644 --- a/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateQuarkiverseExtension/quarkus-my-quarkiverse-ext_pom.xml +++ b/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateQuarkiverseExtension/quarkus-my-quarkiverse-ext_pom.xml @@ -57,9 +57,7 @@ maven-compiler-plugin ${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateStandaloneExtension/my-org-my-own-ext_pom.xml b/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateStandaloneExtension/my-org-my-own-ext_pom.xml index 857ee512a7a4c..f1f292f2a956e 100644 --- a/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateStandaloneExtension/my-org-my-own-ext_pom.xml +++ b/integration-tests/maven/src/test/resources/__snapshots__/CreateExtensionMojoIT/testCreateStandaloneExtension/my-org-my-own-ext_pom.xml @@ -68,9 +68,7 @@ maven-compiler-plugin ${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-callback-from-extension/pom.xml b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-callback-from-extension/pom.xml index f41fb3e03aee8..2812de4f92b63 100644 --- a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-callback-from-extension/pom.xml +++ b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-callback-from-extension/pom.xml @@ -71,9 +71,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-parameter-injection/pom.xml b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-parameter-injection/pom.xml index 03e894f32d660..196f9cfdcb8fa 100644 --- a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-parameter-injection/pom.xml +++ b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-parameter-injection/pom.xml @@ -69,9 +69,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension-with-bytecode-changes/pom.xml b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension-with-bytecode-changes/pom.xml index 77a81d2ea5993..4ff4e0616b90f 100644 --- a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension-with-bytecode-changes/pom.xml +++ b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension-with-bytecode-changes/pom.xml @@ -71,9 +71,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension/pom.xml b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension/pom.xml index 77a81d2ea5993..4ff4e0616b90f 100644 --- a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension/pom.xml +++ b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension/pom.xml @@ -71,9 +71,7 @@ maven-compiler-plugin \${compiler-plugin.version} - - -parameters - + true diff --git a/integration-tests/virtual-threads/pom.xml b/integration-tests/virtual-threads/pom.xml index 1f925deeacc95..a7dada855dcaf 100644 --- a/integration-tests/virtual-threads/pom.xml +++ b/integration-tests/virtual-threads/pom.xml @@ -137,9 +137,7 @@ maven-compiler-plugin ${version.compiler.plugin} - - -parameters - + true From f34fa1a59fa464a0ca545ce0537f1b99e0ab9041 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Mon, 8 Jul 2024 11:28:51 +0200 Subject: [PATCH 75/94] QuarkusComponentTest: register default and support custom converters - fixes #41709 --- .../test/component/QuarkusComponentTest.java | 8 +++ .../QuarkusComponentTestConfiguration.java | 49 +++++++++++++++-- .../QuarkusComponentTestExtension.java | 4 +- .../QuarkusComponentTestExtensionBuilder.java | 28 +++++++++- .../config/ConfigConverterExtensionTest.java | 55 +++++++++++++++++++ .../component/config/ConfigConverterTest.java | 52 ++++++++++++++++++ 6 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java create mode 100644 test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTest.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTest.java index 3d66a0e200756..3af12981b584e 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTest.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTest.java @@ -6,6 +6,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import org.eclipse.microprofile.config.spi.Converter; import org.junit.jupiter.api.extension.ExtendWith; import io.quarkus.arc.processor.AnnotationsTransformer; @@ -72,4 +73,11 @@ */ Class[] annotationsTransformers() default {}; + /** + * The additional config converters. By default, the Quarkus-specific converters are registered. + * + * @see QuarkusComponentTestExtensionBuilder#addConverter(Converter) + */ + Class>[] configConverters() default {}; + } diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java index 8279bbf99c84b..8bde3f4282314 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java @@ -17,16 +17,42 @@ import jakarta.inject.Inject; import jakarta.inject.Provider; +import org.eclipse.microprofile.config.spi.Converter; import org.jboss.logging.Logger; import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.processor.AnnotationsTransformer; +import io.quarkus.runtime.configuration.CharsetConverter; +import io.quarkus.runtime.configuration.CidrAddressConverter; +import io.quarkus.runtime.configuration.DurationConverter; +import io.quarkus.runtime.configuration.InetAddressConverter; +import io.quarkus.runtime.configuration.InetSocketAddressConverter; +import io.quarkus.runtime.configuration.LocaleConverter; +import io.quarkus.runtime.configuration.MemorySizeConverter; +import io.quarkus.runtime.configuration.PathConverter; +import io.quarkus.runtime.configuration.RegexConverter; +import io.quarkus.runtime.configuration.ZoneIdConverter; +import io.quarkus.runtime.logging.LevelConverter; import io.quarkus.test.InjectMock; class QuarkusComponentTestConfiguration { + // As defined in /quarkus/core/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter + static final List> DEFAULT_CONVERTERS = List.of(new InetSocketAddressConverter(), + new CharsetConverter(), + new CidrAddressConverter(), + new InetAddressConverter(), + new RegexConverter(), + new PathConverter(), + new DurationConverter(), + new MemorySizeConverter(), + new LocaleConverter(), + new ZoneIdConverter(), + new LevelConverter()); + static final QuarkusComponentTestConfiguration DEFAULT = new QuarkusComponentTestConfiguration(Map.of(), List.of(), - List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, List.of()); + List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, List.of(), + DEFAULT_CONVERTERS); private static final Logger LOG = Logger.getLogger(QuarkusComponentTestConfiguration.class); @@ -37,11 +63,12 @@ class QuarkusComponentTestConfiguration { final boolean addNestedClassesAsComponents; final int configSourceOrdinal; final List annotationsTransformers; + final List> configConverters; QuarkusComponentTestConfiguration(Map configProperties, List> componentClasses, List> mockConfigurators, boolean useDefaultConfigProperties, boolean addNestedClassesAsComponents, int configSourceOrdinal, - List annotationsTransformers) { + List annotationsTransformers, List> configConverters) { this.configProperties = configProperties; this.componentClasses = componentClasses; this.mockConfigurators = mockConfigurators; @@ -49,6 +76,7 @@ class QuarkusComponentTestConfiguration { this.addNestedClassesAsComponents = addNestedClassesAsComponents; this.configSourceOrdinal = configSourceOrdinal; this.annotationsTransformers = annotationsTransformers; + this.configConverters = configConverters; } QuarkusComponentTestConfiguration update(Class testClass) { @@ -58,6 +86,7 @@ QuarkusComponentTestConfiguration update(Class testClass) { boolean addNestedClassesAsComponents = this.addNestedClassesAsComponents; int configSourceOrdinal = this.configSourceOrdinal; List annotationsTransformers = new ArrayList<>(this.annotationsTransformers); + List> configConverters = new ArrayList<>(this.configConverters); QuarkusComponentTest testAnnotation = testClass.getAnnotation(QuarkusComponentTest.class); if (testAnnotation != null) { @@ -71,7 +100,17 @@ QuarkusComponentTestConfiguration update(Class testClass) { try { annotationsTransformers.add(transformerClass.getDeclaredConstructor().newInstance()); } catch (Exception e) { - LOG.errorf("Unable to instantiate %s", transformerClass); + LOG.errorf(e, "Unable to instantiate %s", transformerClass); + } + } + } + Class>[] converters = testAnnotation.configConverters(); + if (converters.length > 0) { + for (Class> converterClass : converters) { + try { + configConverters.add(converterClass.getDeclaredConstructor().newInstance()); + } catch (Exception e) { + LOG.errorf(e, "Unable to instantiate %s", converterClass); } } } @@ -120,7 +159,7 @@ QuarkusComponentTestConfiguration update(Class testClass) { return new QuarkusComponentTestConfiguration(Map.copyOf(configProperties), List.copyOf(componentClasses), this.mockConfigurators, useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - List.copyOf(annotationsTransformers)); + List.copyOf(annotationsTransformers), List.copyOf(configConverters)); } QuarkusComponentTestConfiguration update(Method testMethod) { @@ -132,7 +171,7 @@ QuarkusComponentTestConfiguration update(Method testMethod) { } return new QuarkusComponentTestConfiguration(configProperties, componentClasses, mockConfigurators, useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - annotationsTransformers); + annotationsTransformers, configConverters); } private static boolean resolvesToBuiltinBean(Class rawType) { diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java index 2656adaa4fcb1..e8204f6d3f4ec 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java @@ -57,6 +57,7 @@ import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.eclipse.microprofile.config.spi.Converter; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; @@ -203,7 +204,7 @@ public QuarkusComponentTestExtension() { public QuarkusComponentTestExtension(Class... additionalComponentClasses) { this(new QuarkusComponentTestConfiguration(Map.of(), List.of(additionalComponentClasses), List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, - List.of())); + List.of(), List.of())); } QuarkusComponentTestExtension(QuarkusComponentTestConfiguration baseConfiguration) { @@ -421,6 +422,7 @@ private void startContainer(ExtensionContext context, Lifecycle testInstanceLife ClassLoader tccl = Thread.currentThread().getContextClassLoader(); SmallRyeConfigBuilder configBuilder = new SmallRyeConfigBuilder().forClassLoader(tccl) .addDefaultInterceptors() + .withConverters(configuration.configConverters.toArray(new Converter[] {})) .addDefaultSources() .withSources(new ApplicationPropertiesConfigSourceLoader.InFileSystem()) .withSources(new ApplicationPropertiesConfigSourceLoader.InClassPath()) diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java index e0c267e507065..cdf13d9c25c3a 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java @@ -7,6 +7,8 @@ import java.util.Map; import java.util.function.Function; +import org.eclipse.microprofile.config.spi.Converter; + import io.quarkus.arc.processor.AnnotationsTransformer; /** @@ -26,6 +28,7 @@ public class QuarkusComponentTestExtensionBuilder { private final List> componentClasses = new ArrayList<>(); private final List> mockConfigurators = new ArrayList<>(); private final List annotationsTransformers = new ArrayList<>(); + private final List> configConverters = new ArrayList<>(); private boolean useDefaultConfigProperties = false; private boolean addNestedClassesAsComponents = true; private int configSourceOrdinal = QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL; @@ -105,6 +108,17 @@ public QuarkusComponentTestExtensionBuilder addAnnotationsTransformer(Annotation return this; } + /** + * Add an additional {@link Converter}. By default, the Quarkus-specific converters are registered. + * + * @param transformer + * @return self + */ + public QuarkusComponentTestExtensionBuilder addConverter(Converter converter) { + configConverters.add(converter); + return this; + } + /** * Configure a new mock of a bean. *

@@ -124,10 +138,18 @@ public MockBeanConfigurator mock(Class beanClass) { * @return a new extension instance */ public QuarkusComponentTestExtension build() { + List> converters; + if (configConverters.isEmpty()) { + converters = QuarkusComponentTestConfiguration.DEFAULT_CONVERTERS; + } else { + converters = new ArrayList<>(QuarkusComponentTestConfiguration.DEFAULT_CONVERTERS); + converters.addAll(configConverters); + converters = List.copyOf(converters); + } return new QuarkusComponentTestExtension(new QuarkusComponentTestConfiguration(Map.copyOf(configProperties), - List.copyOf(componentClasses), - List.copyOf(mockConfigurators), useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - List.copyOf(annotationsTransformers))); + List.copyOf(componentClasses), List.copyOf(mockConfigurators), useDefaultConfigProperties, + addNestedClassesAsComponents, configSourceOrdinal, + List.copyOf(annotationsTransformers), converters)); } void registerMockBean(MockBeanConfiguratorImpl mock) { diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java new file mode 100644 index 0000000000000..2714e2ab0a135 --- /dev/null +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java @@ -0,0 +1,55 @@ +package io.quarkus.test.component.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import jakarta.annotation.Priority; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.component.QuarkusComponentTestExtension; +import io.quarkus.test.component.TestConfigProperty; + +public class ConfigConverterExtensionTest { + + @RegisterExtension + static final QuarkusComponentTestExtension extension = QuarkusComponentTestExtension.builder() + .addConverter(new CustomBooleanConverter()).build(); + + @TestConfigProperty(key = "my.boolean", value = "jo") + @TestConfigProperty(key = "my.duration", value = "5s") + @Test + public void testQuarkusDurationConverter(Foo foo) { + assertEquals(TimeUnit.SECONDS.toMillis(5), foo.durationVal.toMillis()); + assertTrue(foo.boolVal); + } + + @Singleton + public static class Foo { + + @ConfigProperty(name = "my.duration", defaultValue = "60s") + Duration durationVal; + + @ConfigProperty(name = "my.boolean") + boolean boolVal; + + } + + @SuppressWarnings("serial") + @Priority(300) + public static class CustomBooleanConverter implements Converter { + + @Override + public Boolean convert(String value) throws IllegalArgumentException, NullPointerException { + return "jo".equals(value) ? true : false; + } + + } +} diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java new file mode 100644 index 0000000000000..28c498b785beb --- /dev/null +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java @@ -0,0 +1,52 @@ +package io.quarkus.test.component.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import jakarta.annotation.Priority; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.component.QuarkusComponentTest; +import io.quarkus.test.component.TestConfigProperty; +import io.quarkus.test.component.config.ConfigConverterTest.CustomBooleanConverter; + +@QuarkusComponentTest(configConverters = CustomBooleanConverter.class) +public class ConfigConverterTest { + + @TestConfigProperty(key = "my.boolean", value = "jo") + @TestConfigProperty(key = "my.duration", value = "5s") + @Test + public void testQuarkusDurationConverter(Foo foo) { + assertEquals(TimeUnit.SECONDS.toMillis(5), foo.durationVal.toMillis()); + assertTrue(foo.boolVal); + } + + @Singleton + public static class Foo { + + @ConfigProperty(name = "my.duration", defaultValue = "60s") + Duration durationVal; + + @ConfigProperty(name = "my.boolean") + boolean boolVal; + + } + + @SuppressWarnings("serial") + @Priority(300) + public static class CustomBooleanConverter implements Converter { + + @Override + public Boolean convert(String value) throws IllegalArgumentException, NullPointerException { + return "jo".equals(value) ? true : false; + } + + } +} From 947bbdb6c0eeabbf3202fd2dd0e79f7c4573dc45 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Mon, 8 Jul 2024 16:17:11 +0200 Subject: [PATCH 76/94] Add QuarkusComponentTestExtensionBuilder.setConfigBuilderCustomizer() --- .../QuarkusComponentTestConfiguration.java | 13 +++-- .../QuarkusComponentTestExtension.java | 56 ++++++++++--------- .../QuarkusComponentTestExtensionBuilder.java | 21 ++++++- .../config/ConfigBuilderCustomizerTest.java | 52 +++++++++++++++++ .../config/ConfigConverterExtensionTest.java | 2 +- .../component/config/ConfigConverterTest.java | 2 +- 6 files changed, 113 insertions(+), 33 deletions(-) create mode 100644 test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigBuilderCustomizerTest.java diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java index 8bde3f4282314..1d6f226a713db 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import jakarta.enterprise.event.Event; import jakarta.enterprise.inject.Instance; @@ -34,6 +35,7 @@ import io.quarkus.runtime.configuration.ZoneIdConverter; import io.quarkus.runtime.logging.LevelConverter; import io.quarkus.test.InjectMock; +import io.smallrye.config.SmallRyeConfigBuilder; class QuarkusComponentTestConfiguration { @@ -52,7 +54,7 @@ class QuarkusComponentTestConfiguration { static final QuarkusComponentTestConfiguration DEFAULT = new QuarkusComponentTestConfiguration(Map.of(), List.of(), List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, List.of(), - DEFAULT_CONVERTERS); + DEFAULT_CONVERTERS, null); private static final Logger LOG = Logger.getLogger(QuarkusComponentTestConfiguration.class); @@ -64,11 +66,13 @@ class QuarkusComponentTestConfiguration { final int configSourceOrdinal; final List annotationsTransformers; final List> configConverters; + final Consumer configBuilderCustomizer; QuarkusComponentTestConfiguration(Map configProperties, List> componentClasses, List> mockConfigurators, boolean useDefaultConfigProperties, boolean addNestedClassesAsComponents, int configSourceOrdinal, - List annotationsTransformers, List> configConverters) { + List annotationsTransformers, List> configConverters, + Consumer configBuilderCustomizer) { this.configProperties = configProperties; this.componentClasses = componentClasses; this.mockConfigurators = mockConfigurators; @@ -77,6 +81,7 @@ class QuarkusComponentTestConfiguration { this.configSourceOrdinal = configSourceOrdinal; this.annotationsTransformers = annotationsTransformers; this.configConverters = configConverters; + this.configBuilderCustomizer = configBuilderCustomizer; } QuarkusComponentTestConfiguration update(Class testClass) { @@ -159,7 +164,7 @@ QuarkusComponentTestConfiguration update(Class testClass) { return new QuarkusComponentTestConfiguration(Map.copyOf(configProperties), List.copyOf(componentClasses), this.mockConfigurators, useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - List.copyOf(annotationsTransformers), List.copyOf(configConverters)); + List.copyOf(annotationsTransformers), List.copyOf(configConverters), configBuilderCustomizer); } QuarkusComponentTestConfiguration update(Method testMethod) { @@ -171,7 +176,7 @@ QuarkusComponentTestConfiguration update(Method testMethod) { } return new QuarkusComponentTestConfiguration(configProperties, componentClasses, mockConfigurators, useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - annotationsTransformers, configConverters); + annotationsTransformers, configConverters, configBuilderCustomizer); } private static boolean resolvesToBuiltinBean(Class rawType) { diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java index e8204f6d3f4ec..5896ee4f04115 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java @@ -79,6 +79,7 @@ import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; @@ -204,7 +205,7 @@ public QuarkusComponentTestExtension() { public QuarkusComponentTestExtension(Class... additionalComponentClasses) { this(new QuarkusComponentTestConfiguration(Map.of(), List.of(additionalComponentClasses), List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, - List.of(), List.of())); + List.of(), List.of(), null)); } QuarkusComponentTestExtension(QuarkusComponentTestConfiguration baseConfiguration) { @@ -252,7 +253,7 @@ public void afterEach(ExtensionContext context) throws Exception { @Override public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception { long start = System.nanoTime(); - context.getRoot().getStore(NAMESPACE).put(KEY_TEST_INSTANCE, testInstance); + store(context).put(KEY_TEST_INSTANCE, testInstance); LOG.debugf("postProcessTestInstance: %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); } @@ -322,7 +323,7 @@ && isTestMethod(parameterContext.getDeclaringExecutable()) public Object resolveParameter(ParameterContext parameterContext, ExtensionContext context) throws ParameterResolutionException { @SuppressWarnings("unchecked") - List> injectedParams = context.getRoot().getStore(NAMESPACE).get(KEY_INJECTED_PARAMS, List.class); + List> injectedParams = store(context).get(KEY_INJECTED_PARAMS, List.class); ArcContainer container = Arc.container(); BeanManager beanManager = container.beanManager(); java.lang.reflect.Type requiredType = parameterContext.getParameter().getParameterizedType(); @@ -339,7 +340,7 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte private void destroyDependentTestMethodParams(ExtensionContext context) { @SuppressWarnings("unchecked") - List> injectedParams = context.getRoot().getStore(NAMESPACE).get(KEY_INJECTED_PARAMS, List.class); + List> injectedParams = store(context).get(KEY_INJECTED_PARAMS, List.class); for (InstanceHandle handle : injectedParams) { if (handle.getBean() != null && handle.getBean().getScope().equals(Dependent.class)) { try { @@ -355,17 +356,17 @@ private void destroyDependentTestMethodParams(ExtensionContext context) { private void buildContainer(ExtensionContext context) { QuarkusComponentTestConfiguration testClassConfiguration = baseConfiguration .update(context.getRequiredTestClass()); - context.getRoot().getStore(NAMESPACE).put(KEY_TEST_CLASS_CONFIG, testClassConfiguration); + store(context).put(KEY_TEST_CLASS_CONFIG, testClassConfiguration); ClassLoader oldTccl = initArcContainer(context, testClassConfiguration); - context.getRoot().getStore(NAMESPACE).put(KEY_OLD_TCCL, oldTccl); + store(context).put(KEY_OLD_TCCL, oldTccl); } @SuppressWarnings("unchecked") private void cleanup(ExtensionContext context) { - ClassLoader oldTccl = context.getRoot().getStore(NAMESPACE).get(KEY_OLD_TCCL, ClassLoader.class); + ClassLoader oldTccl = store(context).get(KEY_OLD_TCCL, ClassLoader.class); Thread.currentThread().setContextClassLoader(oldTccl); - context.getRoot().getStore(NAMESPACE).remove(KEY_CONFIG_MAPPINGS); - Set generatedResources = context.getRoot().getStore(NAMESPACE).get(KEY_GENERATED_RESOURCES, Set.class); + store(context).remove(KEY_CONFIG_MAPPINGS); + Set generatedResources = store(context).get(KEY_GENERATED_RESOURCES, Set.class); for (Path path : generatedResources) { try { LOG.debugf("Delete generated %s", path); @@ -379,7 +380,7 @@ private void cleanup(ExtensionContext context) { @SuppressWarnings("unchecked") private void stopContainer(ExtensionContext context, Lifecycle testInstanceLifecycle) throws Exception { if (testInstanceLifecycle.equals(context.getTestInstanceLifecycle().orElse(Lifecycle.PER_METHOD))) { - for (FieldInjector fieldInjector : (List) context.getRoot().getStore(NAMESPACE) + for (FieldInjector fieldInjector : (List) store(context) .get(KEY_INJECTED_FIELDS, List.class)) { fieldInjector.unset(context.getRequiredTestInstance()); } @@ -392,10 +393,10 @@ private void stopContainer(ExtensionContext context, Lifecycle testInstanceLifec ConfigBeanCreator.clear(); InterceptorMethodCreator.clear(); - SmallRyeConfig config = context.getRoot().getStore(NAMESPACE).get(KEY_CONFIG, SmallRyeConfig.class); + SmallRyeConfig config = store(context).get(KEY_CONFIG, SmallRyeConfig.class); ConfigProviderResolver.instance().releaseConfig(config); ConfigProviderResolver - .setInstance(context.getRoot().getStore(NAMESPACE).get(KEY_OLD_CONFIG_PROVIDER_RESOLVER, + .setInstance(store(context).get(KEY_OLD_CONFIG_PROVIDER_RESOLVER, ConfigProviderResolver.class)); } } @@ -405,15 +406,15 @@ private void startContainer(ExtensionContext context, Lifecycle testInstanceLife // Init ArC Arc.initialize(); - QuarkusComponentTestConfiguration configuration = context.getRoot().getStore(NAMESPACE) - .get(KEY_TEST_CLASS_CONFIG, QuarkusComponentTestConfiguration.class); + QuarkusComponentTestConfiguration configuration = store(context).get(KEY_TEST_CLASS_CONFIG, + QuarkusComponentTestConfiguration.class); Optional testMethod = context.getTestMethod(); if (testMethod.isPresent()) { configuration = configuration.update(testMethod.get()); } ConfigProviderResolver oldConfigProviderResolver = ConfigProviderResolver.instance(); - context.getRoot().getStore(NAMESPACE).put(KEY_OLD_CONFIG_PROVIDER_RESOLVER, oldConfigProviderResolver); + store(context).put(KEY_OLD_CONFIG_PROVIDER_RESOLVER, oldConfigProviderResolver); SmallRyeConfigProviderResolver smallRyeConfigProviderResolver = new SmallRyeConfigProviderResolver(); ConfigProviderResolver.setInstance(smallRyeConfigProviderResolver); @@ -430,28 +431,33 @@ private void startContainer(ExtensionContext context, Lifecycle testInstanceLife new QuarkusComponentTestConfigSource(configuration.configProperties, configuration.configSourceOrdinal)); @SuppressWarnings("unchecked") - Set configMappings = context.getRoot().getStore(NAMESPACE).get(KEY_CONFIG_MAPPINGS, - Set.class); + Set configMappings = store(context).get(KEY_CONFIG_MAPPINGS, Set.class); if (configMappings != null) { // Register the mappings found during bean discovery for (ConfigClassWithPrefix mapping : configMappings) { configBuilder.withMapping(mapping.getKlass(), mapping.getPrefix()); } } + if (configuration.configBuilderCustomizer != null) { + configuration.configBuilderCustomizer.accept(configBuilder); + } SmallRyeConfig config = configBuilder.build(); smallRyeConfigProviderResolver.registerConfig(config, tccl); - context.getRoot().getStore(NAMESPACE).put(KEY_CONFIG, config); + store(context).put(KEY_CONFIG, config); ConfigBeanCreator.setClassLoader(tccl); // Inject fields declated on the test class Object testInstance = context.getRequiredTestInstance(); - context.getRoot().getStore(NAMESPACE).put(KEY_INJECTED_FIELDS, - injectFields(context.getRequiredTestClass(), testInstance)); + store(context).put(KEY_INJECTED_FIELDS, injectFields(context.getRequiredTestClass(), testInstance)); // Injected test method parameters - context.getRoot().getStore(NAMESPACE).put(KEY_INJECTED_PARAMS, new CopyOnWriteArrayList<>()); + store(context).put(KEY_INJECTED_PARAMS, new CopyOnWriteArrayList<>()); } } + private Store store(ExtensionContext context) { + return context.getRoot().getStore(NAMESPACE); + } + private BeanRegistrar registrarForMock(MockBeanConfiguratorImpl mock) { return new BeanRegistrar() { @@ -633,7 +639,7 @@ public void writeResource(Resource resource) throws IOException { }); } - extensionContext.getRoot().getStore(NAMESPACE).put(KEY_GENERATED_RESOURCES, generatedResources); + store(extensionContext).put(KEY_GENERATED_RESOURCES, generatedResources); builder.addAnnotationTransformation(AnnotationsTransformer.appliedToField().whenContainsAny(qualifiers) .whenContainsNone(DotName.createSimple(Inject.class)).thenTransform(t -> t.add(Inject.class))); @@ -783,7 +789,7 @@ public void register(RegistrationContext registrationContext) { .configClassWithPrefix(ConfigMappingBeanCreator.tryLoad(mapping), e.getKey())); } } - extensionContext.getRoot().getStore(NAMESPACE).put(KEY_CONFIG_MAPPINGS, configMappings); + store(extensionContext).put(KEY_CONFIG_MAPPINGS, configMappings); } LOG.debugf("Test injection points analyzed in %s ms [found: %s, mocked: %s]", @@ -850,7 +856,7 @@ public void accept(BytecodeTransformer transformer) { return oldTccl; } - private void processTestInterceptorMethods(Class testClass, ExtensionContext extensionContext, + private void processTestInterceptorMethods(Class testClass, ExtensionContext context, BeanRegistrar.RegistrationContext registrationContext, Set interceptorBindings) { List> annotations = List.of(AroundInvoke.class, PostConstruct.class, PreDestroy.class, AroundConstruct.class); @@ -873,7 +879,7 @@ private void processTestInterceptorMethods(Class testClass, ExtensionContext return ic -> { Object instance = null; if (!Modifier.isStatic(method.getModifiers())) { - Object testInstance = extensionContext.getRoot().getStore(NAMESPACE).get(KEY_TEST_INSTANCE); + Object testInstance = store(context).get(KEY_TEST_INSTANCE); if (testInstance == null) { throw new IllegalStateException("Test instance not available"); } diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java index cdf13d9c25c3a..b1fdccb0d1aeb 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java @@ -5,11 +5,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Function; import org.eclipse.microprofile.config.spi.Converter; import io.quarkus.arc.processor.AnnotationsTransformer; +import io.smallrye.config.SmallRyeConfigBuilder; /** * Convenient builder for {@link QuarkusComponentTestExtension}. @@ -32,6 +34,7 @@ public class QuarkusComponentTestExtensionBuilder { private boolean useDefaultConfigProperties = false; private boolean addNestedClassesAsComponents = true; private int configSourceOrdinal = QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL; + private Consumer configBuilderCustomizer; /** * The initial set of components under test is derived from the test class. The types of all fields annotated with @@ -111,14 +114,28 @@ public QuarkusComponentTestExtensionBuilder addAnnotationsTransformer(Annotation /** * Add an additional {@link Converter}. By default, the Quarkus-specific converters are registered. * - * @param transformer + * @param converter * @return self + * @see #setConfigBuilderCustomizer(Consumer) */ public QuarkusComponentTestExtensionBuilder addConverter(Converter converter) { configConverters.add(converter); return this; } + /** + * Set the {@link SmallRyeConfigBuilder} customizer. + *

+ * The customizer can affect the configuration of a test method and should be used with caution. + * + * @param customizer + * @return self + */ + public QuarkusComponentTestExtensionBuilder setConfigBuilderCustomizer(Consumer customizer) { + this.configBuilderCustomizer = customizer; + return this; + } + /** * Configure a new mock of a bean. *

@@ -149,7 +166,7 @@ public QuarkusComponentTestExtension build() { return new QuarkusComponentTestExtension(new QuarkusComponentTestConfiguration(Map.copyOf(configProperties), List.copyOf(componentClasses), List.copyOf(mockConfigurators), useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - List.copyOf(annotationsTransformers), converters)); + List.copyOf(annotationsTransformers), converters, configBuilderCustomizer)); } void registerMockBean(MockBeanConfiguratorImpl mock) { diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigBuilderCustomizerTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigBuilderCustomizerTest.java new file mode 100644 index 0000000000000..6150ea7aff01a --- /dev/null +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigBuilderCustomizerTest.java @@ -0,0 +1,52 @@ +package io.quarkus.test.component.config; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.function.Consumer; + +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.component.QuarkusComponentTestExtension; +import io.quarkus.test.component.TestConfigProperty; +import io.smallrye.config.SmallRyeConfigBuilder; + +public class ConfigBuilderCustomizerTest { + + @RegisterExtension + static final QuarkusComponentTestExtension extension = QuarkusComponentTestExtension.builder() + .setConfigBuilderCustomizer(new Consumer() { + @Override + public void accept(SmallRyeConfigBuilder builder) { + builder.withConverter(Boolean.class, 300, new CustomBooleanConverter()); + } + }).build(); + + @TestConfigProperty(key = "my.boolean", value = "jo") + @Test + public void testBuilderCustomizer(Foo foo) { + assertTrue(foo.boolVal); + } + + @Singleton + public static class Foo { + + @ConfigProperty(name = "my.boolean") + boolean boolVal; + + } + + @SuppressWarnings("serial") + public static class CustomBooleanConverter implements Converter { + + @Override + public Boolean convert(String value) throws IllegalArgumentException, NullPointerException { + return "jo".equals(value) ? true : false; + } + + } +} diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java index 2714e2ab0a135..3b4eb785f45f5 100644 --- a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java @@ -26,7 +26,7 @@ public class ConfigConverterExtensionTest { @TestConfigProperty(key = "my.boolean", value = "jo") @TestConfigProperty(key = "my.duration", value = "5s") @Test - public void testQuarkusDurationConverter(Foo foo) { + public void testConverters(Foo foo) { assertEquals(TimeUnit.SECONDS.toMillis(5), foo.durationVal.toMillis()); assertTrue(foo.boolVal); } diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java index 28c498b785beb..423df0e2e41ea 100644 --- a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java @@ -23,7 +23,7 @@ public class ConfigConverterTest { @TestConfigProperty(key = "my.boolean", value = "jo") @TestConfigProperty(key = "my.duration", value = "5s") @Test - public void testQuarkusDurationConverter(Foo foo) { + public void testConverters(Foo foo) { assertEquals(TimeUnit.SECONDS.toMillis(5), foo.durationVal.toMillis()); assertTrue(foo.boolVal); } From 2517290b0c15c1979c2c831ccef5a1c308dd2c97 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Mon, 8 Jul 2024 17:13:30 +0200 Subject: [PATCH 77/94] WebSockets Next: add support for Kotlin suspend functions Kotlin `suspend` functions are treated like Java methods that return `Uni`. That is, they are considered non-blocking. The implementation uses CDI method invokers (to avoid custom bytecode generation), which actually convert the `suspend` function result into a `Uni` under the hood. With this commit, only single-shot `suspend` functions are supported; `suspend` functions returning `Flow` are not supported yet. --- bom/application/pom.xml | 5 + docs/src/main/asciidoc/kotlin.adoc | 3 + .../asciidoc/websockets-next-reference.adoc | 28 ++- extensions/websockets-next/deployment/pom.xml | 72 ++++++ .../websockets/next/deployment/Callback.java | 20 +- .../KotlinContinuationCallbackArgument.java | 17 ++ .../next/deployment/WebSocketProcessor.java | 225 +++++++++++++----- .../websockets/next/test/utils/WSClient.java | 7 + .../websockets/next/test/kotlin/BinaryEcho.kt | 14 ++ .../next/test/kotlin/BinaryEchoSuspend.kt | 15 ++ .../websockets/next/test/kotlin/Echo.kt | 12 + .../next/test/kotlin/EchoSuspend.kt | 14 ++ .../test/kotlin/KotlinWebSocketClientTest.kt | 93 ++++++++ .../KotlinWebSocketSessionContextTest.kt | 85 +++++++ .../KotlinWebSocketSuspendingClientTest.kt | 99 ++++++++ .../next/test/kotlin/KotlinWebSocketTest.kt | 75 ++++++ .../websockets/next/test/kotlin/Message.kt | 5 + extensions/websockets-next/kotlin/pom.xml | 115 +++++++++ .../kotlin/ApplicationCoroutineScope.kt | 18 ++ .../next/runtime/kotlin/CoroutineInvoker.kt | 37 +++ .../next/runtime/kotlin/VertxDispatcher.kt | 24 ++ extensions/websockets-next/pom.xml | 1 + extensions/websockets-next/runtime/pom.xml | 4 + .../quarkus/arc/processor/KotlinDotNames.java | 7 +- .../io/quarkus/arc/processor/KotlinUtils.java | 25 ++ 25 files changed, 941 insertions(+), 79 deletions(-) create mode 100644 extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/KotlinContinuationCallbackArgument.java create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/BinaryEcho.kt create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/BinaryEchoSuspend.kt create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/Echo.kt create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/EchoSuspend.kt create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketClientTest.kt create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketSessionContextTest.kt create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketSuspendingClientTest.kt create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketTest.kt create mode 100644 extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/Message.kt create mode 100644 extensions/websockets-next/kotlin/pom.xml create mode 100644 extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/ApplicationCoroutineScope.kt create mode 100644 extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/CoroutineInvoker.kt create mode 100644 extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/VertxDispatcher.kt diff --git a/bom/application/pom.xml b/bom/application/pom.xml index d5befe87004f0..cd8cc958ec44b 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -2166,6 +2166,11 @@ quarkus-websockets-next-deployment ${project.version} + + io.quarkus + quarkus-websockets-next-kotlin + ${project.version} + io.quarkus quarkus-undertow-spi diff --git a/docs/src/main/asciidoc/kotlin.adoc b/docs/src/main/asciidoc/kotlin.adoc index 84e2e61be86fd..2ff3227ba340a 100644 --- a/docs/src/main/asciidoc/kotlin.adoc +++ b/docs/src/main/asciidoc/kotlin.adoc @@ -504,6 +504,9 @@ The following extensions provide support for Kotlin Coroutines by allowing the u |`quarkus-vertx` |Support is provided for `@ConsumeEvent` methods +|`quarkus-websockets-next` +|Support is provided for server-side and client-side endpoint methods + |=== === Kotlin coroutines and Mutiny diff --git a/docs/src/main/asciidoc/websockets-next-reference.adoc b/docs/src/main/asciidoc/websockets-next-reference.adoc index ff4bf6b790da1..3ccf664f55127 100644 --- a/docs/src/main/asciidoc/websockets-next-reference.adoc +++ b/docs/src/main/asciidoc/websockets-next-reference.adoc @@ -180,7 +180,7 @@ The session context remains active until the `@OnClose` method completes executi In cases where a WebSocket endpoint does not declare an `@OnOpen` method, the session context is still created. It remains active until the connection terminates, regardless of the presence of an `@OnClose` method. -Methods annotated with `@OnTextMessage,` `@OnBinaryMessage,` `@OnOpen`, and `@OnClose` also have the request scoped activated for the duration of the method execution (until it produced its result). +Methods annotated with `@OnTextMessage,` `@OnBinaryMessage,` `@OnOpen`, and `@OnClose` also have the request scope activated for the duration of the method execution (until it produced its result). [[callback-methods]] === Callback methods @@ -224,6 +224,7 @@ Here are the rules governing execution: * When `@RunOnVirtualThread` is employed, each invocation spawns a new virtual thread. * Methods returning `CompletionStage`, `Uni` and `Multi` are considered non-blocking. * Methods returning `void` or plain objects are considered blocking. +* Kotlin `suspend` functions are considered non-blocking. ==== Method parameters @@ -248,10 +249,12 @@ The method must subscribe to the `Multi` to receive these items (or return a Mul Methods annotated with `@OnTextMessage` or `@OnBinaryMessage` can return various types to handle WebSocket communication efficiently: * `void`: Indicates a blocking method where no explicit response is sent back to the client. -* `Uni`: Denotes a non-blocking method where the completion of the returned Uni signifies the end of processing. No explicit response is sent back to the client. +* `Uni`: Denotes a non-blocking method where the completion of the returned `Uni` signifies the end of processing. No explicit response is sent back to the client. * An object of type `X` represents a blocking method in which the returned object is serialized and sent back to the client as a response. * `Uni`: Specifies a non-blocking method where the item emitted by the non-null `Uni` is sent to the client as a response. * `Multi`: Indicates a non-blocking method where the items emitted by the non-null `Multi` are sequentially sent to the client until completion or cancellation. +* Kotlin `suspend` function returning `Unit`: Denotes a non-blocking method where no explicit response is sent back to the client. +* Kotlin `suspend` function returning `X`: Specifies a non-blocking method where the returned item is sent to the client as a response. Here are some examples of these methods: @@ -381,6 +384,8 @@ The supported return types for `@OnOpen` methods are: * An object of type `X`: Represents a blocking method where the returned object is serialized and sent back to the client. * `Uni`: Specifies a non-blocking method where the item emitted by the non-null `Uni` is sent to the client. * `Multi`: Indicates a non-blocking method where the items emitted by the non-null `Multi` are sequentially sent to the client until completion or cancellation. +* Kotlin `suspend` function returning `Unit`: Denotes a non-blocking method where no explicit message is sent back to the client. +* Kotlin `suspend` function returning `X`: Specifies a non-blocking method where the returned item is sent to the client. Items sent to the client are <> except for the `String`, `io.vertx.core.json.JsonObject`, `io.vertx.core.json.JsonArray`, `io.vertx.core.buffer.Buffer`, and `byte[]` types. In the case of `Multi`, Quarkus subscribes to the returned `Multi` and writes the items to the `WebSocket` as they are emitted. @@ -391,6 +396,7 @@ For `@OnClose` methods, the supported return types include: * `void`: The method is considered blocking. * `Uni`: The method is considered non-blocking. +* Kotlin `suspend` function returning `Unit`: The method is considered non-blocking. NOTE: `@OnClose` methods declared on a server endpoint cannot send items to the connected client by returning objects. They can only send messages to the other clients by using the `WebSocketConnection` object. @@ -424,7 +430,7 @@ Alternatively, an error message can be logged or no operation performed. The WebSocket Next extension supports automatic serialization and deserialization of messages. -Objects of type `String`, `JsonObject`, `JsonArray`, `Buffer`, and `byte[]` are sent as-is and by-pass the serialization and deserialization. +Objects of type `String`, `JsonObject`, `JsonArray`, `Buffer`, and `byte[]` are sent as-is and bypass the serialization and deserialization. When no codec is provided, the serialization and deserialization convert the message from/to JSON automatically. When you need to customize the serialization and deserialization, you can provide a custom codec. @@ -485,7 +491,7 @@ Item find(Item item) { //.... } ---- -1. Specify the codec to use for both the deserialization of the incoming message +1. Specify the codec to use for the deserialization of the incoming message 2. Specify the codec to use for the serialization of the outgoing message === Ping/pong messages @@ -509,7 +515,7 @@ quarkus.websockets-next.server.auto-ping-interval=2 <1> The `@OnPongMessage` annotation is used to define a callback that consumes pong messages sent from the client/server. An endpoint must declare at most one method annotated with `@OnPongMessage`. -The callback method must return either `void` or `Uni`, and it must accept a single parameter of type `Buffer`. +The callback method must return either `void` or `Uni` (or be a Kotlin `suspend` function returning `Unit`), and it must accept a single parameter of type `Buffer`. [source,java] ---- @@ -539,18 +545,18 @@ This extension reuses the _main_ HTTP server. Thus, the configuration of the WebSocket server is done in the `quarkus.http.` configuration section. -WebSocket paths configured within the application are concatenated with the root path defined by `quarkus.http.root` (which defaults to /). +WebSocket paths configured within the application are concatenated with the root path defined by `quarkus.http.root` (which defaults to `/`). This concatenation ensures that WebSocket endpoints are appropriately positioned within the application's URL structure. Refer to the xref:http-reference.adoc[HTTP guide] for more details. === Sub-websockets endpoints -A `@WebSocket` endpoint can encapsulate static nested classes, which are also annotated with /`@WebSocket` and represent _sub-websockets_. -The resulting path of these sub-web sockets concatenates the path from the enclosing class and the nested class. +A `@WebSocket` endpoint can encapsulate static nested classes, which are also annotated with `@WebSocket` and represent _sub-websockets_. +The resulting path of these sub-websockets concatenates the path from the enclosing class and the nested class. The resulting path is normalized, following the HTTP URL rules. -Sub-web sockets inherit access to the path parameters declared in the `@WebSocket` annotation of both the enclosing and nested classes. +Sub-websockets inherit access to the path parameters declared in the `@WebSocket` annotation of both the enclosing and nested classes. The `consumePrimary` method within the enclosing class can access the `version` parameter in the following example. Meanwhile, the `consumeNested` method within the nested class can access both `version` and `id` parameters: @@ -884,9 +890,9 @@ public class MyBean { <4> Set the execution model for callback handlers. By default, the callback may block the current thread. However in this case, the callback is executed on the event loop and may not block the current thread. <5> The lambda will be called for every text message sent from the server. -The basic connector is closed to a low-level API and is reserved for advanced users. +The basic connector is closer to a low-level API and is reserved for advanced users. However, unlike others low-level WebSocket clients, it is still a CDI bean and can be injected in other beans. -It also provides a way to configure the execution model of the callbacks, ensuring the optimal integration with the rest of Quarkus. +It also provides a way to configure the execution model of the callbacks, ensuring optimal integration with the rest of Quarkus. [[ws-client-connection]] === WebSocket client connection diff --git a/extensions/websockets-next/deployment/pom.xml b/extensions/websockets-next/deployment/pom.xml index 03928d9cbb66e..3b62c8108947a 100644 --- a/extensions/websockets-next/deployment/pom.xml +++ b/extensions/websockets-next/deployment/pom.xml @@ -65,10 +65,56 @@ smallrye-certificate-generator-junit5 test + + org.jetbrains.kotlin + kotlin-stdlib + test + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + test + + + io.smallrye.reactive + mutiny-kotlin + test + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + + compile + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + + + test-compile + + test-compile + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + + + maven-compiler-plugin @@ -80,6 +126,32 @@ + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + maven-surefire-plugin diff --git a/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/Callback.java b/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/Callback.java index 575230301ae2f..5c7df46e344b2 100644 --- a/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/Callback.java +++ b/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/Callback.java @@ -16,7 +16,10 @@ import io.quarkus.arc.deployment.TransformedAnnotationsBuildItem; import io.quarkus.arc.processor.Annotations; +import io.quarkus.arc.processor.BeanInfo; import io.quarkus.arc.processor.DotNames; +import io.quarkus.arc.processor.KotlinDotNames; +import io.quarkus.arc.processor.KotlinUtils; import io.quarkus.gizmo.BytecodeCreator; import io.quarkus.gizmo.FieldDescriptor; import io.quarkus.gizmo.ResultHandle; @@ -35,15 +38,17 @@ public class Callback { public final Target target; public final String endpointPath; public final AnnotationInstance annotation; + public final BeanInfo bean; public final MethodInfo method; public final ExecutionModel executionModel; public final MessageType messageType; public final List arguments; - public Callback(Target target, AnnotationInstance annotation, MethodInfo method, ExecutionModel executionModel, - CallbackArgumentsBuildItem callbackArguments, TransformedAnnotationsBuildItem transformedAnnotations, - String endpointPath, IndexView index) { + public Callback(Target target, AnnotationInstance annotation, BeanInfo bean, MethodInfo method, + ExecutionModel executionModel, CallbackArgumentsBuildItem callbackArguments, + TransformedAnnotationsBuildItem transformedAnnotations, String endpointPath, IndexView index) { this.target = target; + this.bean = bean; this.method = method; this.annotation = annotation; this.executionModel = executionModel; @@ -104,6 +109,15 @@ public boolean isReturnTypeMulti() { return WebSocketDotNames.MULTI.equals(returnType().name()); } + public boolean isKotlinSuspendFunction() { + return KotlinUtils.isKotlinSuspendMethod(method); + } + + public boolean isKotlinSuspendFunctionReturningUnit() { + return KotlinUtils.isKotlinSuspendMethod(method) + && KotlinUtils.getKotlinSuspendMethodResult(method).name().equals(KotlinDotNames.UNIT); + } + public boolean acceptsMessage() { return messageType != MessageType.NONE; } diff --git a/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/KotlinContinuationCallbackArgument.java b/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/KotlinContinuationCallbackArgument.java new file mode 100644 index 0000000000000..fa6eda14b4f14 --- /dev/null +++ b/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/KotlinContinuationCallbackArgument.java @@ -0,0 +1,17 @@ +package io.quarkus.websockets.next.deployment; + +import io.quarkus.arc.processor.KotlinUtils; +import io.quarkus.gizmo.ResultHandle; + +public class KotlinContinuationCallbackArgument implements CallbackArgument { + @Override + public boolean matches(ParameterContext context) { + return KotlinUtils.isKotlinContinuationParameter(context.parameter()); + } + + @Override + public ResultHandle get(InvocationBytecodeContext context) { + // the actual value is provided by the invoker + return context.bytecode().loadNull(); + } +} diff --git a/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java b/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java index 4ed22a9985603..a6e5c44f375d7 100644 --- a/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java +++ b/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java @@ -17,18 +17,21 @@ import java.util.stream.Collectors; import jakarta.enterprise.context.SessionScoped; +import jakarta.enterprise.invoke.Invoker; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTransformation; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassInfo.NestingType; +import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; import org.jboss.jandex.MethodInfo; import org.jboss.jandex.PrimitiveType; import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; +import org.objectweb.asm.Opcodes; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; @@ -39,6 +42,7 @@ import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem; import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem.ContextConfiguratorBuildItem; import io.quarkus.arc.deployment.CustomScopeBuildItem; +import io.quarkus.arc.deployment.InvokerFactoryBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeansRuntimeInitBuildItem; import io.quarkus.arc.deployment.TransformedAnnotationsBuildItem; @@ -50,7 +54,11 @@ import io.quarkus.arc.processor.BuiltinScope; import io.quarkus.arc.processor.DotNames; import io.quarkus.arc.processor.InjectionPointInfo; +import io.quarkus.arc.processor.InvokerInfo; +import io.quarkus.arc.processor.KotlinDotNames; +import io.quarkus.arc.processor.KotlinUtils; import io.quarkus.arc.processor.Types; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; import io.quarkus.deployment.GeneratedClassGizmoAdaptor; @@ -68,6 +76,7 @@ import io.quarkus.gizmo.CatchBlockCreator; import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.ClassOutput; +import io.quarkus.gizmo.FieldCreator; import io.quarkus.gizmo.FunctionCreator; import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodDescriptor; @@ -108,6 +117,8 @@ import io.quarkus.websockets.next.runtime.WebSocketHttpServerOptionsCustomizer; import io.quarkus.websockets.next.runtime.WebSocketServerRecorder; import io.quarkus.websockets.next.runtime.WebSocketSessionContext; +import io.quarkus.websockets.next.runtime.kotlin.ApplicationCoroutineScope; +import io.quarkus.websockets.next.runtime.kotlin.CoroutineInvoker; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.groups.UniCreate; @@ -190,6 +201,18 @@ void additionalBeans(CombinedIndexBuildItem combinedIndex, BuildProducer additionalBean) { + if (!QuarkusClassLoader.isClassPresentAtRuntime("kotlinx.coroutines.CoroutineScope")) { + return; + } + + additionalBean.produce(AdditionalBeanBuildItem.builder() + .addBeanClass(ApplicationCoroutineScope.class) + .setUnremovable() + .build()); + } + @BuildStep ContextConfiguratorBuildItem registerSessionContext(ContextRegistrationPhaseBuildItem phase) { return new ContextConfiguratorBuildItem(phase.getContext() @@ -211,6 +234,7 @@ void builtinCallbackArguments(BuildProducer providers providers.produce(new CallbackArgumentBuildItem(new HandshakeRequestCallbackArgument())); providers.produce(new CallbackArgumentBuildItem(new ErrorCallbackArgument())); providers.produce(new CallbackArgumentBuildItem(new CloseReasonCallbackArgument())); + providers.produce(new CallbackArgumentBuildItem(new KotlinContinuationCallbackArgument())); } @BuildStep @@ -228,7 +252,7 @@ void collectGlobalErrorHandlers(BeanArchiveIndexBuildItem beanArchiveIndex, ClassInfo beanClass = bean.getTarget().get().asClass(); if (beanClass.declaredAnnotation(WebSocketDotNames.WEB_SOCKET) == null && beanClass.declaredAnnotation(WebSocketDotNames.WEB_SOCKET_CLIENT) == null) { - for (Callback callback : findErrorHandlers(Target.UNDEFINED, index, beanClass, callbackArguments, + for (Callback callback : findErrorHandlers(Target.UNDEFINED, index, bean, beanClass, callbackArguments, transformedAnnotations, null)) { GlobalErrorHandler errorHandler = new GlobalErrorHandler(bean, callback); DotName errorTypeName = callback.argumentType(ErrorCallbackArgument::isError).name(); @@ -327,19 +351,17 @@ public void collectEndpoints(BeanArchiveIndexBuildItem beanArchiveIndex, inboundProcessingMode = webSocketClientAnnotation.value("inboundProcessingMode"); } - Callback onOpen = findCallback(target, beanArchiveIndex.getIndex(), beanClass, WebSocketDotNames.ON_OPEN, - callbackArguments, transformedAnnotations, path); - Callback onTextMessage = findCallback(target, beanArchiveIndex.getIndex(), beanClass, - WebSocketDotNames.ON_TEXT_MESSAGE, - callbackArguments, transformedAnnotations, path); - Callback onBinaryMessage = findCallback(target, beanArchiveIndex.getIndex(), beanClass, + Callback onOpen = findCallback(target, beanArchiveIndex.getIndex(), bean, beanClass, + WebSocketDotNames.ON_OPEN, callbackArguments, transformedAnnotations, path); + Callback onTextMessage = findCallback(target, beanArchiveIndex.getIndex(), bean, beanClass, + WebSocketDotNames.ON_TEXT_MESSAGE, callbackArguments, transformedAnnotations, path); + Callback onBinaryMessage = findCallback(target, beanArchiveIndex.getIndex(), bean, beanClass, WebSocketDotNames.ON_BINARY_MESSAGE, callbackArguments, transformedAnnotations, path); - Callback onPongMessage = findCallback(target, beanArchiveIndex.getIndex(), beanClass, - WebSocketDotNames.ON_PONG_MESSAGE, - callbackArguments, transformedAnnotations, path, + Callback onPongMessage = findCallback(target, beanArchiveIndex.getIndex(), bean, beanClass, + WebSocketDotNames.ON_PONG_MESSAGE, callbackArguments, transformedAnnotations, path, this::validateOnPongMessage); - Callback onClose = findCallback(target, beanArchiveIndex.getIndex(), beanClass, WebSocketDotNames.ON_CLOSE, - callbackArguments, transformedAnnotations, path, + Callback onClose = findCallback(target, beanArchiveIndex.getIndex(), bean, beanClass, + WebSocketDotNames.ON_CLOSE, callbackArguments, transformedAnnotations, path, this::validateOnClose); if (onOpen == null && onTextMessage == null && onBinaryMessage == null && onPongMessage == null) { throw new WebSocketServerException( @@ -354,7 +376,7 @@ public void collectEndpoints(BeanArchiveIndexBuildItem beanArchiveIndex, onBinaryMessage, onPongMessage, onClose, - findErrorHandlers(target, index, beanClass, callbackArguments, transformedAnnotations, path))); + findErrorHandlers(target, index, bean, beanClass, callbackArguments, transformedAnnotations, path))); } } @@ -383,6 +405,7 @@ public void generateEndpoints(BeanArchiveIndexBuildItem index, List generatedClasses, BuildProducer generatedEndpoints, BuildProducer reflectiveClasses) { @@ -409,7 +432,8 @@ public String apply(String name) { // and delegates callback invocations to the endpoint bean String generatedName = generateEndpoint(endpoint, argumentProviders, transformedAnnotations, index.getIndex(), classOutput, globalErrorHandlers, - endpoint.isClient() ? CLIENT_ENDPOINT_SUFFIX : SERVER_ENDPOINT_SUFFIX); + endpoint.isClient() ? CLIENT_ENDPOINT_SUFFIX : SERVER_ENDPOINT_SUFFIX, + invokerFactory); reflectiveClasses.produce(ReflectiveClassBuildItem.builder(generatedName).constructors().build()); generatedEndpoints .produce(new GeneratedEndpointBuildItem(endpoint.id, endpoint.bean.getImplClazz().name().toString(), @@ -626,9 +650,16 @@ static String getPathPrefix(IndexView index, DotName enclosingClassName) { } private void validateOnPongMessage(Callback callback) { - if (callback.returnType().kind() != Kind.VOID && !WebSocketProcessor.isUniVoid(callback.returnType())) { - throw new WebSocketServerException( - "@OnPongMessage callback must return void or Uni: " + callback.asString()); + if (KotlinUtils.isKotlinMethod(callback.method)) { + if (!callback.isReturnTypeVoid() && !callback.isKotlinSuspendFunctionReturningUnit()) { + throw new WebSocketServerException( + "@OnPongMessage callback must return Unit: " + callback.asString()); + } + } else { + if (callback.returnType().kind() != Kind.VOID && !WebSocketProcessor.isUniVoid(callback.returnType())) { + throw new WebSocketServerException( + "@OnPongMessage callback must return void or Uni: " + callback.asString()); + } } Type messageType = callback.argumentType(MessageCallbackArgument::isMessage); if (messageType == null || !messageType.name().equals(WebSocketDotNames.BUFFER)) { @@ -639,9 +670,16 @@ private void validateOnPongMessage(Callback callback) { } private void validateOnClose(Callback callback) { - if (callback.returnType().kind() != Kind.VOID && !WebSocketProcessor.isUniVoid(callback.returnType())) { - throw new WebSocketServerException( - "@OnClose callback must return void or Uni: " + callback.asString()); + if (KotlinUtils.isKotlinMethod(callback.method)) { + if (!callback.isReturnTypeVoid() && !callback.isKotlinSuspendFunctionReturningUnit()) { + throw new WebSocketServerException( + "@OnClose callback must return Unit: " + callback.asString()); + } + } else { + if (callback.returnType().kind() != Kind.VOID && !WebSocketProcessor.isUniVoid(callback.returnType())) { + throw new WebSocketServerException( + "@OnClose callback must return void or Uni: " + callback.asString()); + } } } @@ -710,7 +748,8 @@ static String generateEndpoint(WebSocketEndpointBuildItem endpoint, IndexView index, ClassOutput classOutput, GlobalErrorHandlersBuildItem globalErrorHandlers, - String endpointSuffix) { + String endpointSuffix, + InvokerFactoryBuildItem invokerFactory) { ClassInfo implClazz = endpoint.bean.getImplClazz(); String baseName; if (implClazz.enclosingClass() != null) { @@ -733,7 +772,6 @@ static String generateEndpoint(WebSocketEndpointBuildItem endpoint, Codecs.class, ContextSupport.class, SecuritySupport.class), constructor.getThis(), constructor.getMethodParam(0), constructor.getMethodParam(1), constructor.getMethodParam(2), constructor.getMethodParam(3)); - constructor.returnNull(); MethodCreator inboundProcessingMode = endpointCreator.getMethodCreator("inboundProcessingMode", InboundProcessingMode.class); @@ -751,7 +789,8 @@ static String generateEndpoint(WebSocketEndpointBuildItem endpoint, // Call the business method TryBlock tryBlock = onErrorTryBlock(doOnOpen, doOnOpen.getThis()); ResultHandle[] args = callback.generateArguments(tryBlock.getThis(), tryBlock, transformedAnnotations, index); - ResultHandle ret = tryBlock.invokeVirtualMethod(MethodDescriptor.of(callback.method), beanInstance, args); + ResultHandle ret = callBusinessMethod(endpointCreator, constructor, callback, "Open", tryBlock, + beanInstance, args, invokerFactory); encodeAndReturnResult(tryBlock.getThis(), tryBlock, callback, globalErrorHandlers, endpoint, ret); MethodCreator onOpenExecutionModel = endpointCreator.getMethodCreator("onOpenExecutionModel", @@ -759,12 +798,12 @@ static String generateEndpoint(WebSocketEndpointBuildItem endpoint, onOpenExecutionModel.returnValue(onOpenExecutionModel.load(callback.executionModel)); } - generateOnMessage(endpointCreator, endpoint, endpoint.onBinaryMessage, argumentProviders, transformedAnnotations, - index, globalErrorHandlers); - generateOnMessage(endpointCreator, endpoint, endpoint.onTextMessage, argumentProviders, transformedAnnotations, index, - globalErrorHandlers); - generateOnMessage(endpointCreator, endpoint, endpoint.onPongMessage, argumentProviders, transformedAnnotations, index, - globalErrorHandlers); + generateOnMessage(endpointCreator, constructor, endpoint, endpoint.onBinaryMessage, argumentProviders, + transformedAnnotations, index, globalErrorHandlers, invokerFactory); + generateOnMessage(endpointCreator, constructor, endpoint, endpoint.onTextMessage, argumentProviders, + transformedAnnotations, index, globalErrorHandlers, invokerFactory); + generateOnMessage(endpointCreator, constructor, endpoint, endpoint.onPongMessage, argumentProviders, + transformedAnnotations, index, globalErrorHandlers, invokerFactory); if (endpoint.onClose != null) { Callback callback = endpoint.onClose; @@ -775,7 +814,8 @@ static String generateEndpoint(WebSocketEndpointBuildItem endpoint, // Call the business method TryBlock tryBlock = onErrorTryBlock(doOnClose, doOnClose.getThis()); ResultHandle[] args = callback.generateArguments(tryBlock.getThis(), tryBlock, transformedAnnotations, index); - ResultHandle ret = tryBlock.invokeVirtualMethod(MethodDescriptor.of(callback.method), beanInstance, args); + ResultHandle ret = callBusinessMethod(endpointCreator, constructor, callback, "Close", tryBlock, + beanInstance, args, invokerFactory); encodeAndReturnResult(tryBlock.getThis(), tryBlock, callback, globalErrorHandlers, endpoint, ret); MethodCreator onCloseExecutionModel = endpointCreator.getMethodCreator("onCloseExecutionModel", @@ -783,15 +823,19 @@ static String generateEndpoint(WebSocketEndpointBuildItem endpoint, onCloseExecutionModel.returnValue(onCloseExecutionModel.load(callback.executionModel)); } - generateOnError(endpointCreator, endpoint, argumentProviders, transformedAnnotations, globalErrorHandlers, index); + generateOnError(endpointCreator, constructor, endpoint, transformedAnnotations, globalErrorHandlers, index, + invokerFactory); + + // we write into the constructor when generating callback invokers, so need to finish it late + constructor.returnVoid(); endpointCreator.close(); return generatedName.replace('/', '.'); } - private static void generateOnError(ClassCreator endpointCreator, WebSocketEndpointBuildItem endpoint, - CallbackArgumentsBuildItem callbackArguments, TransformedAnnotationsBuildItem transformedAnnotations, - GlobalErrorHandlersBuildItem globalErrorHandlers, IndexView index) { + private static void generateOnError(ClassCreator endpointCreator, MethodCreator constructor, + WebSocketEndpointBuildItem endpoint, TransformedAnnotationsBuildItem transformedAnnotations, + GlobalErrorHandlersBuildItem globalErrorHandlers, IndexView index, InvokerFactoryBuildItem invokerFactory) { Map errors = new HashMap<>(); List throwableInfos = new ArrayList<>(); @@ -841,7 +885,8 @@ private static void generateOnError(ClassCreator endpointCreator, WebSocketEndpo MethodDescriptor.ofMethod(WebSocketEndpointBase.class, "beanInstance", Object.class, String.class), endpointThis, funBytecode.load(throwableInfo.bean().getIdentifier())); ResultHandle[] args = callback.generateArguments(endpointThis, tryBlock, transformedAnnotations, index); - ResultHandle ret = tryBlock.invokeVirtualMethod(MethodDescriptor.of(callback.method), beanInstance, args); + ResultHandle ret = callBusinessMethod(endpointCreator, constructor, callback, "Error", tryBlock, + beanInstance, args, invokerFactory); encodeAndReturnResult(endpointThis, tryBlock, callback, globalErrorHandlers, endpoint, ret); // return doErrorExecute() @@ -891,9 +936,10 @@ record GlobalErrorHandler(BeanInfo bean, Callback callback) { } - private static void generateOnMessage(ClassCreator endpointCreator, WebSocketEndpointBuildItem endpoint, Callback callback, + private static void generateOnMessage(ClassCreator endpointCreator, MethodCreator constructor, + WebSocketEndpointBuildItem endpoint, Callback callback, CallbackArgumentsBuildItem callbackArguments, TransformedAnnotationsBuildItem transformedAnnotations, - IndexView index, GlobalErrorHandlersBuildItem globalErrorHandlers) { + IndexView index, GlobalErrorHandlersBuildItem globalErrorHandlers, InvokerFactoryBuildItem invokerFactory) { if (callback == null) { return; } @@ -924,8 +970,8 @@ private static void generateOnMessage(ClassCreator endpointCreator, WebSocketEnd MethodDescriptor.ofMethod(WebSocketEndpointBase.class, "beanInstance", Object.class), tryBlock.getThis()); ResultHandle[] args = callback.generateArguments(tryBlock.getThis(), tryBlock, transformedAnnotations, index); // Call the business method - ResultHandle ret = tryBlock.invokeVirtualMethod(MethodDescriptor.of(callback.method), beanInstance, - args); + ResultHandle ret = callBusinessMethod(endpointCreator, constructor, callback, messageType, tryBlock, beanInstance, args, + invokerFactory); encodeAndReturnResult(tryBlock.getThis(), tryBlock, callback, globalErrorHandlers, endpoint, ret); MethodCreator onMessageExecutionModel = endpointCreator.getMethodCreator("on" + messageType + "MessageExecutionModel", @@ -946,6 +992,34 @@ private static void generateOnMessage(ClassCreator endpointCreator, WebSocketEnd } } + private static ResultHandle callBusinessMethod(ClassCreator clazz, MethodCreator constructor, Callback callback, + String messageType, BytecodeCreator bytecode, ResultHandle beanInstance, ResultHandle[] args, + InvokerFactoryBuildItem invokerFactory) { + // using CDI method invokers for Kotlin `suspend` functions to minimize custom bytecode generation + // other methods are invoked directly + if (KotlinUtils.isKotlinSuspendMethod(callback.method)) { + InvokerInfo invoker = invokerFactory.createInvoker(callback.bean, callback.method) + .withInvocationWrapper(CoroutineInvoker.class, "inNewCoroutine") + .build(); + // create a field in the endpoint class and put the created invoker into it in the `constructor` + FieldCreator invokerField = clazz.getFieldCreator("invokerFor" + messageType, Invoker.class) + .setModifiers(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL); + constructor.writeInstanceField(invokerField.getFieldDescriptor(), constructor.getThis(), + constructor.newInstance(MethodDescriptor.ofConstructor(invoker.getClassName()))); + // use the invoker to invoke the business method in the endpoint method (`bytecode`) + ResultHandle invokerHandle = bytecode.readInstanceField(invokerField.getFieldDescriptor(), bytecode.getThis()); + ResultHandle argsArray = bytecode.newArray(Object.class, args.length); + for (int i = 0; i < args.length; i++) { + bytecode.writeArrayValue(argsArray, i, args[i]); + } + return bytecode.invokeInterfaceMethod( + MethodDescriptor.ofMethod(Invoker.class, "invoke", Object.class, Object.class, Object[].class), + invokerHandle, beanInstance, argsArray); + } else { + return bytecode.invokeVirtualMethod(MethodDescriptor.of(callback.method), beanInstance, args); + } + } + private static TryBlock uniFailureTryBlock(BytecodeCreator method) { TryBlock tryBlock = method.tryBlock(); CatchBlockCreator catchBlock = tryBlock.addCatch(Throwable.class); @@ -1070,8 +1144,15 @@ private static ResultHandle encodeMessage(ResultHandle endpointThis, BytecodeCre // ---------------------- // === Binary message === // ---------------------- - if (callback.isReturnTypeUni()) { - Type messageType = callback.returnType().asParameterizedType().arguments().get(0); + if (callback.isReturnTypeUni() || callback.isKotlinSuspendFunction()) { + Type messageType = callback.isReturnTypeUni() + ? callback.returnType().asParameterizedType().arguments().get(0) + : KotlinUtils.getKotlinSuspendMethodResult(callback.method); + if (messageType.name().equals(KotlinDotNames.UNIT)) { + value = method.invokeInterfaceMethod(MethodDescriptor.ofMethod(Uni.class, "replaceWithVoid", Uni.class), + value); + messageType = ClassType.create(WebSocketDotNames.VOID); + } if (messageType.name().equals(WebSocketDotNames.VOID)) { // Uni return uniOnFailureDoOnError(endpointThis, method, callback, value, endpoint, globalErrorHandlers); @@ -1082,8 +1163,7 @@ private static ResultHandle encodeMessage(ResultHandle endpointThis, BytecodeCre // }); FunctionCreator fun = method.createFunction(Function.class); BytecodeCreator funBytecode = fun.getBytecode(); - ResultHandle buffer = encodeBuffer(funBytecode, - callback.returnType().asParameterizedType().arguments().get(0), + ResultHandle buffer = encodeBuffer(funBytecode, messageType, funBytecode.getMethodParam(0), endpointThis, callback); funBytecode.returnValue(funBytecode.invokeVirtualMethod( MethodDescriptor.ofMethod(WebSocketEndpointBase.class, @@ -1130,8 +1210,15 @@ private static ResultHandle encodeMessage(ResultHandle endpointThis, BytecodeCre // ---------------------- // === Text message === // ---------------------- - if (callback.isReturnTypeUni()) { - Type messageType = callback.returnType().asParameterizedType().arguments().get(0); + if (callback.isReturnTypeUni() || callback.isKotlinSuspendFunction()) { + Type messageType = callback.isReturnTypeUni() + ? callback.returnType().asParameterizedType().arguments().get(0) + : KotlinUtils.getKotlinSuspendMethodResult(callback.method); + if (messageType.name().equals(KotlinDotNames.UNIT)) { + value = method.invokeInterfaceMethod(MethodDescriptor.ofMethod(Uni.class, "replaceWithVoid", Uni.class), + value); + messageType = ClassType.create(WebSocketDotNames.VOID); + } if (messageType.name().equals(WebSocketDotNames.VOID)) { // Uni return uniOnFailureDoOnError(endpointThis, method, callback, value, endpoint, globalErrorHandlers); @@ -1142,7 +1229,7 @@ private static ResultHandle encodeMessage(ResultHandle endpointThis, BytecodeCre // }); FunctionCreator fun = method.createFunction(Function.class); BytecodeCreator funBytecode = fun.getBytecode(); - ResultHandle text = encodeText(funBytecode, callback.returnType().asParameterizedType().arguments().get(0), + ResultHandle text = encodeText(funBytecode, messageType, funBytecode.getMethodParam(0), endpointThis, callback); funBytecode.returnValue(funBytecode.invokeVirtualMethod( MethodDescriptor.ofMethod(WebSocketEndpointBase.class, @@ -1267,9 +1354,8 @@ private static void encodeAndReturnResult(ResultHandle endpointThis, BytecodeCre } } - static List findErrorHandlers(Target target, IndexView index, ClassInfo beanClass, - CallbackArgumentsBuildItem callbackArguments, - TransformedAnnotationsBuildItem transformedAnnotations, + static List findErrorHandlers(Target target, IndexView index, BeanInfo bean, ClassInfo beanClass, + CallbackArgumentsBuildItem callbackArguments, TransformedAnnotationsBuildItem transformedAnnotations, String endpointPath) { List annotations = findCallbackAnnotations(index, beanClass, WebSocketDotNames.ON_ERROR); if (annotations.isEmpty()) { @@ -1285,8 +1371,9 @@ static List findErrorHandlers(Target target, IndexView index, ClassInf .anyMatch(WebSocketDotNames.WEB_SOCKET_CLIENT_CONNECTION::equals)) { target = Target.CLIENT; } - Callback callback = new Callback(target, annotation, method, executionModel(method, transformedAnnotations), - callbackArguments, transformedAnnotations, endpointPath, index); + Callback callback = new Callback(target, annotation, bean, method, + executionModel(method, transformedAnnotations), callbackArguments, transformedAnnotations, + endpointPath, index); long errorArguments = callback.arguments.stream().filter(ca -> ca instanceof ErrorCallbackArgument).count(); if (errorArguments != 1) { throw new WebSocketException( @@ -1316,16 +1403,16 @@ private static List findCallbackAnnotations(IndexView index, return annotations; } - static Callback findCallback(Target target, IndexView index, ClassInfo beanClass, DotName annotationName, - CallbackArgumentsBuildItem callbackArguments, TransformedAnnotationsBuildItem transformedAnnotations, - String endpointPath) { - return findCallback(target, index, beanClass, annotationName, callbackArguments, transformedAnnotations, endpointPath, - null); + static Callback findCallback(Target target, IndexView index, BeanInfo bean, ClassInfo beanClass, + DotName annotationName, CallbackArgumentsBuildItem callbackArguments, + TransformedAnnotationsBuildItem transformedAnnotations, String endpointPath) { + return findCallback(target, index, bean, beanClass, annotationName, callbackArguments, + transformedAnnotations, endpointPath, null); } - private static Callback findCallback(Target target, IndexView index, ClassInfo beanClass, DotName annotationName, - CallbackArgumentsBuildItem callbackArguments, TransformedAnnotationsBuildItem transformedAnnotations, - String endpointPath, + private static Callback findCallback(Target target, IndexView index, BeanInfo bean, ClassInfo beanClass, + DotName annotationName, CallbackArgumentsBuildItem callbackArguments, + TransformedAnnotationsBuildItem transformedAnnotations, String endpointPath, Consumer validator) { List annotations = findCallbackAnnotations(index, beanClass, annotationName); if (annotations.isEmpty()) { @@ -1333,9 +1420,9 @@ private static Callback findCallback(Target target, IndexView index, ClassInfo b } else if (annotations.size() == 1) { AnnotationInstance annotation = annotations.get(0); MethodInfo method = annotation.target().asMethod(); - Callback callback = new Callback(target, annotation, method, executionModel(method, transformedAnnotations), - callbackArguments, - transformedAnnotations, endpointPath, index); + Callback callback = new Callback(target, annotation, bean, method, + executionModel(method, transformedAnnotations), callbackArguments, transformedAnnotations, + endpointPath, index); long messageArguments = callback.arguments.stream().filter(ca -> ca instanceof MessageCallbackArgument).count(); if (callback.acceptsMessage()) { if (messageArguments > 1) { @@ -1370,6 +1457,14 @@ private static Callback findCallback(Target target, IndexView index, ClassInfo b } private static ExecutionModel executionModel(MethodInfo method, TransformedAnnotationsBuildItem transformedAnnotations) { + if (KotlinUtils.isKotlinSuspendMethod(method) + && (transformedAnnotations.hasAnnotation(method, WebSocketDotNames.RUN_ON_VIRTUAL_THREAD) + || transformedAnnotations.hasAnnotation(method, WebSocketDotNames.BLOCKING) + || transformedAnnotations.hasAnnotation(method, WebSocketDotNames.NON_BLOCKING))) { + throw new WebSocketException("Kotlin `suspend` functions in WebSockets Next endpoints may not be " + + "annotated @Blocking, @NonBlocking or @RunOnVirtualThread: " + method); + } + if (transformedAnnotations.hasAnnotation(method, WebSocketDotNames.RUN_ON_VIRTUAL_THREAD)) { return ExecutionModel.VIRTUAL_THREAD; } else if (transformedAnnotations.hasAnnotation(method, WebSocketDotNames.BLOCKING)) { @@ -1382,6 +1477,10 @@ private static ExecutionModel executionModel(MethodInfo method, TransformedAnnot } static boolean hasBlockingSignature(MethodInfo method) { + if (KotlinUtils.isKotlinSuspendMethod(method)) { + return false; + } + switch (method.returnType().kind()) { case VOID: case CLASS: @@ -1414,6 +1513,8 @@ private static boolean isOnOpenWithBinaryReturnType(Callback callback) { Type returnType = callback.returnType(); if (callback.isReturnTypeUni() || callback.isReturnTypeMulti()) { returnType = callback.returnType().asParameterizedType().arguments().get(0); + } else if (callback.isKotlinSuspendFunction()) { + returnType = KotlinUtils.getKotlinSuspendMethodResult(callback.method); } return WebSocketDotNames.BUFFER.equals(returnType.name()) || (returnType.kind() == Kind.ARRAY && PrimitiveType.BYTE.equals(returnType.asArrayType().constituent())); diff --git a/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/utils/WSClient.java b/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/utils/WSClient.java index 2f1974089db90..926c0d1b82d15 100644 --- a/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/utils/WSClient.java +++ b/extensions/websockets-next/deployment/src/test/java/io/quarkus/websockets/next/test/utils/WSClient.java @@ -144,6 +144,13 @@ public Buffer sendAndAwaitReply(String message) { return messages.get(c); } + public Buffer sendAndAwaitReply(Buffer message) { + var c = messages.size(); + sendAndAwait(message); + Awaitility.await().until(() -> messages.size() > c); + return messages.get(c); + } + public boolean isClosed() { return socket.get().isClosed(); } diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/BinaryEcho.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/BinaryEcho.kt new file mode 100644 index 0000000000000..a42359791fd38 --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/BinaryEcho.kt @@ -0,0 +1,14 @@ +package io.quarkus.websockets.next.test.kotlin + +import io.quarkus.websockets.next.OnBinaryMessage +import io.quarkus.websockets.next.WebSocket +import io.vertx.core.buffer.Buffer +import kotlinx.coroutines.delay + +@WebSocket(path = "/binary-echo") +class BinaryEcho { + @OnBinaryMessage + fun process(msg: Buffer): Buffer { + return msg + } +} diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/BinaryEchoSuspend.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/BinaryEchoSuspend.kt new file mode 100644 index 0000000000000..a2182d7607535 --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/BinaryEchoSuspend.kt @@ -0,0 +1,15 @@ +package io.quarkus.websockets.next.test.kotlin + +import io.quarkus.websockets.next.OnBinaryMessage +import io.quarkus.websockets.next.WebSocket +import io.vertx.core.buffer.Buffer +import kotlinx.coroutines.delay + +@WebSocket(path = "/binary-echo-suspend") +class BinaryEchoSuspend { + @OnBinaryMessage + suspend fun process(msg: Buffer): Buffer { + delay(100) + return msg + } +} diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/Echo.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/Echo.kt new file mode 100644 index 0000000000000..fc60701b4991b --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/Echo.kt @@ -0,0 +1,12 @@ +package io.quarkus.websockets.next.test.kotlin + +import io.quarkus.websockets.next.OnTextMessage +import io.quarkus.websockets.next.WebSocket + +@WebSocket(path = "/echo") +class Echo { + @OnTextMessage + fun process(msg: Message): Message { + return msg + } +} diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/EchoSuspend.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/EchoSuspend.kt new file mode 100644 index 0000000000000..92d27b2d9d85b --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/EchoSuspend.kt @@ -0,0 +1,14 @@ +package io.quarkus.websockets.next.test.kotlin + +import io.quarkus.websockets.next.OnTextMessage +import io.quarkus.websockets.next.WebSocket +import kotlinx.coroutines.delay + +@WebSocket(path = "/echo-suspend") +class EchoSuspend { + @OnTextMessage + suspend fun process(msg: Message): Message { + delay(100) + return msg + } +} diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketClientTest.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketClientTest.kt new file mode 100644 index 0000000000000..9ec2cd00baf2c --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketClientTest.kt @@ -0,0 +1,93 @@ +package io.quarkus.websockets.next.test.kotlin + +import io.quarkus.test.QuarkusUnitTest +import io.quarkus.test.common.http.TestHTTPResource +import io.quarkus.websockets.next.OnClose +import io.quarkus.websockets.next.OnOpen +import io.quarkus.websockets.next.OnTextMessage +import io.quarkus.websockets.next.WebSocket +import io.quarkus.websockets.next.WebSocketClient +import io.quarkus.websockets.next.WebSocketConnector +import jakarta.inject.Inject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +import java.net.URI +import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +class KotlinWebSocketClientTest { + companion object { + @RegisterExtension + val test = QuarkusUnitTest() + .withApplicationRoot { jar -> + jar.addClasses(ServerEndpoint::class.java, ClientEndpoint::class.java) + } + } + + @Inject + lateinit var connector: WebSocketConnector + + @TestHTTPResource("/") + lateinit var uri: URI + + @Test + fun test() { + val connection = connector.baseUri(uri).connectAndAwait() + connection.sendTextAndAwait("Hi!") + + assertTrue(ClientEndpoint.messagesLatch.await(5, TimeUnit.SECONDS)) + assertEquals("Hello there", ClientEndpoint.messages[0]) + assertEquals("Hi!", ClientEndpoint.messages[1]) + + connection.closeAndAwait() + assertTrue(ClientEndpoint.closedLatch.await(5, TimeUnit.SECONDS)) + assertTrue(ServerEndpoint.closedLatch.await(5, TimeUnit.SECONDS)) + } + + @WebSocket(path = "/endpoint") + class ServerEndpoint { + companion object { + val closedLatch: CountDownLatch = CountDownLatch(1) + } + + @OnOpen + fun open(): String { + return "Hello there" + } + + @OnTextMessage + fun echo(message: String): String { + return message + } + + @OnClose + fun close() { + closedLatch.countDown() + } + } + + @WebSocketClient(path = "/endpoint") + class ClientEndpoint { + companion object { + val messages: MutableList = CopyOnWriteArrayList() + + val messagesLatch: CountDownLatch = CountDownLatch(2) + + val closedLatch: CountDownLatch = CountDownLatch(1) + } + + @OnTextMessage + fun onMessage(message: String) { + messages.add(message) + messagesLatch.countDown() + } + + @OnClose + fun onClose() { + closedLatch.countDown() + } + } +} diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketSessionContextTest.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketSessionContextTest.kt new file mode 100644 index 0000000000000..4fd8f29d1b03a --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketSessionContextTest.kt @@ -0,0 +1,85 @@ +package io.quarkus.websockets.next.test.kotlin + +import io.quarkus.test.QuarkusUnitTest +import io.quarkus.test.common.http.TestHTTPResource +import io.quarkus.websockets.next.OnTextMessage +import io.quarkus.websockets.next.WebSocket +import io.quarkus.websockets.next.test.utils.WSClient +import io.vertx.core.Vertx +import jakarta.enterprise.context.SessionScoped +import jakarta.inject.Inject +import kotlinx.coroutines.delay +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +import java.net.URI +import java.util.UUID + +class KotlinWebSocketSessionContextTest { + companion object { + @RegisterExtension + val test = QuarkusUnitTest() + .withApplicationRoot { jar -> + jar.addClasses(MyData::class.java, Endpoint::class.java, WSClient::class.java) + } + } + + @Inject + lateinit var vertx: Vertx + + @TestHTTPResource("endpoint") + lateinit var endpoint: URI + + @Test + fun testEcho() { + WSClient.create(vertx).connect(endpoint).use { client1 -> + WSClient.create(vertx).connect(endpoint).use { client2 -> + var id1: String? = null + var id2: String? = null + for (i in 1..10) { + val client = if (i % 2 == 0) client1 else client2 + val req = "hello$i" + val resp = client.sendAndAwaitReply(req).toString().split(" ") + assertEquals(3, resp.size) + assertEquals(req, resp[0]) + assertEquals(resp[1], resp[2]) + if (i % 2 == 0) { + if (id1 == null) { + id1 = resp[1] + } + assertEquals(id1, resp[1]) + } else { + if (id2 == null) { + id2 = resp[1] + } + assertEquals(id2, resp[1]) + } + } + assertNotNull(id1) + assertNotNull(id2) + assertNotEquals(id1, id2) + } + } + } + + @SessionScoped + class MyData { + val id = UUID.randomUUID().toString() + } + + @WebSocket(path = "/endpoint") + class Endpoint { + @Inject + lateinit var data: MyData + + @OnTextMessage + suspend fun echo(message: String): String { + val id1 = data.id + delay(100) + val id2 = data.id + return "$message $id1 $id2" + } + } +} diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketSuspendingClientTest.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketSuspendingClientTest.kt new file mode 100644 index 0000000000000..9c4a8fdc9051a --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketSuspendingClientTest.kt @@ -0,0 +1,99 @@ +package io.quarkus.websockets.next.test.kotlin + +import io.quarkus.test.QuarkusUnitTest +import io.quarkus.test.common.http.TestHTTPResource +import io.quarkus.websockets.next.OnClose +import io.quarkus.websockets.next.OnOpen +import io.quarkus.websockets.next.OnTextMessage +import io.quarkus.websockets.next.WebSocket +import io.quarkus.websockets.next.WebSocketClient +import io.quarkus.websockets.next.WebSocketConnector +import jakarta.inject.Inject +import kotlinx.coroutines.delay +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +import java.net.URI +import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +class KotlinWebSocketSuspendingClientTest { + companion object { + @RegisterExtension + val test = QuarkusUnitTest() + .withApplicationRoot { jar -> + jar.addClasses(ServerEndpoint::class.java, ClientEndpoint::class.java) + } + } + + @Inject + lateinit var connector: WebSocketConnector + + @TestHTTPResource("/") + lateinit var uri: URI + + @Test + fun test() { + val connection = connector.baseUri(uri).connectAndAwait() + connection.sendTextAndAwait("Hi!") + + assertTrue(ClientEndpoint.messagesLatch.await(5, TimeUnit.SECONDS)) + assertEquals("Hello there", ClientEndpoint.messages[0]) + assertEquals("Hi!", ClientEndpoint.messages[1]) + + connection.closeAndAwait() + assertTrue(ClientEndpoint.closedLatch.await(5, TimeUnit.SECONDS)) + assertTrue(ServerEndpoint.closedLatch.await(5, TimeUnit.SECONDS)) + } + + @WebSocket(path = "/endpoint") + class ServerEndpoint { + companion object { + val closedLatch: CountDownLatch = CountDownLatch(1) + } + + @OnOpen + suspend fun open(): String { + delay(100) + return "Hello there" + } + + @OnTextMessage + suspend fun echo(message: String): String { + delay(100) + return message + } + + @OnClose + suspend fun close() { + delay(100) + closedLatch.countDown() + } + } + + @WebSocketClient(path = "/endpoint") + class ClientEndpoint { + companion object { + val messages: MutableList = CopyOnWriteArrayList() + + val messagesLatch: CountDownLatch = CountDownLatch(2) + + val closedLatch: CountDownLatch = CountDownLatch(1) + } + + @OnTextMessage + suspend fun onMessage(message: String) { + delay(100) + messages.add(message) + messagesLatch.countDown() + } + + @OnClose + suspend fun onClose() { + delay(100) + closedLatch.countDown() + } + } +} diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketTest.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketTest.kt new file mode 100644 index 0000000000000..12dd973ee9848 --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/KotlinWebSocketTest.kt @@ -0,0 +1,75 @@ +package io.quarkus.websockets.next.test.kotlin + +import io.quarkus.test.QuarkusUnitTest +import io.quarkus.test.common.http.TestHTTPResource +import io.quarkus.websockets.next.test.utils.WSClient +import io.vertx.core.Vertx +import io.vertx.core.buffer.Buffer +import io.vertx.core.json.JsonObject +import jakarta.inject.Inject +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +import java.net.URI + +class KotlinWebSocketTest { + companion object { + @RegisterExtension + val test = QuarkusUnitTest() + .withApplicationRoot { jar -> + jar.addClasses(Echo::class.java, EchoSuspend::class.java, BinaryEcho::class.java, + BinaryEchoSuspend::class.java, Message::class.java, WSClient::class.java) + } + } + + @Inject + lateinit var vertx: Vertx + + @TestHTTPResource("echo") + lateinit var echo: URI + + @TestHTTPResource("echo-suspend") + lateinit var echoSuspend: URI + + @TestHTTPResource("binary-echo") + lateinit var binaryEcho: URI + + @TestHTTPResource("binary-echo-suspend") + lateinit var binaryEchoSuspend: URI + + @Test + fun testEcho() { + doTest(echo) + } + + @Test + fun testEchoSuspend() { + doTest(echoSuspend) + } + + private fun doTest(uri: URI) { + WSClient.create(vertx).connect(uri).use { client -> + val req = JsonObject().put("msg", "hello") + val resp = client.sendAndAwaitReply(req.toString()).toJsonObject() + assertEquals(req, resp) + } + } + + @Test + fun testBinaryEcho() { + doTestBinary(binaryEcho) + } + + @Test + fun testBinaryEchoSuspend() { + doTestBinary(binaryEchoSuspend) + } + + private fun doTestBinary(uri: URI) { + WSClient.create(vertx).connect(uri).use { client -> + val req = Buffer.buffer("hello there!") + val resp = client.sendAndAwaitReply(req) + assertEquals(req, resp) + } + } +} diff --git a/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/Message.kt b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/Message.kt new file mode 100644 index 0000000000000..96563b27e7382 --- /dev/null +++ b/extensions/websockets-next/deployment/src/test/kotlin/io/quarkus/websockets/next/test/kotlin/Message.kt @@ -0,0 +1,5 @@ +package io.quarkus.websockets.next.test.kotlin + +import com.fasterxml.jackson.annotation.JsonCreator + +data class Message @JsonCreator constructor(var msg: String) diff --git a/extensions/websockets-next/kotlin/pom.xml b/extensions/websockets-next/kotlin/pom.xml new file mode 100644 index 0000000000000..bda3816be3a85 --- /dev/null +++ b/extensions/websockets-next/kotlin/pom.xml @@ -0,0 +1,115 @@ + + + + io.quarkus + quarkus-websockets-next-parent + 999-SNAPSHOT + + 4.0.0 + + quarkus-websockets-next-kotlin + Quarkus - WebSockets Next - Kotlin + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-vertx + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + true + + + org.jetbrains.kotlinx + kotlinx-coroutines-jdk8 + true + + + io.smallrye.reactive + mutiny-kotlin + true + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + + compile + + + + test-compile + + test-compile + + + + + ${maven.compiler.target} + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + default-compile + none + + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + false + + + + + diff --git a/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/ApplicationCoroutineScope.kt b/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/ApplicationCoroutineScope.kt new file mode 100644 index 0000000000000..78ee39b924706 --- /dev/null +++ b/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/ApplicationCoroutineScope.kt @@ -0,0 +1,18 @@ +package io.quarkus.websockets.next.runtime.kotlin + +import jakarta.annotation.PreDestroy +import jakarta.inject.Singleton +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel + +@Singleton +class ApplicationCoroutineScope : CoroutineScope, AutoCloseable { + override val coroutineContext: CoroutineContext = SupervisorJob() + + @PreDestroy + override fun close() { + coroutineContext.cancel() + } +} diff --git a/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/CoroutineInvoker.kt b/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/CoroutineInvoker.kt new file mode 100644 index 0000000000000..5a6b6f8e28a77 --- /dev/null +++ b/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/CoroutineInvoker.kt @@ -0,0 +1,37 @@ +package io.quarkus.websockets.next.runtime.kotlin + +import io.quarkus.arc.Arc +import io.smallrye.mutiny.Uni +import io.smallrye.mutiny.coroutines.asUni +import io.vertx.core.Vertx +import jakarta.enterprise.invoke.Invoker +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async + +object CoroutineInvoker { + @JvmStatic + @OptIn(ExperimentalCoroutinesApi::class) + fun inNewCoroutine(instance: T, arguments: Array, invoker: Invoker): Uni { + val coroutineScope = Arc.container().instance(ApplicationCoroutineScope::class.java).get() + val dispatcher: CoroutineDispatcher = + Vertx.currentContext()?.let(::VertxDispatcher) + ?: throw IllegalStateException("No Vertx context found") + + return coroutineScope + .async(context = dispatcher) { + suspendCoroutine { continuation -> + arguments[arguments.size - 1] = continuation + try { + continuation.resume(invoker.invoke(instance, arguments)) + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } + .asUni() + } +} diff --git a/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/VertxDispatcher.kt b/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/VertxDispatcher.kt new file mode 100644 index 0000000000000..1377b1835fe6c --- /dev/null +++ b/extensions/websockets-next/kotlin/src/main/kotlin/io/quarkus/websockets/next/runtime/kotlin/VertxDispatcher.kt @@ -0,0 +1,24 @@ +package io.quarkus.websockets.next.runtime.kotlin + +import io.quarkus.arc.Arc +import io.vertx.core.Context +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineDispatcher + +class VertxDispatcher(private val vertxContext: Context) : CoroutineDispatcher() { + override fun dispatch(context: CoroutineContext, block: Runnable) { + val requestContext = Arc.container().requestContext() + vertxContext.runOnContext { + if (requestContext.isActive) { + block.run() + } else { + try { + requestContext.activate() + block.run() + } finally { + requestContext.terminate() + } + } + } + } +} diff --git a/extensions/websockets-next/pom.xml b/extensions/websockets-next/pom.xml index 5ff9318b3e765..1e149d244734f 100644 --- a/extensions/websockets-next/pom.xml +++ b/extensions/websockets-next/pom.xml @@ -16,6 +16,7 @@ deployment runtime + kotlin diff --git a/extensions/websockets-next/runtime/pom.xml b/extensions/websockets-next/runtime/pom.xml index a72d7832edd42..de16a0cfaacaa 100644 --- a/extensions/websockets-next/runtime/pom.xml +++ b/extensions/websockets-next/runtime/pom.xml @@ -34,6 +34,10 @@ io.quarkus quarkus-tls-registry + + io.quarkus + quarkus-websockets-next-kotlin + io.quarkus.security diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/KotlinDotNames.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/KotlinDotNames.java index cef74e824cf2d..37ab47604ba2d 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/KotlinDotNames.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/KotlinDotNames.java @@ -2,8 +2,9 @@ import org.jboss.jandex.DotName; -class KotlinDotNames { - static final DotName METADATA = DotName.createSimple("kotlin.Metadata"); +public final class KotlinDotNames { + public static final DotName METADATA = DotName.createSimple("kotlin.Metadata"); + public static final DotName UNIT = DotName.createSimple("kotlin.Unit"); - static final DotName CONTINUATION = DotName.createSimple("kotlin.coroutines.Continuation"); + public static final DotName CONTINUATION = DotName.createSimple("kotlin.coroutines.Continuation"); } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/KotlinUtils.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/KotlinUtils.java index 1628a83b3adef..966a7fc2c57ab 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/KotlinUtils.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/KotlinUtils.java @@ -4,6 +4,7 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.MethodParameterInfo; import org.jboss.jandex.Type; public class KotlinUtils { @@ -27,6 +28,10 @@ public static boolean isKotlinSuspendMethod(MethodInfo method) { return KotlinDotNames.CONTINUATION.equals(lastParameter.name()); } + public static boolean isKotlinContinuationParameter(MethodParameterInfo parameter) { + return isKotlinSuspendMethod(parameter.method()) && KotlinDotNames.CONTINUATION.equals(parameter.type().name()); + } + public static boolean isNoninterceptableKotlinMethod(MethodInfo method) { // the Kotlin compiler generates somewhat streamlined bytecode when it determines // that a `suspend` method cannot be overridden, and that bytecode doesn't work @@ -36,4 +41,24 @@ public static boolean isNoninterceptableKotlinMethod(MethodInfo method) { return isKotlinSuspendMethod(method) && (Modifier.isFinal(method.flags()) || Modifier.isFinal(method.declaringClass().flags())); } + + public static Type getKotlinSuspendMethodResult(MethodInfo method) { + if (!isKotlinSuspendMethod(method)) { + throw new IllegalArgumentException("Not a suspend function: " + method); + } + + Type lastParameter = method.parameterType(method.parametersCount() - 1); + if (lastParameter.kind() != Type.Kind.PARAMETERIZED_TYPE) { + throw new IllegalArgumentException("Continuation parameter type not parameterized: " + lastParameter); + } + Type resultType = lastParameter.asParameterizedType().arguments().get(0); + if (resultType.kind() != Type.Kind.WILDCARD_TYPE) { + throw new IllegalArgumentException("Continuation parameter type argument not wildcard: " + resultType); + } + Type lowerBound = resultType.asWildcardType().superBound(); + if (lowerBound == null) { + throw new IllegalArgumentException("Continuation parameter type argument without lower bound: " + resultType); + } + return lowerBound; + } } From e365794d4c65def694e4c5a3573f92c96f9aebf8 Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Wed, 10 Jul 2024 13:55:41 +0300 Subject: [PATCH 78/94] feat: introduce build item to control kubernetes generated output directory --- .../CustomKubernetesOutputDirBuildItem.java | 23 ++++++ .../deployment/KubernetesProcessor.java | 11 ++- .../KubernetesWithCustomOutputDirTest.java | 72 +++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/CustomKubernetesOutputDirBuildItem.java create mode 100644 integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithCustomOutputDirTest.java diff --git a/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/CustomKubernetesOutputDirBuildItem.java b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/CustomKubernetesOutputDirBuildItem.java new file mode 100644 index 0000000000000..fcdff93b7800a --- /dev/null +++ b/extensions/kubernetes/spi/src/main/java/io/quarkus/kubernetes/spi/CustomKubernetesOutputDirBuildItem.java @@ -0,0 +1,23 @@ +package io.quarkus.kubernetes.spi; + +import java.nio.file.Path; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * Build item that allows us to supply a custom output dir instead of defaulting to {project.target.dir}/kubernetes + * It's different from the {@link KubernetesOutputDirBuildItem} as it's used to communicate the intention to override the dir + * while {@link KubernetesOutputDirBuildItem} is used to communicate the effective output dir. + */ +public final class CustomKubernetesOutputDirBuildItem extends SimpleBuildItem { + + private final Path outputDir; + + public CustomKubernetesOutputDirBuildItem(Path outputDir) { + this.outputDir = outputDir; + } + + public Path getOutputDir() { + return outputDir; + } +} diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java index dbf5ab08bb887..e241341fc5fa4 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KubernetesProcessor.java @@ -51,6 +51,7 @@ import io.quarkus.deployment.util.FileUtil; import io.quarkus.kubernetes.spi.ConfigurationSupplierBuildItem; import io.quarkus.kubernetes.spi.ConfiguratorBuildItem; +import io.quarkus.kubernetes.spi.CustomKubernetesOutputDirBuildItem; import io.quarkus.kubernetes.spi.CustomProjectRootBuildItem; import io.quarkus.kubernetes.spi.DecoratorBuildItem; import io.quarkus.kubernetes.spi.DekorateOutputBuildItem; @@ -115,6 +116,7 @@ public void build(ApplicationInfoBuildItem applicationInfo, List decorators, BuildProducer dekorateSessionProducer, Optional customProjectRoot, + Optional customOutputDir, BuildProducer generatedResourceProducer, BuildProducer generatedKubernetesResourceProducer, BuildProducer outputDirectoryBuildItemBuildProducer) { @@ -187,8 +189,13 @@ public void build(ApplicationInfoBuildItem applicationInfo, } }); - Path targetDirectory = getEffectiveOutputDirectory(kubernetesConfig, project.getRoot(), - outputTarget.getOutputDirectory()); + //The targetDirectory should be the custom if provided, oterwise the 'default' output directory. + //I this case 'default' means that one that we used until now (up until we introduced the ability to override). + Path targetDirectory = customOutputDir + .map(c -> c.getOutputDir()) + .map(d -> d.isAbsolute() ? d : project.getRoot().resolve(d)) + .orElseGet(() -> getEffectiveOutputDirectory(kubernetesConfig, project.getRoot(), + outputTarget.getOutputDirectory())); outputDirectoryBuildItemBuildProducer.produce(new KubernetesOutputDirectoryBuildItem(targetDirectory)); diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithCustomOutputDirTest.java b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithCustomOutputDirTest.java new file mode 100644 index 0000000000000..4748fe0e1864d --- /dev/null +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/java/io/quarkus/it/kubernetes/KubernetesWithCustomOutputDirTest.java @@ -0,0 +1,72 @@ +package io.quarkus.it.kubernetes; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.quarkus.builder.BuildContext; +import io.quarkus.kubernetes.spi.CustomKubernetesOutputDirBuildItem; +import io.quarkus.kubernetes.spi.CustomProjectRootBuildItem; +import io.quarkus.test.ProdBuildResults; +import io.quarkus.test.ProdModeTestBuildStep; +import io.quarkus.test.ProdModeTestResults; +import io.quarkus.test.QuarkusProdModeTest; + +public class KubernetesWithCustomOutputDirTest { + + private static final String APP_NAME = "kubernetes-with-custom-output-dir"; + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot((jar) -> jar.addClasses(GreetingResource.class)) + .setApplicationName(APP_NAME) + .setApplicationVersion("0.1-SNAPSHOT") + .addBuildChainCustomizerEntries( + new QuarkusProdModeTest.BuildChainCustomizerEntry(CustomProjectRootBuildItemProducerProdMode.class, + List.of(CustomProjectRootBuildItem.class, CustomKubernetesOutputDirBuildItem.class), + Collections.emptyList())); + + @ProdBuildResults + private ProdModeTestResults prodModeTestResults; + + @Test + public void assertGeneratedResources() throws IOException { + final Path kubernetesDir = prodModeTestResults.getBuildDir().getParent().resolve("custom-sources") + .resolve(".kubernetes"); + assertThat(kubernetesDir) + .isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.json")) + .isDirectoryContaining(p -> p.getFileName().endsWith("kubernetes.yml")) + .satisfies(p -> assertThat(p.toFile().listFiles()).hasSize(2)); + List kubernetesList = DeserializationUtil.deserializeAsList(kubernetesDir.resolve("kubernetes.yml")); + assertThat(kubernetesList).filteredOn(h -> h.getMetadata().getName().equals(APP_NAME)) + .filteredOn(e -> e instanceof Deployment).singleElement().satisfies(d -> { + assertThat(d.getMetadata()).satisfies(m -> { + assertThat(m.getName()).isEqualTo(APP_NAME); + }); + }); + } + + public static class CustomProjectRootBuildItemProducerProdMode extends ProdModeTestBuildStep { + + public CustomProjectRootBuildItemProducerProdMode(Map testContext) { + super(testContext); + } + + @Override + public void execute(BuildContext context) { + context.produce(new CustomProjectRootBuildItem( + (Path) getTestContext().get(QuarkusProdModeTest.BUILD_CONTEXT_CUSTOM_SOURCES_PATH_KEY))); + context.produce(new CustomKubernetesOutputDirBuildItem( + ((Path) getTestContext().get(QuarkusProdModeTest.BUILD_CONTEXT_CUSTOM_SOURCES_PATH_KEY)) + .resolve(".kubernetes"))); + } + } +} From abd49d0adab7901ba5948d774fe64f15f44a037d Mon Sep 17 00:00:00 2001 From: Rolfe Dlugy-Hegwer Date: Wed, 10 Jul 2024 07:33:01 -0400 Subject: [PATCH 79/94] Apply QE feedback to community docs --- .../security-authentication-mechanisms.adoc | 3 ++ .../security-getting-started-tutorial.adoc | 2 +- docs/src/main/asciidoc/security-jwt.adoc | 6 +-- .../security-oidc-auth0-tutorial.adoc | 4 +- ...-bearer-token-authentication-tutorial.adoc | 10 ++-- ...rity-oidc-bearer-token-authentication.adoc | 38 +++++++------ ...idc-code-flow-authentication-tutorial.adoc | 4 +- ...ecurity-oidc-code-flow-authentication.adoc | 54 ++++++++++--------- ...urity-openid-connect-client-reference.adoc | 10 ++-- .../security-openid-connect-client.adoc | 8 +-- .../security-openid-connect-dev-services.adoc | 2 +- .../security-openid-connect-providers.adoc | 2 +- docs/src/main/asciidoc/security-testing.adoc | 4 +- 13 files changed, 81 insertions(+), 66 deletions(-) diff --git a/docs/src/main/asciidoc/security-authentication-mechanisms.adoc b/docs/src/main/asciidoc/security-authentication-mechanisms.adoc index ead7ae4238a0e..df72bd312e02d 100644 --- a/docs/src/main/asciidoc/security-authentication-mechanisms.adoc +++ b/docs/src/main/asciidoc/security-authentication-mechanisms.adoc @@ -48,7 +48,10 @@ ifndef::no-webauthn-authentication[] |WebAuthn |xref:security-webauthn.adoc[WebAuthn] endif::no-webauthn-authentication[] +ifndef::no-quarkus-kerberos[] |Kerberos ticket |link:https://quarkiverse.github.io/quarkiverse-docs/quarkus-kerberos/dev/index.html[Kerberos] +endif::no-quarkus-kerberos[] + |==== For more information, see the following <> table. diff --git a/docs/src/main/asciidoc/security-getting-started-tutorial.adoc b/docs/src/main/asciidoc/security-getting-started-tutorial.adoc index 5dcab363d539b..81e98d981a785 100644 --- a/docs/src/main/asciidoc/security-getting-started-tutorial.adoc +++ b/docs/src/main/asciidoc/security-getting-started-tutorial.adoc @@ -46,7 +46,7 @@ To examine the completed example, download the {quickstarts-archive-url}[archive git clone {quickstarts-clone-url} ---- -You can find the solution in the `security-jpa-quickstart` link:{quickstarts-tree-url}/security-jpa-quickstart[directory]. +You can find the solution in the `security-jpa-quickstart` link:{quickstarts-tree-url}/security-jpa-quickstart/[directory]. ==== :sectnums: diff --git a/docs/src/main/asciidoc/security-jwt.adoc b/docs/src/main/asciidoc/security-jwt.adoc index 101fd589580c6..781a24f71987e 100644 --- a/docs/src/main/asciidoc/security-jwt.adoc +++ b/docs/src/main/asciidoc/security-jwt.adoc @@ -825,7 +825,7 @@ Please see the xref:security-openid-connect-client-reference.adoc#token-propagat [[integration-testing-wiremock]] ==== Wiremock -If you configure `mp.jwt.verify.publickey.location` to point to HTTPS or HTTP based JsonWebKey (JWK) set then you can use the same approach as described in the xref:security-oidc-bearer-token-authentication.adoc#integration-testing[OpenID Connect Bearer Token Integration testing] `Wiremock` section but only change the `application.properties` to use MP JWT configuration properties instead: +If you configure `mp.jwt.verify.publickey.location` to point to HTTPS or HTTP based JsonWebKey (JWK) set then you can use the same approach as described in the xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing[OpenID Connect Bearer Token Integration testing] `Wiremock` section but only change the `application.properties` to use MP JWT configuration properties instead: [source, properties] ---- @@ -837,7 +837,7 @@ mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus [[integration-testing-keycloak]] ==== Keycloak -If you work with Keycloak and configure `mp.jwt.verify.publickey.location` to point to HTTPS or HTTP based JsonWebKey (JWK) set then you can use the same approach as described in the xref:security-oidc-bearer-token-authentication.adoc#integration-testing[OpenID Connect Bearer Token Integration testing] Keycloak section but only change the `application.properties` to use MP JWT configuration properties instead: +If you work with Keycloak and configure `mp.jwt.verify.publickey.location` to point to HTTPS or HTTP based JsonWebKey (JWK) set then you can use the same approach as described in the xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing[OpenID Connect Bearer Token Integration testing] Keycloak section but only change the `application.properties` to use MP JWT configuration properties instead: [source, properties] ---- @@ -865,7 +865,7 @@ mp.jwt.verify.issuer=${client.quarkus.oidc.auth-server-url} [[integration-testing-public-key]] ==== Local Public Key -You can use the same approach as described in the xref:security-oidc-bearer-token-authentication.adoc#integration-testing[OpenID Connect Bearer Token Integration testing] `Local Public Key` section but only change the `application.properties` to use MP JWT configuration properties instead: +You can use the same approach as described in the xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing[OpenID Connect Bearer Token Integration testing] `Local Public Key` section but only change the `application.properties` to use MP JWT configuration properties instead: [source, properties] ---- diff --git a/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc b/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc index 6fe1d7753f41e..318e537e9587c 100644 --- a/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc +++ b/docs/src/main/asciidoc/security-oidc-auth0-tutorial.adoc @@ -929,7 +929,7 @@ Press `r` and notice this test failing with `403` which is expected because the image::auth0-test-failure-403.png[Auth0 test failure 403] -Before fixing the test, let's review the options available for testing Quarkus endpoints secured by OIDC. These options might vary, depending on which flow your application supports and how you prefer to test. Endpoints which use OIDC authorization code flow can be tested using xref:security-oidc-code-flow-authentication#integration-testing[one of these options] and endpoints which use Bearer token authentication can be tested using xref:security-oidc-bearer-token-authentication#integration-testing[one of these options]. +Before fixing the test, let's review the options available for testing Quarkus endpoints secured by OIDC. These options might vary, depending on which flow your application supports and how you prefer to test. Endpoints which use OIDC authorization code flow can be tested using xref:security-oidc-code-flow-authentication#code-flow-integration-testing[one of these options] and endpoints which use Bearer token authentication can be tested using xref:security-oidc-bearer-token-authentication#bearer-token-integration-testing[one of these options]. As you can see, testing of the endpoints secured with Auth0 can be done with the help of `Wiremock`, or `@TestSecurity` annotation. Experiment with writing such tests on your own and reach out if you encounter any problems. @@ -957,7 +957,7 @@ image::auth0-password-grant.png[Auth0 password grant] It is important to clarify that we do not recommend using the deprecated OAuth2 `password` token grant in production. However using it can help testing the endpoint with tokens acquired from the live dev Auth0 tenant. ==== -`OidcTestClient` should be used to test applications accepting bearer tokens which will work for the endpoint developed in this tutorial as it supports both authorization code flow and bearer token authentication. You would need to use OIDC WireMock or `HtmlUnit` directly against the Auth0 dev tenant if only the authorization code flow was supported - in the latter case `HtmlUnit` test code would have to be aligned with how Auth0 challenges users to enter their credentials. If you like, you can copy the xref:security-oidc-code-flow-authentication#integration-testing-wiremock[HtmlUnit test fragment] from the documentation and experiment with it. +`OidcTestClient` should be used to test applications accepting bearer tokens which will work for the endpoint developed in this tutorial as it supports both authorization code flow and bearer token authentication. You would need to use OIDC WireMock or `HtmlUnit` directly against the Auth0 dev tenant if only the authorization code flow was supported - in the latter case `HtmlUnit` test code would have to be aligned with how Auth0 challenges users to enter their credentials. If you like, you can copy the xref:security-oidc-code-flow-authentication#code-flow-integration-testing-wiremock[HtmlUnit test fragment] from the documentation and experiment with it. In meantime we will now proceed with fixing the currently failing test using `OidcTestClient`. 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 c4a6a64a9a0e3..2cccadbdc69a8 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 @@ -190,7 +190,7 @@ Where: * `%prod.quarkus.oidc.auth-server-url` sets the base URL of the OpenID Connect (OIDC) server. The `%prod.` profile prefix ensures that `Dev Services for Keycloak` launches a container when you run the application in development (dev) mode. -For more information, see the <> section. +For more information, see the <> section. * `quarkus.oidc.client-id` sets a client id that identifies the application. * `quarkus.oidc.credentials.secret` sets the client secret, which is used by the `client_secret_basic` authentication method. @@ -206,7 +206,7 @@ You do not need to do this if you have already built a link:{quickstarts-tree-ur [NOTE] ==== Do not start the Keycloak server when you run the application in dev mode; `Dev Services for Keycloak` will start a container. -For more information, see the <> section. +For more information, see the <> section. ==== + . To start a Keycloak server, you can use Docker to run the following command: @@ -238,7 +238,7 @@ endif::no-quarkus-keycloak-admin-client[] -[[keycloak-dev-mode]] +[[bearer-token-tutorial-keycloak-dev-mode]] == Run the application in dev mode . To run the application in dev mode, run the following commands: @@ -301,7 +301,7 @@ include::{includes}/devtools/build-native.adoc[] == Test the application -For information about testing your application in dev mode, see the preceding <> section. +For information about testing your application in dev mode, see the preceding <> section. You can test the application launched in JVM or native modes with `curl`. @@ -352,7 +352,7 @@ export access_token=$(\ ) ---- -For information about writing integration tests that depend on `Dev Services for Keycloak`, see the xref:security-oidc-bearer-token-authentication.adoc#integration-testing-keycloak-devservices[Dev Services for Keycloak] section of the "OpenID Connect (OIDC) Bearer token authentication" guide. +For information about writing integration tests that depend on `Dev Services for Keycloak`, see the xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing-keycloak-devservices[Dev Services for Keycloak] section of the "OpenID Connect (OIDC) Bearer token authentication" guide. //:sectnums!: diff --git a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc index 0e3650ea89011..612a65ccd5c7a 100644 --- a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc @@ -88,20 +88,20 @@ Injection of `JsonWebToken` is supported in `@ApplicationScoped`, `@Singleton`, However, the use of `@RequestScoped` is required if the individual claims are injected as simple types. For more information, see the xref:security-jwt.adoc#supported-injection-scopes[Supported injection scopes] section of the Quarkus "Using JWT RBAC" guide. -[[user-info]] +[[bearer-token-user-info]] === `UserInfo` If you must request a UserInfo JSON object from the OIDC `UserInfo` endpoint, set `quarkus.oidc.authentication.user-info-required=true`. A request is sent to the OIDC provider `UserInfo` endpoint, and an `io.quarkus.oidc.UserInfo` (a simple `javax.json.JsonObject` wrapper) object is created. `io.quarkus.oidc.UserInfo` can be injected or accessed as a `SecurityIdentity` `userinfo` attribute. -`quarkus.oidc.authentication.user-info-required` is automatically enabled if one of these conditions is met: +`quarkus.oidc.authentication.user-info-required` is automatically enabled if one of these conditions is met: - if `quarkus.oidc.roles.source` is set to `userinfo` or `quarkus.oidc.token.verify-access-token-with-user-info` is set to `true` or `quarkus.oidc.authentication.id-token-required` is set to `false`, the current OIDC tenant must support a UserInfo endpoint in these cases. - if `io.quarkus.oidc.UserInfo` injection point is detected but only if the current OIDC tenant supports a UserInfo endpoint. -[[config-metadata]] +[[bearer-token-config-metadata]] === Configuration metadata The current tenant's discovered link:https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata[OpenID Connect Configuration Metadata] is represented by `io.quarkus.oidc.OidcConfigurationMetadata` and can be injected or accessed as a `SecurityIdentity` `configuration-metadata` attribute. @@ -207,7 +207,7 @@ public class ProtectedResource { For more information about the `io.quarkus.security.PermissionsAllowed` annotation, see the xref:security-authorize-web-endpoints-reference.adoc#permission-annotation[Permission annotation] section of the "Authorization of web endpoints" guide. -[[token-verification-introspection]] +[[bearer-token-token-verification-introspection]] === Token verification and introspection If the token is a JWT token, then, by default, it is verified with a `JsonWebKey` (JWK) key from a local `JsonWebKeySet`, retrieved from the OIDC provider's JWK endpoint. @@ -256,7 +256,7 @@ A disadvantage is that a remote OIDC metadata discovery call is required to disc The `io.quarkus.oidc.TokenIntrospection`, a simple `jakarta.json.JsonObject` wrapper object, will be created. It can be injected or accessed as a `SecurityIdentity` `introspection` attribute, providing either the JWT or opaque token has been successfully introspected. -[[token-introspection-userinfo-cache]] +[[bearer-token-token-introspection-userinfo-cache]] === Token introspection and `UserInfo` cache All opaque access tokens must be remotely introspected. @@ -305,7 +305,7 @@ Additionally, the cleanup timer, if activated, periodically checks for expired e You can experiment with the default cache implementation or register a custom one. -[[jwt-claim-verification]] +[[bearer-token-jwt-claim-verification]] === JSON Web Token claim verification After the bearer JWT token's signature has been verified and its `expires at` (`exp`) claim has been checked, the `iss` (`issuer`) claim value is verified next. @@ -542,7 +542,7 @@ public class BearerGlobalTokenChainValidator implements TokenCertificateValidato If introspection of the Bearer token is necessary, then `OidcProviderClient` must authenticate to the OIDC provider. For more information about supported authentication options, see the xref:security-oidc-code-flow-authentication.adoc#oidc-provider-client-authentication[OIDC provider client authentication] section in the Quarkus "OpenID Connect authorization code flow mechanism for protecting web applications" guide. -[[integration-testing]] +[[bearer-token-integration-testing]] === Testing [NOTE] @@ -574,7 +574,8 @@ testImplementation("io.rest-assured:rest-assured") testImplementation("io.quarkus:quarkus-junit5") ---- -[[integration-testing-wiremock]] +ifndef::no-deprecated-test-resource[] +[[bearer-token-integration-testing-wiremock]] ==== WireMock Add the following dependencies to your test project: @@ -694,6 +695,7 @@ public class CustomOidcWireMockStubTest { } } ---- +endif::no-deprecated-test-resource[] [[integration-testing-oidc-test-client]] === `OidcTestClient` @@ -709,7 +711,8 @@ For example, you have the following configuration: %test.quarkus.oidc.credentials.secret=secret ---- -To start, add the same dependency, `quarkus-test-oidc-server`, as described in the <> section. +ifndef::no-deprecated-test-resource[] +To start, add the same dependency, `quarkus-test-oidc-server`, as described in the <> section. Next, write the test code as follows: @@ -760,8 +763,9 @@ This test code acquires a token by using a `password` grant from the test `Auth0 For a test like this to work, the test `Auth0` application must have the `password` grant enabled. This example code also shows how to pass additional parameters. For `Auth0`, these are the `audience` and `scope` parameters. +endif::no-deprecated-test-resource[] -[[integration-testing-keycloak-devservices]] +[[bearer-token-integration-testing-keycloak-devservices]] ==== Dev Services for Keycloak The preferred approach for integration testing against Keycloak is xref:security-openid-connect-dev-services.adoc[Dev Services for Keycloak]. @@ -856,14 +860,14 @@ public class NativeBearerTokenAuthenticationIT extends BearerTokenAuthentication For more information about initializing and configuring Dev Services for Keycloak, see the xref:security-openid-connect-dev-services.adoc[Dev Services for Keycloak] guide. ifndef::no-deprecated-test-resource[] -[[integration-testing-keycloak]] +[[bearer-token-integration-testing-keycloak]] ==== `KeycloakTestResourceLifecycleManager` You can also use `KeycloakTestResourceLifecycleManager` for integration testing with Keycloak. [IMPORTANT] ==== -Use <> instead of `KeycloakTestResourceLifecycleManager` for integration testing with Keycloak, unless you have specific requirements for using `KeycloakTestResourceLifecycleManager`. +Use <> instead of `KeycloakTestResourceLifecycleManager` for integration testing with Keycloak, unless you have specific requirements for using `KeycloakTestResourceLifecycleManager`. ==== First, add the following dependency: @@ -973,13 +977,15 @@ quarkus.oidc.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y smallrye.jwt.sign.key.location=/privateKey.pem ---- -To generate JWT tokens, copy `privateKey.pem` from the `integration-tests/oidc-tenancy` in the `main` Quarkus repository and use a test code similar to the one in the preceding <> section. +ifndef::no-deprecated-test-resource[] +To generate JWT tokens, copy `privateKey.pem` from the `integration-tests/oidc-tenancy` in the `main` Quarkus repository and use a test code similar to the one in the preceding <> section. You can use your own test keys, if preferred. This approach provides limited coverage compared to the WireMock approach. For example, the remote communication code is not covered. +endif::no-deprecated-test-resource[] -[[integration-testing-security-annotation]] +[[bearer-token-integration-testing-security-annotation]] ==== TestSecurity annotation You can use `@TestSecurity` and `@OidcSecurity` annotations to test the `service` application endpoint code, which depends on either one, or all three, of the following injections: @@ -1339,11 +1345,11 @@ xref:security-openid-connect-multitenancy.adoc#tenant-config-resolver[Dynamic te Authentication that requires a dynamic tenant will fail. ==== -[[oidc-request-filters]] +[[bearer-token-oidc-request-filters]] == OIDC request filters You can filter OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFilter` implementations, which can update or add new request headers, and log requests. -For more information, see xref:security-oidc-code-flow-authentication#oidc-request-filters[OIDC request filters]. +For more information, see xref:security-oidc-code-flow-authentication#code-flow-oidc-request-filters[OIDC request filters]. == References 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 f47b743a12086..788913c7dc197 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 @@ -266,11 +266,11 @@ After clicking the `Login` button, you are redirected back to the application, a The session for this demo is valid for a short period of time and, on every page refresh, you will be asked to re-authenticate. For information about how to increase the session timeouts, see the Keycloak https://www.keycloak.org/docs/latest/server_admin/#_timeouts[session timeout] documentation. -For example, you can access the Keycloak Admin console directly from the dev UI by clicking the `Keycloak Admin` link if you use xref:security-oidc-code-flow-authentication.adoc#integration-testing-keycloak-devservices[Dev Services for Keycloak] in dev mode: +For example, you can access the Keycloak Admin console directly from the dev UI by clicking the `Keycloak Admin` link if you use xref:security-oidc-code-flow-authentication.adoc#code-flow-integration-testing-keycloak-devservices[Dev Services for Keycloak] in dev mode: image::dev-ui-oidc-keycloak-card.png[alt=Dev UI OpenID Connect Card,role="center"] -For more information about writing the integration tests that depend on `Dev Services for Keycloak`, see the xref:security-oidc-code-flow-authentication.adoc#integration-testing-keycloak-devservices[Dev Services for Keycloak] section. +For more information about writing the integration tests that depend on `Dev Services for Keycloak`, see the xref:security-oidc-code-flow-authentication.adoc#code-flow-integration-testing-keycloak-devservices[Dev Services for Keycloak] section. :sectnums!: diff --git a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc index 7a2ebc6cd5a4e..fcccaf23740f6 100644 --- a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc @@ -308,7 +308,7 @@ quarkus.oidc.introspection-credentials.name=introspection-user-name quarkus.oidc.introspection-credentials.secret=introspection-user-secret ---- -[[oidc-request-filters]] +[[code-flow-oidc-request-filters]] === OIDC request filters You can filter OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFilter` implementations, which can update or add new request headers and can also log requests. @@ -640,7 +640,7 @@ Injection of the `JsonWebToken` and `AccessTokenCredential` is supported in both Quarkus OIDC uses the refresh token to refresh the current ID and access tokens as part of its <> process. -[[user-info]] +[[code-flow-user-info]] ==== User info If the ID token does not provide enough information about the currently authenticated user, you can get more information from the `UserInfo` endpoint. @@ -655,7 +655,7 @@ A request is sent to the OIDC provider `UserInfo` endpoint by using the access t - if `io.quarkus.oidc.UserInfo` injection point is detected but only if the current OIDC tenant supports a UserInfo endpoint. -[[config-metadata]] +[[code-flow-config-metadata]] ==== Accessing the OIDC configuration information The current tenant's discovered link:https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata[OpenID Connect configuration metadata] is represented by `io.quarkus.oidc.OidcConfigurationMetadata` and can be injected or accessed as a `SecurityIdentity` `configuration-metadata` attribute. @@ -689,11 +689,11 @@ You can also map `SecurityIdentity` roles created from token claims to deploymen A core part of the authentication process is ensuring the chain of trust and validity of the information. This is done by ensuring tokens can be trusted. -[[token-verification-introspection]] +[[code-flow-token-verification-introspection]] ==== Token verification and introspection The verification process of OIDC authorization code flow tokens follows the Bearer token authentication token verification and introspection logic. -For more information, see the xref:security-oidc-bearer-token-authentication.adoc#token-verification-introspection[Token verification and introspection] section of the "Quarkus OpenID Connect (OIDC) Bearer token authentication" guide. +For more information, see the xref:security-oidc-bearer-token-authentication.adoc#bearer-token-token-verification-introspection[Token verification and introspection] section of the "Quarkus OpenID Connect (OIDC) Bearer token authentication" guide. [NOTE] ==== @@ -701,19 +701,19 @@ With Quarkus `web-app` applications, only the `IdToken` is verified by default b If you expect the access token to contain the roles required to access the current Quarkus endpoint (`quarkus.oidc.roles.source=accesstoken`), then it will also be verified. ==== -[[token-introspection-userinfo-cache]] +[[code-flow-token-introspection-userinfo-cache]] ==== Token introspection and UserInfo cache Code flow access tokens are not introspected unless they are expected to be the source of roles. However, they will be used to get `UserInfo`. There will be one or two remote calls with the code flow access token if the token introspection, `UserInfo`, or both are required. -For more information about using the default token cache or registering a custom cache implementation, see xref:security-oidc-bearer-token-authentication.adoc#token-introspection-userinfo-cache[Token introspection and UserInfo cache]. +For more information about using the default token cache or registering a custom cache implementation, see xref:security-oidc-bearer-token-authentication.adoc#bearer-token-token-introspection-userinfo-cache[Token introspection and UserInfo cache]. -[[jwt-claim-verification]] +[[code-flow-jwt-claim-verification]] ==== JSON web token claim verification -For information about the claim verification, including the `iss` (issuer) claim, see the xref:security-oidc-bearer-token-authentication.adoc#jwt-claim-verification[JSON Web Token claim verification] section. +For information about the claim verification, including the `iss` (issuer) claim, see the xref:security-oidc-bearer-token-authentication.adoc#bearer-token-jwt-claim-verification[JSON Web Token claim verification] section. It applies to ID tokens and also to access tokens in a JWT format, if the `web-app` application has requested the access token verification. [[jose4j-validator]] @@ -771,7 +771,7 @@ For example, to set the cookie path dynamically by using the value of the`X-Forw If `quarkus.oidc.authentication.cookie-path-header` is set but no configured HTTP header is available in the current request, then the `quarkus.oidc.authentication.cookie-path` will be checked. If your application is deployed across multiple domains, set the `quarkus.oidc.authentication.cookie-domain` property so that the session cookie is visible to all protected Quarkus services. -For example, if you have Quarkus services deployed on the following two domains, then you must set the `quarkus.oidc.authentication.cookie-domain` property to `company.net`. +For example, if you have Quarkus services deployed on the following two domains, then you must set the `quarkus.oidc.authentication.cookie-domain` property to `company.net`: * \https://whatever.wherever.company.net/ * \https://another.address.company.net/ @@ -1035,13 +1035,13 @@ Let's start with explicit logout operations. ==== User-initiated logout Users can request a logout by sending a request to the Quarkus endpoint logout path set with a `quarkus.oidc.logout.path` property. -For example, if the endpoint address is `https://application.com/webapp` and the `quarkus.oidc.logout.path` is set to "/logout", then the logout request must be sent to `https://application.com/webapp/logout`. +For example, if the endpoint address is `https://application.com/webapp` and the `quarkus.oidc.logout.path` is set to `/logout`, then the logout request must be sent to `https://application.com/webapp/logout`. This logout request starts an https://openid.net/specs/openid-connect-session-1_0.html#RPLogout[RP-initiated logout]. The user will be redirected to the OIDC provider to log out, where they can be asked to confirm the logout is indeed intended. The user will be returned to the endpoint post-logout page once the logout has been completed and if the `quarkus.oidc.logout.post-logout-path` property is set. -For example, if the endpoint address is `https://application.com/webapp` and the `quarkus.oidc.logout.post-logout-path` is set to "/signin", then the user will be returned to `https://application.com/webapp/signin`. +For example, if the endpoint address is `https://application.com/webapp` and the `quarkus.oidc.logout.post-logout-path` is set to `/signin`, then the user will be returned to `https://application.com/webapp/signin`. Note, this URI must be registered as a valid `post_logout_redirect_uri` in the OIDC provider. If the `quarkus.oidc.logout.post-logout-path` is set, then a `q_post_logout` cookie will be created and a matching `state` query parameter will be added to the logout redirect URI and the OIDC provider will return this `state` once the logout has been completed. @@ -1276,7 +1276,7 @@ This `access` token represents an authenticated user authorizing the current Qua For OIDC, you validate the ID token as proof of authentication validity whereas in the case of OAuth2, you validate the access token. This is done by subsequently calling an endpoint that requires the access token and that typically returns user information. -This approach is similar to the OIDC <> approach, with `UserInfo` fetched by Quarkus OIDC on your behalf. +This approach is similar to the OIDC <> approach, with `UserInfo` fetched by Quarkus OIDC on your behalf. For example, when working with GitHub, the Quarkus endpoint can acquire an `access` token, which allows the Quarkus endpoint to request a GitHub profile for the current user. @@ -1298,19 +1298,19 @@ This simplifies how you handle an application that supports multiple OIDC provid The next step is to ensure that the returned access token can be useful and is valid to the current Quarkus endpoint. The first way is to call the OAuth2 provider introspection endpoint by configuring `quarkus.oidc.introspection-path`, if the provider offers such an endpoint. In this case, you can use the access token as a source of roles using `quarkus.oidc.roles.source=accesstoken`. -If no introspection endpoint is present, you can attempt instead to request <> from the provider as it will at least validate the access token. +If no introspection endpoint is present, you can attempt instead to request <> from the provider as it will at least validate the access token. To do so, specify `quarkus.oidc.token.verify-access-token-with-user-info=true`. You also need to set the `quarkus.oidc.user-info-path` property to a URL endpoint that fetches the user info (or to an endpoint protected by the access token). For GitHub, since it does not have an introspection endpoint, requesting the UserInfo is required. [NOTE] ==== -Requiring <> involves making a remote call on every request. +Requiring <> involves making a remote call on every request. Therefore, `UserInfo` is embedded in the internal generated `IdToken` and saved in the encrypted session cookie. It can be disabled with `quarkus.oidc.cache-user-info-in-idtoken=false`. Alternatively, you might want to consider caching `UserInfo` using a default or custom UserInfo cache provider. -For more information, see the xref:security-oidc-bearer-token-authentication.adoc#token-introspection-userinfo-cache[Token Introspection and UserInfo cache] section of the "OpenID Connect (OIDC) Bearer token authentication" guide. +For more information, see the xref:security-oidc-bearer-token-authentication.adoc#bearer-token-token-introspection-userinfo-cache[Token Introspection and UserInfo cache] section of the "OpenID Connect (OIDC) Bearer token authentication" guide. Most well-known social OAuth2 providers enforce rate-limiting so there is a high chance you will prefer to have UserInfo cached. ==== @@ -1552,6 +1552,7 @@ Future callQuarkusService() async { If you plan to consume this application from a single-page application running on a different domain, you need to configure cross-origin resource sharing (CORS). For more information, see the xref:security-cors.adoc#cors-filter[CORS filter] section of the "Cross-origin resource sharing" guide. +ifndef::no-other-unproductized-features[] === Calling Cloud provider services ==== Google Cloud @@ -1587,6 +1588,7 @@ quarkus.oidc.client-id={GOOGLE_CLIENT_ID} quarkus.oidc.credentials.secret={GOOGLE_CLIENT_SECRET} quarkus.oidc.token.issuer=https://accounts.google.com ---- +endif::no-other-unproductized-features[] === Running Quarkus application behind a reverse proxy @@ -1671,7 +1673,7 @@ Then, you are ready to start authenticating your Quarkus users to the Okta SAML You can configure other OIDC providers to provide a SAML bridge similarly to how it can be done for Keycloak. -[[integration-testing]] +[[code-flow-integration-testing]] == Testing Testing is often tricky when it comes to authentication to a separate OIDC-like server. @@ -1708,7 +1710,7 @@ testImplementation("io.quarkus:quarkus-junit5") ---- ifndef::no-deprecated-test-resource[] -[[integration-testing-wiremock]] +[[code-flow-integration-testing-wiremock]] === Wiremock Add the following dependency: @@ -1796,7 +1798,7 @@ Additionally, `OidcWiremockTestResource` sets the token issuer and audience to ` `OidcWiremockTestResource` can be used to emulate all OIDC providers. endif::no-deprecated-test-resource[] -[[integration-testing-keycloak-devservices]] +[[code-flow-integration-testing-keycloak-devservices]] === Dev Services for Keycloak Using xref:security-openid-connect-dev-services.adoc[Dev Services for Keycloak] is recommended for integration testing against Keycloak. @@ -1821,7 +1823,8 @@ If a custom realm file has to be imported into Keycloak before running the tests quarkus.keycloak.devservices.realm-path=quarkus-realm.json ---- -Finally, write a test code the same way as it is described in the <> section. +ifndef::no-deprecated-test-resource[] +Finally, write a test code the same way as it is described in the <> section. The only difference is that `@WithTestResource` is no longer needed: [source, java] @@ -1830,13 +1833,14 @@ The only difference is that `@WithTestResource` is no longer needed: public class CodeFlowAuthorizationTest { } ---- +endif::no-deprecated-test-resource[] ifndef::no-deprecated-test-resource[] -[[integration-testing-keycloak]] +[[code-flow-integration-testing-keycloak]] === Using KeycloakTestResourceLifecycleManager Use `KeycloakTestResourceLifecycleManager` for your tests only if there is a good reason not to use `Dev Services for Keycloak`. -If you need to do the integration testing against Keycloak then you are encouraged to do it with <>. +If you need to do the integration testing against Keycloak then you are encouraged to do it with <>. First, add the following dependency: @@ -1877,7 +1881,7 @@ Then, configure the Maven Surefire plugin as follows (and similarly the Maven Fa ---- -Now, set the configuration and write the test code the same way as it is described in the <> section. +Now, set the configuration and write the test code the same way as it is described in the <> section. The only difference is the name of `WithTestResource`: [source, java] @@ -1898,7 +1902,7 @@ By default, `KeycloakTestResourceLifecycleManager` uses HTTPS to initialize a Ke The default realm name is `quarkus` and client id is `quarkus-web-app` - set `keycloak.realm` and `keycloak.web-app.client` system properties to customize the values if needed. endif::no-deprecated-test-resource[] -[[integration-testing-security-annotation]] +[[code-flow-integration-testing-security-annotation]] === TestSecurity annotation You can use @TestSecurity and @OidcSecurity annotations to test the `web-app` application endpoint code, which depends on either one of the following injections, or all four: @@ -1908,7 +1912,7 @@ You can use @TestSecurity and @OidcSecurity annotations to test the `web-app` ap * `UserInfo` * `OidcConfigurationMetadata` -For more information, see xref:security-oidc-bearer-token-authentication.adoc#integration-testing-security-annotation[Use TestingSecurity with injected JsonWebToken]. +For more information, see xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing-security-annotation[Use TestingSecurity with injected JsonWebToken]. === Checking errors in the logs 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 73ccafa26060b..b309660c8133a 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc @@ -956,7 +956,8 @@ Start by adding the following dependencies to your test project: ---- -[[integration-testing-wiremock]] +ifndef::no-deprecated-test-resource[] +[[oidc-client-ref-integration-testing-wiremock]] ==== Wiremock Add the following dependencies to your test project: @@ -1045,10 +1046,11 @@ quarkus.oidc-client.grant-options.password.password=alice ---- And finally, write the test code. Given the Wiremock-based resource above, the first test invocation should return the `access_token_1` access token, which will expire in 4 seconds. Use `awaitility` to wait for about 5 seconds, and now the next test invocation should return the `access_token_2` access token, which confirms the expired `access_token_1` access token has been refreshed. +endif::no-deprecated-test-resource[] ==== Keycloak -If you work with Keycloak, you can use the same approach described in the xref:security-oidc-bearer-token-authentication.adoc#integration-testing-keycloak[OpenID Connect Bearer Token Integration testing] Keycloak section. +If you work with Keycloak, you can use the same approach described in the xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing-keycloak[OpenID Connect Bearer Token Integration testing] Keycloak section. === How to check the errors in the logs @@ -1068,7 +1070,7 @@ quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".level=T quarkus.log.category."io.quarkus.oidc.client.runtime.OidcClientRecorder".min-level=TRACE ---- -[[oidc-request-filters]] +[[oidc-client-ref-oidc-request-filters]] == OIDC request filters You can filter OIDC requests made by Quarkus to the OIDC provider by registering one or more `OidcRequestFilter` implementations, which can update or add new request headers. For example, a filter can analyze the request body and add its digest as a new header value: @@ -1337,7 +1339,7 @@ As mentioned, use `AccessTokenRequestFilter` if you work with Keycloak or an Ope [[integration-testing-token-propagation]] === Testing -You can generate the tokens as described in xref:security-oidc-bearer-token-authentication.adoc#integration-testing[OpenID Connect Bearer Token Integration testing] section. +You can generate the tokens as described in xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing[OpenID Connect Bearer Token Integration testing] section. Prepare the REST test endpoints. You can have the test front-end endpoint, which uses the injected MP REST client with a registered token propagation filter, call the downstream endpoint. For example, see the `integration-tests/resteasy-client-oidc-token-propagation` in the `main` Quarkus repository. ifndef::no-quarkus-oidc-token-propagation[] diff --git a/docs/src/main/asciidoc/security-openid-connect-client.adoc b/docs/src/main/asciidoc/security-openid-connect-client.adoc index 2cd8cc109f104..649e884ee3e20 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client.adoc @@ -535,12 +535,12 @@ The preceding configuration references Keycloak, which is used by `ProtectedReso Both REST clients point to `ProtectedResource`'s HTTP address. NOTE: Adding a `%prod.` profile prefix to `quarkus.oidc.auth-server-url` ensures that `Dev Services for Keycloak` launches a container for you when the application is run in dev or test modes. -For more information, see the <> section. +For more information, see the <> section. == Starting and configuring the Keycloak server NOTE: Do not start the Keycloak server when you run the application in dev or test modes; `Dev Services for Keycloak` launches a container. -For more information, see the <> section. +For more information, see the <> section. Ensure you put the link:{quickstarts-tree-url}/security-openid-connect-client-quickstart/config/quarkus-realm.json[realm configuration file] on the classpath, in the `target/classes` directory. This placement ensures that the file is automatically imported in dev mode. However, if you have already built a link:{quickstarts-tree-url}/security-openid-connect-quickstart[complete solution], you do not need to add the realm file to the classpath because the build process has already done so. @@ -566,7 +566,7 @@ This `quarkus` realm file adds a `frontend` client, and `alice` and `admin` user `alice` has a `user` role. `admin` has both `user` and `admin` roles. -[[keycloak-dev-mode]] +[[oidc-client-keycloak-dev-mode]] == Running the application in dev mode To run the application in a dev mode, use: @@ -627,7 +627,7 @@ After a little while, when the build finishes, you can run the native binary dir == Testing the application -For more information about testing your application in dev mode, see the preceding <> section. +For more information about testing your application in dev mode, see the preceding <> section. You can test the application launched in JVM or Native modes with `curl`. 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 f8d398f50a74d..b43fabd579b2e 100644 --- a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc @@ -253,7 +253,7 @@ The OIDC Dev UI is also more beneficial because hybrid applications can also acc You can run the tests against a Keycloak container started in a test mode in a xref:continuous-testing.adoc[Continuous Testing] mode. It is also recommended to run the integration tests against Keycloak by using Dev Services for Keycloak. -For more information, see xref:security-oidc-bearer-token-authentication.adoc#integration-testing-keycloak-devservices[Testing OpenID Connect Service Applications with Dev Services] and xref:security-oidc-code-flow-authentication.adoc#integration-testing-keycloak-devservices[Testing OpenID Connect WebApp Applications with Dev Services]. +For more information, see xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing-keycloak-devservices[Testing OpenID Connect Service Applications with Dev Services] and xref:security-oidc-code-flow-authentication.adoc#code-flow-integration-testing-keycloak-devservices[Testing OpenID Connect WebApp Applications with Dev Services]. [[keycloak-initialization]] === Keycloak initialization diff --git a/docs/src/main/asciidoc/security-openid-connect-providers.adoc b/docs/src/main/asciidoc/security-openid-connect-providers.adoc index 2015d1160f206..c0b70fa440e8d 100644 --- a/docs/src/main/asciidoc/security-openid-connect-providers.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-providers.adoc @@ -734,7 +734,7 @@ It may not be a problem when Quarkus fetches public verification keys from OIDC- Therefore, UserInfo is embedded in an internally generated ID token and is encrypted in the session cookie. You can disable it with `quarkus.oidc.cache-user-info-in-idtoken=false`. -Alternatively, use a default or custom UserInfo cache provider, please see the xref:security-oidc-bearer-token-authentication#token-introspection-userinfo-cache[Token Introspection and UserInfo cache] section of the "OpenID Connect (OIDC) Bearer token authentication" guide. +Alternatively, use a default or custom UserInfo cache provider, please see the xref:security-oidc-bearer-token-authentication#bearer-token-token-introspection-userinfo-cache[Token Introspection and UserInfo cache] section of the "OpenID Connect (OIDC) Bearer token authentication" guide. == References diff --git a/docs/src/main/asciidoc/security-testing.adoc b/docs/src/main/asciidoc/security-testing.adoc index fa84c80d55444..7c0a1cd435a44 100644 --- a/docs/src/main/asciidoc/security-testing.adoc +++ b/docs/src/main/asciidoc/security-testing.adoc @@ -91,7 +91,7 @@ This will run the test with an identity with the given username and roles. Note disable authorization while also providing an identity to run the test under, which can be useful if the endpoint expects an identity to be present. -See xref:security-oidc-bearer-token-authentication.adoc#integration-testing-security-annotation[OpenID Connect Bearer Token Integration testing], xref:security-oidc-code-flow-authentication.adoc#integration-testing-security-annotation[OpenID Connect Authorization Code Flow Integration testing] and xref:security-jwt.adoc#integration-testing-security-annotation[SmallRye JWT Integration testing] for more details about testing the endpoint code which depends on the injected `JsonWebToken`. +See xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing-security-annotation[OpenID Connect Bearer Token Integration testing], xref:security-oidc-code-flow-authentication.adoc#code-flow-integration-testing-security-annotation[OpenID Connect Authorization Code Flow Integration testing] and xref:security-jwt.adoc#integration-testing-security-annotation[SmallRye JWT Integration testing] for more details about testing the endpoint code which depends on the injected `JsonWebToken`. Additionally, you can specify attributes for the identity, perhaps custom items that were added with identity augmentation: @@ -248,7 +248,7 @@ quarkus.http.auth.permission.form.auth-mechanism=form == Use Wiremock for Integration Testing You can also use Wiremock to mock the authorization OAuth2 and OIDC services: -See xref:security-oauth2.adoc#integration-testing[OAuth2 Integration testing], xref:security-oidc-bearer-token-authentication.adoc#integration-testing-wiremock[OpenID Connect Bearer Token Integration testing], xref:security-oidc-code-flow-authentication.adoc#integration-testing-wiremock[OpenID Connect Authorization Code Flow Integration testing] and xref:security-jwt.adoc#integration-testing-wiremock[SmallRye JWT Integration testing] for more details. +See xref:security-oauth2.adoc#integration-testing[OAuth2 Integration testing], xref:security-oidc-bearer-token-authentication.adoc#bearer-token-integration-testing-wiremock[OpenID Connect Bearer Token Integration testing], xref:security-oidc-code-flow-authentication.adoc#code-flow-integration-testing-wiremock[OpenID Connect Authorization Code Flow Integration testing] and xref:security-jwt.adoc#integration-testing-wiremock[SmallRye JWT Integration testing] for more details. == References From 656b38be0b014f469ef232d34047211b3c5383f1 Mon Sep 17 00:00:00 2001 From: Jakub Jedlicka Date: Wed, 10 Jul 2024 16:23:21 +0200 Subject: [PATCH 80/94] Fix code example for JSON serialisation in rest guide --- docs/src/main/asciidoc/rest.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/rest.adoc b/docs/src/main/asciidoc/rest.adoc index 5531dbb5c0001..f40b865e34917 100644 --- a/docs/src/main/asciidoc/rest.adoc +++ b/docs/src/main/asciidoc/rest.adoc @@ -1420,10 +1420,11 @@ public class Person { @SecureField(rolesAllowed = "${role:admin}") <1> private String address; - public Person(Long id, String first, String last) { + public Person(Long id, String first, String last, String address) { this.id = id; this.first = first; this.last = last; + this.address = address; } public Long getId() { @@ -1466,7 +1467,7 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.Response; @Path("person") -public class Person { +public class PersonResource { @Path("{id}") @GET From 2e8bcc38ce9c518589fe307a1340c70e07706fe4 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 10 Jul 2024 15:03:33 +0200 Subject: [PATCH 81/94] Drop some old relocations They have been here since before the current LTS so we can safely drop them. --- bom/application/pom.xml | 40 ------------------- relocations/pom.xml | 8 ---- relocations/quarkus-jaeger-deployment/pom.xml | 22 ---------- relocations/quarkus-jaeger/pom.xml | 22 ---------- .../pom.xml | 22 ---------- .../pom.xml | 22 ---------- .../pom.xml | 22 ---------- .../pom.xml | 22 ---------- .../pom.xml | 22 ---------- .../quarkus-smallrye-opentracing/pom.xml | 22 ---------- 10 files changed, 224 deletions(-) delete mode 100644 relocations/quarkus-jaeger-deployment/pom.xml delete mode 100644 relocations/quarkus-jaeger/pom.xml delete mode 100644 relocations/quarkus-opentelemetry-exporter-jaeger-deployment/pom.xml delete mode 100644 relocations/quarkus-opentelemetry-exporter-jaeger/pom.xml delete mode 100644 relocations/quarkus-opentelemetry-exporter-otlp-deployment/pom.xml delete mode 100644 relocations/quarkus-opentelemetry-exporter-otlp/pom.xml delete mode 100644 relocations/quarkus-smallrye-opentracing-deployment/pom.xml delete mode 100644 relocations/quarkus-smallrye-opentracing/pom.xml diff --git a/bom/application/pom.xml b/bom/application/pom.xml index d5befe87004f0..51679bf5247b7 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -6401,46 +6401,6 @@ - - io.quarkus - quarkus-opentelemetry-exporter-jaeger-deployment - ${project.version} - - - io.quarkus - quarkus-opentelemetry-exporter-jaeger - ${project.version} - - - io.quarkus - quarkus-opentelemetry-exporter-otlp-deployment - ${project.version} - - - io.quarkus - quarkus-opentelemetry-exporter-otlp - ${project.version} - - - io.quarkus - quarkus-jaeger - ${project.version} - - - io.quarkus - quarkus-jaeger-deployment - ${project.version} - - - io.quarkus - quarkus-smallrye-opentracing - ${project.version} - - - io.quarkus - quarkus-smallrye-opentracing-deployment - ${project.version} - io.quarkus quarkus-hibernate-search-orm-coordination-outbox-polling diff --git a/relocations/pom.xml b/relocations/pom.xml index 74c679ecd5ea4..948de8d624f7b 100644 --- a/relocations/pom.xml +++ b/relocations/pom.xml @@ -16,14 +16,6 @@ pom - quarkus-opentelemetry-exporter-jaeger - quarkus-opentelemetry-exporter-jaeger-deployment - quarkus-opentelemetry-exporter-otlp - quarkus-opentelemetry-exporter-otlp-deployment - quarkus-jaeger - quarkus-jaeger-deployment - quarkus-smallrye-opentracing - quarkus-smallrye-opentracing-deployment quarkus-hibernate-search-orm-coordination-outbox-polling quarkus-hibernate-search-orm-coordination-outbox-polling-deployment quarkus-resteasy-reactive diff --git a/relocations/quarkus-jaeger-deployment/pom.xml b/relocations/quarkus-jaeger-deployment/pom.xml deleted file mode 100644 index d914dd2d3500d..0000000000000 --- a/relocations/quarkus-jaeger-deployment/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - quarkus-relocations-parent - io.quarkus - 999-SNAPSHOT - - 4.0.0 - - quarkus-jaeger-deployment - - - - io.quarkiverse.jaeger - quarkus-jaeger-deployment - 1.0.0 - ${project.groupId}:${project.artifactId}:${project.version} was relocated to io.quarkiverse.jaeger:${project.artifactId}:1.0.0 and is not managed by io.quarkus.platform:quarkus-bom:${project.version} anymore. Please, update the groupId and add the corresponding version to the dependency declaration in your project configuration. For more information about this change, please refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.6 - - - diff --git a/relocations/quarkus-jaeger/pom.xml b/relocations/quarkus-jaeger/pom.xml deleted file mode 100644 index 5b5139e08bf92..0000000000000 --- a/relocations/quarkus-jaeger/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - quarkus-relocations-parent - io.quarkus - 999-SNAPSHOT - - 4.0.0 - - quarkus-jaeger - - - - io.quarkiverse.jaeger - quarkus-jaeger - 1.0.0 - ${project.groupId}:${project.artifactId}:${project.version} was relocated to io.quarkiverse.jaeger:${project.artifactId}:1.0.0 and is not managed by io.quarkus.platform:quarkus-bom:${project.version} anymore. Please, update the groupId and add the corresponding version to the dependency declaration in your project configuration. For more information about this change, please refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.6 - - - diff --git a/relocations/quarkus-opentelemetry-exporter-jaeger-deployment/pom.xml b/relocations/quarkus-opentelemetry-exporter-jaeger-deployment/pom.xml deleted file mode 100644 index 8912b7489c93a..0000000000000 --- a/relocations/quarkus-opentelemetry-exporter-jaeger-deployment/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - quarkus-relocations-parent - io.quarkus - 999-SNAPSHOT - - 4.0.0 - - quarkus-opentelemetry-exporter-jaeger-deployment - - - - io.quarkiverse.opentelemetry.exporter - quarkus-opentelemetry-exporter-jaeger-deployment - 2.0.0.Final - ${project.groupId}:${project.artifactId}:${project.version} was relocated to io.quarkiverse.opentelemetry.exporter:${project.artifactId}:1.0.0 and is not managed by io.quarkus.platform:quarkus-bom:${project.version} anymore. Please, update the groupId and add the corresponding version to the dependency declaration in your project configuration. For more information about this change, please refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-2.14 - - - diff --git a/relocations/quarkus-opentelemetry-exporter-jaeger/pom.xml b/relocations/quarkus-opentelemetry-exporter-jaeger/pom.xml deleted file mode 100644 index f337c497e23fb..0000000000000 --- a/relocations/quarkus-opentelemetry-exporter-jaeger/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - quarkus-relocations-parent - io.quarkus - 999-SNAPSHOT - - 4.0.0 - - quarkus-opentelemetry-exporter-jaeger - - - - io.quarkiverse.opentelemetry.exporter - quarkus-opentelemetry-exporter-jaeger - 2.0.0.Final - ${project.groupId}:${project.artifactId}:${project.version} was relocated to io.quarkiverse.opentelemetry.exporter:${project.artifactId}:1.0.0 and is not managed by io.quarkus.platform:quarkus-bom:${project.version} anymore. Please, update the groupId and add the corresponding version to the dependency declaration in your project configuration. For more information about this change, please refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-2.14 - - - diff --git a/relocations/quarkus-opentelemetry-exporter-otlp-deployment/pom.xml b/relocations/quarkus-opentelemetry-exporter-otlp-deployment/pom.xml deleted file mode 100644 index d2190e67daddd..0000000000000 --- a/relocations/quarkus-opentelemetry-exporter-otlp-deployment/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - quarkus-relocations-parent - io.quarkus - 999-SNAPSHOT - - 4.0.0 - - quarkus-opentelemetry-exporter-otlp-deployment - - - - io.quarkus - quarkus-opentelemetry-deployment - ${project.version} - ${project.groupId}:${project.artifactId}:${project.version} was relocated to io.quarkus:${project.artifactId}:2.14.0.Final and is not managed by io.quarkus.platform:quarkus-bom:${project.version} anymore. Please, update the groupId and add the corresponding version to the dependency declaration in your project configuration. For more information about this change, please refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-2.14 - - - diff --git a/relocations/quarkus-opentelemetry-exporter-otlp/pom.xml b/relocations/quarkus-opentelemetry-exporter-otlp/pom.xml deleted file mode 100644 index 8a511b45e7440..0000000000000 --- a/relocations/quarkus-opentelemetry-exporter-otlp/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - quarkus-relocations-parent - io.quarkus - 999-SNAPSHOT - - 4.0.0 - - quarkus-opentelemetry-exporter-otlp - - - - io.quarkus - quarkus-opentelemetry - ${project.version} - ${project.groupId}:${project.artifactId}:${project.version} was relocated to io.quarkus:${project.artifactId}:2.14.0.Final and is not managed by io.quarkus.platform:quarkus-bom:${project.version} anymore. Please, update the groupId and add the corresponding version to the dependency declaration in your project configuration. For more information about this change, please refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-2.14 - - - diff --git a/relocations/quarkus-smallrye-opentracing-deployment/pom.xml b/relocations/quarkus-smallrye-opentracing-deployment/pom.xml deleted file mode 100644 index 88e6dc8b0d225..0000000000000 --- a/relocations/quarkus-smallrye-opentracing-deployment/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - quarkus-relocations-parent - io.quarkus - 999-SNAPSHOT - - 4.0.0 - - quarkus-smallrye-opentracing-deployment - - - - io.quarkiverse.opentracing - quarkus-smallrye-opentracing-deployment - 1.0.0 - ${project.groupId}:${project.artifactId}:${project.version} was relocated to io.quarkiverse.opentracing:${project.artifactId}:1.0.0 and is not managed by io.quarkus.platform:quarkus-bom:${project.version} anymore. Please, update the groupId and add the corresponding version to the dependency declaration in your project configuration. For more information about this change, please refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.6 - - - diff --git a/relocations/quarkus-smallrye-opentracing/pom.xml b/relocations/quarkus-smallrye-opentracing/pom.xml deleted file mode 100644 index 442b37e3ad562..0000000000000 --- a/relocations/quarkus-smallrye-opentracing/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - quarkus-relocations-parent - io.quarkus - 999-SNAPSHOT - - 4.0.0 - - quarkus-smallrye-opentracing - - - - io.quarkiverse.opentracing - quarkus-smallrye-opentracing - 1.0.0 - ${project.groupId}:${project.artifactId}:${project.version} was relocated to io.quarkiverse.opentracing:${project.artifactId}:1.0.0 and is not managed by io.quarkus.platform:quarkus-bom:${project.version} anymore. Please, update the groupId and add the corresponding version to the dependency declaration in your project configuration. For more information about this change, please refer to https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.6 - - - From 769358695e98957e95d0b4f7e2c1a347dd3e6046 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Wed, 10 Jul 2024 10:59:42 +0200 Subject: [PATCH 82/94] SmallRye GraphQL 2.9.0 --- bom/application/pom.xml | 4 ++-- .../graphql/deployment/SmallRyeGraphQLProcessor.java | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index d5befe87004f0..9ec8b6b2d31b7 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -38,7 +38,7 @@ 1.12.5 2.1.12 0.22.0 - 21.3 + 22.1 3.1 4.0.1 4.0.1 @@ -55,7 +55,7 @@ 4.1.0 4.0.0 3.10.0 - 2.8.6 + 2.9.0 6.3.0 4.5.3 2.1.2 diff --git a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java index c831f5f6ffd9f..1506dd8f0ee34 100644 --- a/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java +++ b/extensions/smallrye-graphql/deployment/src/main/java/io/quarkus/smallrye/graphql/deployment/SmallRyeGraphQLProcessor.java @@ -53,6 +53,7 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassConditionBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.maven.dependency.GACT; @@ -241,6 +242,11 @@ void registerNativeResourceBundle(BuildProducer runtimeInitializedClasses) { + runtimeInitializedClasses.produce(new RuntimeInitializedClassBuildItem("graphql.util.IdGenerator")); + } + @BuildStep SmallRyeGraphQLModifiedClasesBuildItem createIndex(TransformedClassesBuildItem transformedClassesBuildItem) { Map modifiedClasses = new HashMap<>(); From a010cbd7d29061d9e53b2121556461c8203245aa Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 11 Jul 2024 09:44:37 +0300 Subject: [PATCH 83/94] Properly close AsyncFile in Quarkus REST Fixes: #41811 --- .../serializers/ServerMutinyAsyncFileMessageBodyWriter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/serializers/ServerMutinyAsyncFileMessageBodyWriter.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/serializers/ServerMutinyAsyncFileMessageBodyWriter.java index 2262c937af40e..ec06ca2cc35bd 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/serializers/ServerMutinyAsyncFileMessageBodyWriter.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/serializers/ServerMutinyAsyncFileMessageBodyWriter.java @@ -65,7 +65,9 @@ public void writeResponse(AsyncFile file, Type genericType, ServerRequestContext file.endHandler(new Runnable() { @Override public void run() { - file.close(); + // we don't need to wait for the file to be closed, we just need to make sure it does get closed + //noinspection ResultOfMethodCallIgnored + file.close().subscribeAsCompletionStage(); response.end(); // Not sure if I need to resume, actually ctx.resume(); From 066ef6ab860779506682cf37fcb56382bf12650f Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Thu, 11 Jul 2024 08:56:21 +0200 Subject: [PATCH 84/94] Some adjustments to GraphQL TCKs --- .../src/main/resources/overrides/basicScalarTests.csv | 3 +++ .../main/resources/overrides/importantDatesUS/output.json | 5 +++++ .../src/main/resources/overrides/trackLongLat/output.json | 5 +++++ 3 files changed, 13 insertions(+) create mode 100644 tcks/microprofile-graphql/src/main/resources/overrides/basicScalarTests.csv create mode 100644 tcks/microprofile-graphql/src/main/resources/overrides/importantDatesUS/output.json create mode 100644 tcks/microprofile-graphql/src/main/resources/overrides/trackLongLat/output.json diff --git a/tcks/microprofile-graphql/src/main/resources/overrides/basicScalarTests.csv b/tcks/microprofile-graphql/src/main/resources/overrides/basicScalarTests.csv new file mode 100644 index 0000000000000..5f2526d80fc25 --- /dev/null +++ b/tcks/microprofile-graphql/src/main/resources/overrides/basicScalarTests.csv @@ -0,0 +1,3 @@ +# Basic Scalar Types +23| type ScalarHolder | charArray: [String!] | Expecting a String Array Scalar (for Java Char[]) Type in type ScalarHolder +47| type Query | testCharArray: [String!] | Expecting a non null Stirng Array (for Java Char[]) Scalar Type in type Query \ No newline at end of file diff --git a/tcks/microprofile-graphql/src/main/resources/overrides/importantDatesUS/output.json b/tcks/microprofile-graphql/src/main/resources/overrides/importantDatesUS/output.json new file mode 100644 index 0000000000000..43170576feaa0 --- /dev/null +++ b/tcks/microprofile-graphql/src/main/resources/overrides/importantDatesUS/output.json @@ -0,0 +1,5 @@ +{ + "data": { + "importantDatesUS": null + } +} \ No newline at end of file diff --git a/tcks/microprofile-graphql/src/main/resources/overrides/trackLongLat/output.json b/tcks/microprofile-graphql/src/main/resources/overrides/trackLongLat/output.json new file mode 100644 index 0000000000000..7bb698a75f141 --- /dev/null +++ b/tcks/microprofile-graphql/src/main/resources/overrides/trackLongLat/output.json @@ -0,0 +1,5 @@ +{ + "data": { + "trackHeroLongLat": null + } +} \ No newline at end of file From 00350b7a84a3a1ec0aee49be9e848c84cda2bafd Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 19 Jun 2024 18:26:53 +0200 Subject: [PATCH 85/94] Include .env in the config hack of TestSupport Fixes #41282 I can't say I'm a big fan of the fix but we will have to live with this for now. --- .../deployment/dev/testing/TestSupport.java | 155 ++++++++---------- 1 file changed, 69 insertions(+), 86 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java index 61bb2d278d1c2..2d5ec46885b5e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java @@ -1,7 +1,6 @@ package io.quarkus.deployment.dev.testing; import java.io.IOException; -import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; @@ -13,7 +12,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Properties; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; @@ -42,7 +40,10 @@ import io.quarkus.dev.testing.TestWatchedFiles; import io.quarkus.maven.dependency.ResolvedDependency; import io.quarkus.paths.PathList; -import io.smallrye.config.Converters; +import io.quarkus.runtime.configuration.ApplicationPropertiesConfigSourceLoader; +import io.smallrye.config.DotEnvConfigSourceProvider; +import io.smallrye.config.SmallRyeConfig; +import io.smallrye.config.SmallRyeConfigBuilder; public class TestSupport implements TestController { @@ -76,13 +77,13 @@ public class TestSupport implements TestController { private Throwable compileProblem; private volatile boolean firstRun = true; - String appPropertiesIncludeTags; - String appPropertiesExcludeTags; + List appPropertiesIncludeTags; + List appPropertiesExcludeTags; String appPropertiesIncludePattern; String appPropertiesExcludePattern; - String appPropertiesIncludeEngines; - String appPropertiesExcludeEngines; - String appPropertiesTestType; + List appPropertiesIncludeEngines; + List appPropertiesExcludeEngines; + TestType appPropertiesTestType; private TestConfig config; private volatile boolean closed; @@ -509,90 +510,72 @@ public void addListener(TestListener listener) { * We also can't apply this as part of the test startup, as it is too * late and the filters have already been resolved. *

- * We manually check for application.properties changes and apply them. + * We manually check for configuration changes and apply them. */ private void handleApplicationPropertiesChange() { - for (Path rootPath : curatedApplication.getQuarkusBootstrap().getApplicationRoot()) { - Path appProps = rootPath.resolve("application.properties"); - if (Files.exists(appProps)) { - Properties p = new Properties(); - try (InputStream in = Files.newInputStream(appProps)) { - p.load(in); - } catch (IOException e) { - throw new RuntimeException(e); + SmallRyeConfig updatedConfig = getMinimalConfig(); + + List includeTags = getTrimmedListFromConfig(updatedConfig, "quarkus.test.include-tags").orElse(List.of()); + List excludeTags = getTrimmedListFromConfig(updatedConfig, "quarkus.test.exclude-tags").orElse(List.of()); + String includePattern = updatedConfig.getOptionalValue("quarkus.test.include-pattern", String.class).orElse(null); + String excludePattern = updatedConfig.getOptionalValue("quarkus.test.exclude-pattern", String.class).orElse(null); + List includeEngines = getTrimmedListFromConfig(updatedConfig, "quarkus.test.include-engines").orElse(List.of()); + List excludeEngines = getTrimmedListFromConfig(updatedConfig, "quarkus.test.exclude-engines").orElse(List.of()); + TestType testType = updatedConfig.getOptionalValue("quarkus.test.type", TestType.class).orElse(TestType.ALL); + + if (!firstRun) { + if (!Objects.equals(includeTags, appPropertiesIncludeTags)) { + this.includeTags = includeTags; + } + if (!Objects.equals(excludeTags, appPropertiesExcludeTags)) { + this.excludeTags = excludeTags; + } + if (!Objects.equals(includePattern, appPropertiesIncludePattern)) { + if (includePattern == null) { + this.include = null; + } else { + this.include = Pattern.compile(includePattern); } - String includeTags = p.getProperty("quarkus.test.include-tags"); - String excludeTags = p.getProperty("quarkus.test.exclude-tags"); - String includePattern = p.getProperty("quarkus.test.include-pattern"); - String excludePattern = p.getProperty("quarkus.test.exclude-pattern"); - String includeEngines = p.getProperty("quarkus.test.include-engines"); - String excludeEngines = p.getProperty("quarkus.test.exclude-engines"); - String testType = p.getProperty("quarkus.test.type"); - if (!firstRun) { - if (!Objects.equals(includeTags, appPropertiesIncludeTags)) { - if (includeTags == null) { - this.includeTags = Collections.emptyList(); - } else { - this.includeTags = Arrays.stream(includeTags.split(",")).map(String::trim) - .collect(Collectors.toList()); - } - } - if (!Objects.equals(excludeTags, appPropertiesExcludeTags)) { - if (excludeTags == null) { - this.excludeTags = Collections.emptyList(); - } else { - this.excludeTags = Arrays.stream(excludeTags.split(",")).map(String::trim) - .collect(Collectors.toList()); - } - } - if (!Objects.equals(includePattern, appPropertiesIncludePattern)) { - if (includePattern == null) { - include = null; - } else { - include = Pattern.compile(includePattern); - } - } - if (!Objects.equals(excludePattern, appPropertiesExcludePattern)) { - if (excludePattern == null) { - exclude = null; - } else { - exclude = Pattern.compile(excludePattern); - } - } - if (!Objects.equals(includeEngines, appPropertiesIncludeEngines)) { - if (includeEngines == null) { - this.includeEngines = Collections.emptyList(); - } else { - this.includeEngines = Arrays.stream(includeEngines.split(",")).map(String::trim) - .collect(Collectors.toList()); - } - } - if (!Objects.equals(excludeEngines, appPropertiesExcludeEngines)) { - if (excludeEngines == null) { - this.excludeEngines = Collections.emptyList(); - } else { - this.excludeEngines = Arrays.stream(excludeEngines.split(",")).map(String::trim) - .collect(Collectors.toList()); - } - } - if (!Objects.equals(testType, appPropertiesTestType)) { - if (testType == null) { - this.testType = TestType.ALL; - } else { - this.testType = Converters.getImplicitConverter(TestType.class).convert(testType); - } - } + } + if (!Objects.equals(excludePattern, appPropertiesExcludePattern)) { + if (excludePattern == null) { + this.exclude = null; + } else { + this.exclude = Pattern.compile(excludePattern); } - appPropertiesIncludeTags = includeTags; - appPropertiesExcludeTags = excludeTags; - appPropertiesIncludePattern = includePattern; - appPropertiesExcludePattern = excludePattern; - appPropertiesIncludeEngines = includeEngines; - appPropertiesExcludeEngines = excludeEngines; - appPropertiesTestType = testType; - break; + } + if (!Objects.equals(includeEngines, appPropertiesIncludeEngines)) { + this.includeEngines = includeEngines; + } + if (!Objects.equals(excludeEngines, appPropertiesExcludeEngines)) { + this.excludeEngines = excludeEngines; + } + if (!Objects.equals(testType, appPropertiesTestType)) { + this.testType = testType; } } + + appPropertiesIncludeTags = includeTags; + appPropertiesExcludeTags = excludeTags; + appPropertiesIncludePattern = includePattern; + appPropertiesExcludePattern = excludePattern; + appPropertiesIncludeEngines = includeEngines; + appPropertiesExcludeEngines = excludeEngines; + appPropertiesTestType = testType; + } + + private static SmallRyeConfig getMinimalConfig() { + return new SmallRyeConfigBuilder() + .addDefaultSources() + .withSources(new ApplicationPropertiesConfigSourceLoader.InFileSystem()) + .withSources(new ApplicationPropertiesConfigSourceLoader.InClassPath()) + .withSources(new DotEnvConfigSourceProvider()).build(); + } + + private Optional> getTrimmedListFromConfig(SmallRyeConfig updatedConfig, String property) { + return updatedConfig.getOptionalValue(property, String.class) + .map(t -> Arrays.stream(t.split(",")).map(String::trim) + .collect(Collectors.toList())); } public boolean isStarted() { From 9a71a13cd07e9baed9d23ea2fc31a7e604a8e2db Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 3 Jul 2024 19:11:53 +0200 Subject: [PATCH 86/94] Modernize WatchServiceFileSystemWatcher - Switch to java.nio consistently - Allow to monitor a directory for specific files - Monitor .env file --- .../dev/RuntimeUpdatesProcessor.java | 14 +- .../dev/filesystem/watch/FileChangeEvent.java | 8 +- .../watch/WatchServiceFileSystemWatcher.java | 157 +++++++++++++----- .../dev/FileSystemWatcherTestCase.java | 84 +++++----- 4 files changed, 167 insertions(+), 96 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java index ca8f49634d88a..d69f80e7cf25f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java @@ -249,13 +249,17 @@ public void handleChanges(Collection changes) { periodicTestCompile(); } }; + // monitor .env as it can impact test execution + testClassChangeWatcher.watchFiles(Path.of(context.getApplicationRoot().getProjectDirectory()), + List.of(Path.of(".env")), + callback); Set nonExistent = new HashSet<>(); for (DevModeContext.ModuleInfo module : context.getAllModules()) { for (Path path : module.getMain().getSourcePaths()) { - testClassChangeWatcher.watchPath(path.toFile(), callback); + testClassChangeWatcher.watchDirectoryRecursively(path, callback); } for (Path path : module.getMain().getResourcePaths()) { - testClassChangeWatcher.watchPath(path.toFile(), callback); + testClassChangeWatcher.watchDirectoryRecursively(path, callback); } } for (DevModeContext.ModuleInfo module : context.getAllModules()) { @@ -264,14 +268,14 @@ public void handleChanges(Collection changes) { if (!Files.isDirectory(path)) { nonExistent.add(path); } else { - testClassChangeWatcher.watchPath(path.toFile(), callback); + testClassChangeWatcher.watchDirectoryRecursively(path, callback); } } for (Path path : module.getTest().get().getResourcePaths()) { if (!Files.isDirectory(path)) { nonExistent.add(path); } else { - testClassChangeWatcher.watchPath(path.toFile(), callback); + testClassChangeWatcher.watchDirectoryRecursively(path, callback); } } } @@ -287,7 +291,7 @@ public void run() { Path i = iterator.next(); if (Files.isDirectory(i)) { iterator.remove(); - testClassChangeWatcher.watchPath(i.toFile(), callback); + testClassChangeWatcher.watchDirectoryRecursively(i, callback); added = true; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/FileChangeEvent.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/FileChangeEvent.java index 33468a891627c..a8085072917a8 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/FileChangeEvent.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/FileChangeEvent.java @@ -1,6 +1,6 @@ package io.quarkus.deployment.dev.filesystem.watch; -import java.io.File; +import java.nio.file.Path; /** * The event object that is fired when a file system change is detected. @@ -10,7 +10,7 @@ */ public class FileChangeEvent { - private final File file; + private final Path file; private final Type type; /** @@ -19,7 +19,7 @@ public class FileChangeEvent { * @param file the file which is being watched * @param type the type of event that was encountered */ - public FileChangeEvent(File file, Type type) { + public FileChangeEvent(Path file, Type type) { this.file = file; this.type = type; } @@ -29,7 +29,7 @@ public FileChangeEvent(File file, Type type) { * * @return the file which was being watched */ - public File getFile() { + public Path getFile() { return file; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/WatchServiceFileSystemWatcher.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/WatchServiceFileSystemWatcher.java index cfd21f6f1940e..de2459b4f7599 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/WatchServiceFileSystemWatcher.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/WatchServiceFileSystemWatcher.java @@ -4,12 +4,12 @@ import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; -import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.ClosedWatchServiceException; import java.nio.file.FileSystems; +import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; @@ -26,6 +26,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.jboss.logging.Logger; @@ -43,9 +45,9 @@ public class WatchServiceFileSystemWatcher implements Runnable { private static final AtomicInteger threadIdCounter = new AtomicInteger(0); private WatchService watchService; - private final Map files = Collections.synchronizedMap(new HashMap()); + private final Map monitoredDirectories = Collections.synchronizedMap(new HashMap<>()); private final Map pathDataByKey = Collections - .synchronizedMap(new IdentityHashMap()); + .synchronizedMap(new IdentityHashMap<>()); private volatile boolean stopped = false; private final Thread watchThread; @@ -70,19 +72,19 @@ public void run() { try { PathData pathData = pathDataByKey.get(key); if (pathData != null) { - final List results = new ArrayList(); + final List results = new ArrayList<>(); List> events = key.pollEvents(); - final Set addedFiles = new HashSet(); - final Set deletedFiles = new HashSet(); + final Set addedFiles = new HashSet<>(); + final Set deletedFiles = new HashSet<>(); for (WatchEvent event : events) { Path eventPath = (Path) event.context(); - File targetFile = ((Path) key.watchable()).resolve(eventPath).toFile(); + Path targetFile = ((Path) key.watchable()).resolve(eventPath).toAbsolutePath(); FileChangeEvent.Type type; if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) { type = FileChangeEvent.Type.ADDED; addedFiles.add(targetFile); - if (targetFile.isDirectory()) { + if (Files.isDirectory(targetFile)) { try { addWatchedDirectory(pathData, targetFile); } catch (IOException e) { @@ -107,6 +109,12 @@ public void run() { Iterator it = results.iterator(); while (it.hasNext()) { FileChangeEvent event = it.next(); + + if (!pathData.isMonitored(event.getFile())) { + it.remove(); + continue; + } + if (event.getType() == FileChangeEvent.Type.MODIFIED) { if (addedFiles.contains(event.getFile()) && deletedFiles.contains(event.getFile())) { @@ -134,7 +142,7 @@ public void run() { } if (!results.isEmpty()) { - for (FileChangeCallback callback : pathData.callbacks) { + for (FileChangeCallback callback : pathData.getCallbacks()) { invokeCallback(callback, results); } } @@ -142,7 +150,7 @@ public void run() { } finally { //if the key is no longer valid remove it from the files list if (!key.reset()) { - files.remove(key.watchable()); + monitoredDirectories.remove(key.watchable()); } } } @@ -156,39 +164,59 @@ public void run() { } } - public synchronized void watchPath(File file, FileChangeCallback callback) { + public synchronized void watchDirectoryRecursively(Path directory, FileChangeCallback callback) { try { - PathData data = files.get(file); + Path absoluteDirectory = directory.toAbsolutePath(); + PathData data = monitoredDirectories.get(absoluteDirectory); if (data == null) { - Set allDirectories = doScan(file).keySet(); - Path path = Paths.get(file.toURI()); - data = new PathData(path); - for (File dir : allDirectories) { + Set allDirectories = doScan(absoluteDirectory).keySet(); + data = new PathData(absoluteDirectory, List.of()); + for (Path dir : allDirectories) { addWatchedDirectory(data, dir); } - files.put(file, data); + monitoredDirectories.put(absoluteDirectory, data); + } + data.addCallback(callback); + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * @param directory a directory that will be watched + * @param monitoredFiles list of monitored files relative to directory. An empty list will monitor all files. + * @param callback callback called when a file is changed + */ + public synchronized void watchFiles(Path directory, List monitoredFiles, FileChangeCallback callback) { + try { + Path absoluteDirectory = directory.toAbsolutePath(); + PathData data = monitoredDirectories.get(absoluteDirectory); + if (data == null) { + data = new PathData(absoluteDirectory, monitoredFiles); + addWatchedDirectory(data, absoluteDirectory); + monitoredDirectories.put(absoluteDirectory, data); } - data.callbacks.add(callback); + data.addCallback(callback); } catch (IOException e) { throw new RuntimeException(e); } } - private void addWatchedDirectory(PathData data, File dir) throws IOException { - Path path = Paths.get(dir.toURI()); - WatchKey key = path.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); + private void addWatchedDirectory(PathData data, Path dir) throws IOException { + WatchKey key = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); pathDataByKey.put(key, data); - data.keys.add(key); + data.addWatchKey(key); } - public synchronized void unwatchPath(File file, final FileChangeCallback callback) { - PathData data = files.get(file); + public synchronized void unwatchPath(Path directory, final FileChangeCallback callback) { + PathData data = monitoredDirectories.get(directory); if (data != null) { - data.callbacks.remove(callback); - if (data.callbacks.isEmpty()) { - files.remove(file); - for (WatchKey key : data.keys) { + data.removeCallback(callback); + if (data.getCallbacks().isEmpty()) { + monitoredDirectories.remove(directory); + for (WatchKey key : data.getWatchKeys()) { key.cancel(); pathDataByKey.remove(key); } @@ -205,20 +233,21 @@ public void close() throws IOException { } } - private static Map doScan(File file) { - final Map results = new HashMap(); + private static Map doScan(Path directory) { + final Map results = new HashMap<>(); - final Deque toScan = new ArrayDeque(); - toScan.add(file); + final Deque toScan = new ArrayDeque<>(); + toScan.add(directory); while (!toScan.isEmpty()) { - File next = toScan.pop(); - if (next.isDirectory()) { - results.put(next, next.lastModified()); - File[] list = next.listFiles(); - if (list != null) { - for (File f : list) { - toScan.push(new File(f.getAbsolutePath())); + Path next = toScan.pop(); + if (Files.isDirectory(next)) { + try { + results.put(next, Files.getLastModifiedTime(directory).toMillis()); + try (Stream list = Files.list(next)) { + list.forEach(p -> toScan.push(p.toAbsolutePath())); } + } catch (IOException e) { + throw new UncheckedIOException("Unable to scan: " + next, e); } } } @@ -234,12 +263,52 @@ private static void invokeCallback(FileChangeCallback callback, List callbacks = new ArrayList(); - final List keys = new ArrayList(); - private PathData(Path path) { + private final Path path; + private final List callbacks = new ArrayList<>(); + private final List watchKeys = new ArrayList<>(); + private final List monitoredFiles; + + private PathData(Path path, List monitoredFiles) { this.path = path; + this.monitoredFiles = monitoredFiles.stream().map(p -> path.resolve(p).toAbsolutePath()) + .collect(Collectors.toList()); + } + + private void addWatchKey(WatchKey key) { + this.watchKeys.add(key); + } + + private void addCallback(FileChangeCallback callback) { + this.callbacks.add(callback); + } + + private void removeCallback(FileChangeCallback callback) { + this.callbacks.remove(callback); + } + + private List getCallbacks() { + return callbacks; + } + + private List getWatchKeys() { + return watchKeys; + } + + private boolean isMonitored(Path file) { + if (monitoredFiles.isEmpty()) { + return true; + } + + Path absolutePath = file.isAbsolute() ? file : file.toAbsolutePath(); + + for (Path monitoredFile : monitoredFiles) { + if (monitoredFile.equals(absolutePath)) { + return true; + } + } + + return false; } } diff --git a/core/deployment/src/test/java/io/quarkus/deployment/dev/FileSystemWatcherTestCase.java b/core/deployment/src/test/java/io/quarkus/deployment/dev/FileSystemWatcherTestCase.java index 8ea252fa07e54..d605cb883cdd1 100644 --- a/core/deployment/src/test/java/io/quarkus/deployment/dev/FileSystemWatcherTestCase.java +++ b/core/deployment/src/test/java/io/quarkus/deployment/dev/FileSystemWatcherTestCase.java @@ -5,9 +5,12 @@ import static io.quarkus.deployment.dev.filesystem.watch.FileChangeEvent.Type.REMOVED; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; import java.util.Collection; +import java.util.Comparator; import java.util.concurrent.BlockingDeque; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; @@ -33,8 +36,8 @@ public class FileSystemWatcherTestCase { private final BlockingDeque> results = new LinkedBlockingDeque<>(); private final BlockingDeque> secondResults = new LinkedBlockingDeque<>(); - File rootDir; - File existingSubDir; + Path rootDir; + Path existingSubDir; @BeforeEach public void setup() throws Exception { @@ -42,30 +45,24 @@ public void setup() throws Exception { //as it just relies on polling Assumptions.assumeTrue(RuntimeUpdatesProcessor.IS_LINUX); - rootDir = new File(System.getProperty("java.io.tmpdir") + DIR_NAME); + rootDir = Path.of(System.getProperty("java.io.tmpdir"), DIR_NAME); deleteRecursive(rootDir); - rootDir.mkdirs(); - File existing = new File(rootDir, EXISTING_FILE_NAME); + Files.createDirectories(rootDir); + Path existing = rootDir.resolve(EXISTING_FILE_NAME); touchFile(existing); - existingSubDir = new File(rootDir, EXISTING_DIR); - existingSubDir.mkdir(); - existing = new File(existingSubDir, EXISTING_FILE_NAME); + existingSubDir = rootDir.resolve(EXISTING_DIR); + Files.createDirectory(existingSubDir); + existing = existingSubDir.resolve(EXISTING_FILE_NAME); touchFile(existing); } - private static void touchFile(File existing) throws IOException { - FileOutputStream out = new FileOutputStream(existing); - try { - out.write(("data" + System.currentTimeMillis()).getBytes()); - out.flush(); - } finally { - out.close(); - } + private static void touchFile(Path existing) throws IOException { + Files.writeString(existing, "data" + System.currentTimeMillis()); } @AfterEach - public void after() { + public void after() throws IOException { if (rootDir != null) { deleteRecursive(rootDir); } @@ -75,48 +72,48 @@ public void after() { public void testFileSystemWatcher() throws Exception { WatchServiceFileSystemWatcher watcher = new WatchServiceFileSystemWatcher("test", true); try { - watcher.watchPath(rootDir, new FileChangeCallback() { + watcher.watchDirectoryRecursively(rootDir, new FileChangeCallback() { @Override public void handleChanges(Collection changes) { results.add(changes); } }); - watcher.watchPath(rootDir, new FileChangeCallback() { + watcher.watchDirectoryRecursively(rootDir, new FileChangeCallback() { @Override public void handleChanges(Collection changes) { secondResults.add(changes); } }); //first add a file - File added = new File(rootDir, "newlyAddedFile.txt").getAbsoluteFile(); + Path added = rootDir.resolve("newlyAddedFile.txt").toAbsolutePath(); touchFile(added); checkResult(added, ADDED); - added.setLastModified(500); + Files.setLastModifiedTime(added, FileTime.fromMillis(500)); checkResult(added, MODIFIED); - added.delete(); + Files.delete(added); Thread.sleep(1); checkResult(added, REMOVED); - added = new File(existingSubDir, "newSubDirFile.txt"); + added = existingSubDir.resolve("newSubDirFile.txt"); touchFile(added); checkResult(added, ADDED); - added.setLastModified(500); + Files.setLastModifiedTime(added, FileTime.fromMillis(500)); checkResult(added, MODIFIED); - added.delete(); + Files.delete(added); Thread.sleep(1); checkResult(added, REMOVED); - File existing = new File(rootDir, EXISTING_FILE_NAME); - existing.delete(); + Path existing = rootDir.resolve(EXISTING_FILE_NAME); + Files.delete(existing); Thread.sleep(1); checkResult(existing, REMOVED); - File newDir = new File(rootDir, "newlyCreatedDirectory"); - newDir.mkdir(); + Path newDir = rootDir.resolve("newlyCreatedDirectory"); + Files.createDirectory(newDir); checkResult(newDir, ADDED); - added = new File(newDir, "newlyAddedFileInNewlyAddedDirectory.txt").getAbsoluteFile(); + added = newDir.resolve("newlyAddedFileInNewlyAddedDirectory.txt").toAbsolutePath(); touchFile(added); checkResult(added, ADDED); - added.setLastModified(500); + Files.setLastModifiedTime(added, FileTime.fromMillis(500)); checkResult(added, MODIFIED); - added.delete(); + Files.delete(added); Thread.sleep(1); checkResult(added, REMOVED); @@ -126,7 +123,7 @@ public void handleChanges(Collection changes) { } - private void checkResult(File file, FileChangeEvent.Type type) throws InterruptedException { + private void checkResult(Path file, FileChangeEvent.Type type) throws InterruptedException { Collection results = this.results.poll(20, TimeUnit.SECONDS); Collection secondResults = this.secondResults.poll(20, TimeUnit.SECONDS); Assertions.assertNotNull(results); @@ -151,8 +148,8 @@ private void checkResult(File file, FileChangeEvent.Type type) throws Interrupte endTime = System.currentTimeMillis() + 10000; while (type == ADDED && (res.getType() == MODIFIED || res2.getType() == MODIFIED) - && (res.getFile().equals(file.getParentFile()) || res2.getFile().equals(file.getParentFile())) - && !file.isDirectory() + && (res.getFile().equals(file.getParent()) || res2.getFile().equals(file.getParent())) + && !Files.isDirectory(file) && System.currentTimeMillis() < endTime) { FileChangeEvent[] nextEvents = consumeEvents(); res = nextEvents[0]; @@ -179,14 +176,15 @@ private FileChangeEvent[] consumeEvents() throws InterruptedException { return nextEvents; } - public static void deleteRecursive(final File file) { - File[] files = file.listFiles(); - if (files != null) { - for (File f : files) { - deleteRecursive(f); - } + public static void deleteRecursive(final Path path) throws IOException { + if (!Files.exists(path)) { + return; } - file.delete(); + + Files.walk(path) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); } } From 356bf552b116e238f420f6921898ce3c0f49bd07 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Thu, 11 Jul 2024 15:00:01 +0200 Subject: [PATCH 87/94] Initialize graphql.util.IdGenerator at runtime --- .../vertx/graphql/deployment/VertxGraphqlProcessor.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java b/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java index 4a33ea01ca16f..98619e8703088 100644 --- a/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java +++ b/extensions/vertx-graphql/deployment/src/main/java/io/quarkus/vertx/graphql/deployment/VertxGraphqlProcessor.java @@ -103,4 +103,9 @@ void registerVertxGraphqlUI(VertxGraphqlRecorder recorder, VertxGraphqlConfig co private static boolean doNotIncludeVertxGraphqlUi(LaunchModeBuildItem launchMode, VertxGraphqlConfig config) { return !launchMode.getLaunchMode().isDevOrTest() && !config.ui.alwaysInclude; } + + @BuildStep + void runtimeInitializedClasses(BuildProducer runtimeInitializedClasses) { + runtimeInitializedClasses.produce(new RuntimeInitializedClassBuildItem("graphql.util.IdGenerator")); + } } From d5d0aeec7ae444978017e53206aaf6011015ac04 Mon Sep 17 00:00:00 2001 From: xstefank Date: Wed, 10 Jul 2024 16:48:35 +0200 Subject: [PATCH 88/94] Create new vertx context for blocking health checks --- ...kingChecksVertxContextDuplicationTest.java | 65 +++++++++++++++++++ .../QuarkusAsyncHealthCheckFactory.java | 19 +++++- .../runtime/SmallRyeHealthHandlerBase.java | 2 +- 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/BlockingChecksVertxContextDuplicationTest.java diff --git a/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/BlockingChecksVertxContextDuplicationTest.java b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/BlockingChecksVertxContextDuplicationTest.java new file mode 100644 index 0000000000000..6fd84159bd3f4 --- /dev/null +++ b/extensions/smallrye-health/deployment/src/test/java/io/quarkus/smallrye/health/test/BlockingChecksVertxContextDuplicationTest.java @@ -0,0 +1,65 @@ +package io.quarkus.smallrye.health.test; + +import static org.hamcrest.Matchers.is; + +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.Liveness; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.parsing.Parser; +import io.vertx.core.Context; +import io.vertx.core.Vertx; + +class BlockingChecksVertxContextDuplicationTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(ContextCaptureCheck1.class, ContextCaptureCheck2.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")); + + @Test + void testBlockingChecksPropagateVertxContext() { + try { + RestAssured.defaultParser = Parser.JSON; + RestAssured.when().get("/q/health").then() + .body("status", is("UP"), + "checks.size()", is(2)); + + Assertions.assertNotEquals(ContextCaptureCheck1.capturedContext, ContextCaptureCheck2.capturedContext, + "Expected different contexts to be propagated into different blocking health checks"); + } finally { + RestAssured.reset(); + } + } + + @Liveness + public static class ContextCaptureCheck1 implements HealthCheck { + + public static Context capturedContext = null; + + @Override + public HealthCheckResponse call() { + capturedContext = Vertx.currentContext(); + return HealthCheckResponse.up("ContextCaptureCheck1"); + } + } + + @Liveness + public static class ContextCaptureCheck2 implements HealthCheck { + + public static Context capturedContext = null; + + @Override + public HealthCheckResponse call() { + capturedContext = Vertx.currentContext(); + return HealthCheckResponse.up("ContextCaptureCheck2"); + } + } +} diff --git a/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/QuarkusAsyncHealthCheckFactory.java b/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/QuarkusAsyncHealthCheckFactory.java index cfec52c933b1b..bd7c236dce2e2 100644 --- a/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/QuarkusAsyncHealthCheckFactory.java +++ b/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/QuarkusAsyncHealthCheckFactory.java @@ -1,14 +1,19 @@ package io.quarkus.smallrye.health.runtime; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; + import jakarta.inject.Singleton; import org.eclipse.microprofile.health.HealthCheck; import org.eclipse.microprofile.health.HealthCheckResponse; +import io.smallrye.common.vertx.VertxContext; import io.smallrye.health.AsyncHealthCheckFactory; import io.smallrye.health.api.AsyncHealthCheck; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.vertx.MutinyHelper; +import io.vertx.core.Context; import io.vertx.core.Vertx; /** @@ -27,7 +32,19 @@ public QuarkusAsyncHealthCheckFactory(Vertx vertx) { @Override public Uni callSync(HealthCheck healthCheck) { Uni healthCheckResponseUni = super.callSync(healthCheck); - return healthCheckResponseUni.runSubscriptionOn(MutinyHelper.blockingExecutor(vertx, false)); + return healthCheckResponseUni.runSubscriptionOn(new Executor() { + @Override + public void execute(Runnable command) { + Context duplicatedContext = VertxContext.createNewDuplicatedContext(vertx.getOrCreateContext()); + duplicatedContext.executeBlocking(new Callable() { + @Override + public Void call() throws Exception { + command.run(); + return null; + } + }, false); + } + }); } @Override diff --git a/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/SmallRyeHealthHandlerBase.java b/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/SmallRyeHealthHandlerBase.java index cc0bb85cce758..c35577a1d37d7 100644 --- a/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/SmallRyeHealthHandlerBase.java +++ b/extensions/smallrye-health/runtime/src/main/java/io/quarkus/smallrye/health/runtime/SmallRyeHealthHandlerBase.java @@ -63,7 +63,7 @@ private void doHandle(RoutingContext ctx, ManagedContext requestContext) { .set(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8") .set(HttpHeaders.CACHE_CONTROL, "no-store"); Buffer buffer = Buffer.buffer(256); // this size seems to cover the basic health checks - try (BufferOutputStream outputStream = new BufferOutputStream(buffer);) { + try (BufferOutputStream outputStream = new BufferOutputStream(buffer)) { reporter.reportHealth(outputStream, health); resp.end(buffer); } catch (IOException e) { From ab2bd29b466fe9061cdc8fc03da1b65db6a62e15 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Thu, 11 Jul 2024 13:42:57 -0300 Subject: [PATCH 89/94] Module `java.security.jgss` should export `sun.security.jgss` --- .../io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java index 59cf46bf025ab..48a3b83f1931c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java @@ -788,6 +788,7 @@ public NativeImageInvokerInfo build() { * control its actual inclusion which will depend on the usual analysis. */ nativeImageArgs.add("-J--add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED"); + nativeImageArgs.add("-J--add-exports=java.security.jgss/sun.security.jgss=ALL-UNNAMED"); //address https://github.com/quarkusio/quarkus-quickstarts/issues/993 nativeImageArgs.add("-J--add-opens=java.base/java.text=ALL-UNNAMED"); From 8ef35a8931d849c9099b9fe2d7e15d3363b77ee1 Mon Sep 17 00:00:00 2001 From: brunobat Date: Fri, 5 Jul 2024 10:10:43 +0100 Subject: [PATCH 90/94] OTel Metrics documentation. Base OTel connection config and overrides. --- .../_includes/opentelemetry-config.adoc | 36 + docs/src/main/asciidoc/datasource.adoc | 4 +- .../main/asciidoc/opentelemetry-metrics.adoc | 502 +++++++++++++ .../main/asciidoc/opentelemetry-tracing.adoc | 622 ++++++++++++++++ docs/src/main/asciidoc/opentelemetry.adoc | 702 ++++-------------- .../deployment/OpenTelemetryProcessor.java | 3 +- .../otlp/OtlpExporterBadEndpointTest.java | 2 +- .../exporter/otlp/OtlpExporterConfigTest.java | 10 +- .../otlp/OtlpExporterOverrideConfigTest.java | 34 + ...uredOpenTelemetrySdkBuilderCustomizer.java | 27 +- ...chicalOTelConnectionConfigInterceptor.java | 92 +++ .../runtime/exporter/OtlpExporterConfig.java | 12 - .../exporter/OtlpExporterRuntimeConfig.java | 74 +- .../exporter/OtlpExporterTracesConfig.java | 15 - .../exporter/otlp/OTelExporterRecorder.java | 37 +- ...io.smallrye.config.ConfigSourceInterceptor | 1 + ...alOTelConnectionConfigInterceptorTest.java | 62 ++ .../otlp/HttpClientOptionsConsumerTest.java | 5 - .../otlp/OtlpExporterProviderTest.java | 110 ++- 19 files changed, 1709 insertions(+), 641 deletions(-) create mode 100644 docs/src/main/asciidoc/_includes/opentelemetry-config.adoc create mode 100644 docs/src/main/asciidoc/opentelemetry-metrics.adoc create mode 100644 docs/src/main/asciidoc/opentelemetry-tracing.adoc create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterOverrideConfigTest.java create mode 100644 extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/HierarchicalOTelConnectionConfigInterceptor.java create mode 100644 extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceInterceptor create mode 100644 extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/config/HierarchicalOTelConnectionConfigInterceptorTest.java diff --git a/docs/src/main/asciidoc/_includes/opentelemetry-config.adoc b/docs/src/main/asciidoc/_includes/opentelemetry-config.adoc new file mode 100644 index 0000000000000..98e9507f6f8ca --- /dev/null +++ b/docs/src/main/asciidoc/_includes/opentelemetry-config.adoc @@ -0,0 +1,36 @@ +There are no mandatory configurations for the extension to work. + +By default, the exporters will send out data in batches, using the gRPC protocol and endpoint `http://localhost:4317`. + +If you need to change any of the default property values, here is an example on how to configure the default OTLP gRPC Exporter within the application, using the `src/main/resources/application.properties` file: + +[source,properties] +---- +quarkus.application.name=myservice // <1> +quarkus.otel.exporter.otlp.endpoint=http://localhost:4317 // <2> +quarkus.otel.exporter.otlp.headers=authorization=Bearer my_secret // <3> +quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} [%c{2.}] (%t) %s%e%n // <4> + +# Alternative to the console log +quarkus.http.access-log.pattern="...traceId=%{X,traceId} spanId=%{X,spanId}" // <5> +---- + +<1> All telemetry created from the application will include an OpenTelemetry `Resource` attribute indicating the telemetry was created by the `myservice` application. If not set, it will default to the artifact id. +<2> gRPC endpoint to send the telemetry. If not set, it will default to `http://localhost:4317`. +<3> Optional gRPC headers commonly used for authentication +<4> Add tracing information into log messages. +<5> You can also only put the trace info into the access log. In this case you must omit the info in the console log format. + +We provide signal agnostic configurations for the connection related properties, meaning that you can use the same properties for both tracing and metrics when you set: +[source,properties] +---- +quarkus.otel.exporter.otlp.endpoint=http://localhost:4317 +---- +If you need different configurations for each signal, you can use the specific properties: +[source,properties] +---- +quarkus.otel.exporter.otlp.traces.endpoint=http://trace-uri:4317 // <1> +quarkus.otel.exporter.otlp.metrics.endpoint=http://metrics-uri:4317 // <2> +---- +<1> The endpoint for the traces exporter. +<2> The endpoint for the metrics exporter. diff --git a/docs/src/main/asciidoc/datasource.adoc b/docs/src/main/asciidoc/datasource.adoc index eaf0e84cc44b2..46d9f291a6692 100644 --- a/docs/src/main/asciidoc/datasource.adoc +++ b/docs/src/main/asciidoc/datasource.adoc @@ -638,9 +638,9 @@ If the metrics collection for this datasource is disabled, all values result in [[datasource-tracing]] === Datasource tracing -To use tracing with a datasource, you need to add the xref:opentelemetry.adoc[`quarkus-opentelemetry`] extension to your project. +To use tracing with a datasource, you need to add the xref:opentelemetry-tracing.adoc[`quarkus-opentelemetry`] extension to your project. -You don't need to declare a different driver because you need tracing. If you use a JDBC driver, you need to follow the instructions in the OpenTelemetry extension xref:opentelemetry.adoc#jdbc[here]. +You don't need to declare a different driver because you need tracing. If you use a JDBC driver, you need to follow the instructions in the OpenTelemetry extension xref:opentelemetry-tracing.adoc#jdbc[here]. Even with all the tracing infrastructure in place the datasource tracing is not enabled by default, and you need to enable it by setting this property: [source, properties] diff --git a/docs/src/main/asciidoc/opentelemetry-metrics.adoc b/docs/src/main/asciidoc/opentelemetry-metrics.adoc new file mode 100644 index 0000000000000..d2b93e98d33be --- /dev/null +++ b/docs/src/main/asciidoc/opentelemetry-metrics.adoc @@ -0,0 +1,502 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// += Using OpenTelemetry Metrics +include::_attributes.adoc[] +:categories: observability +:summary: This guide explains how your Quarkus application can utilize OpenTelemetry to provide metrics for interactive web applications. +:topics: observability,opentelemetry,metrics +:extensions: io.quarkus:quarkus-opentelemetry + +This guide explains how your Quarkus application can utilize https://opentelemetry.io/[OpenTelemetry] (OTel) to provide +metrics for interactive web applications. + +[NOTE] +==== +- The xref:opentelemetry.adoc[OpenTelemetry Guide] is available with signal independent information about the OpenTelemetry extension. +- If you search more information about OpenTelemetry Tracing, please refer to the xref:opentelemetry-tracing.adoc[OpenTelemetry Tracing Guide]. +==== + + +== Prerequisites + +:prerequisites-docker-compose: +include::{includes}/prerequisites.adoc[] + +== Architecture + +In this guide, we create a straightforward REST application to demonstrate distributed tracing. + +== Solution + +We recommend that you follow the instructions in the next sections and create the application step by step. +However, you can skip right to the completed example. + +Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. + +The solution is located in the `opentelemetry-quickstart` link:{quickstarts-tree-url}/opentelemetry-quickstart[directory]. + +== Creating the Maven project + +First, we need a new project. Create a new project with the following command: + +:create-app-artifact-id: opentelemetry-quickstart +:create-app-extensions: rest,quarkus-opentelemetry +include::{includes}/devtools/create-app.adoc[] + +This command generates the Maven project and imports the `quarkus-opentelemetry` extension, +which includes the default OpenTelemetry support, +and a gRPC span exporter for https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md[OTLP]. + +If you already have your Quarkus project configured, you can add the `quarkus-opentelemetry` extension +to your project by running the following command in your project base directory: + +:add-extension-extensions: opentelemetry +include::{includes}/devtools/extension-add.adoc[] + +This will add the following to your build file: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-opentelemetry + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.quarkus:quarkus-opentelemetry") +---- + +=== Examine the Jakarta REST resource + +Create a `src/main/java/org/acme/opentelemetry/MetricResource.java` file with the following content: + +[[metric-resource-class]] +[source,java] +---- +package org.acme; + +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.jboss.logging.Logger; + +@Path("/hello-metrics") +public class MetricResource { + + private static final Logger LOG = Logger.getLogger(MetricResource.class); + + private final LongCounter counter; + + public MetricResource(Meter meter) { <1> + counter = meter.counterBuilder("hello-metrics") <2> + .setDescription("hello-metrics") + .setUnit("invocations") + .build(); + } + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + counter.add(1); <3> + LOG.info("hello-metrics"); + return "hello-metrics"; + } +} +---- + +Quarkus is not currently producing metrics out of the box. Here we are creating a counter for the number of invocations of the `hello()` method. + +<1> Constructor injection of the `Meter` instance. +<2> Create a `LongCounter` named `hello-metrics` with a description and unit. +<3> Increment the counter by one for each invocation of the `hello()` method. + + +=== Create the configuration + +There are no mandatory configurations for the extension to work. + +If you need to change any of the default property values, here is an example on how to configure the default OTLP gRPC Exporter within the application, using the `src/main/resources/application.properties` file: + +[source,properties] +---- +quarkus.application.name=myservice // <1> +quarkus.otel.metrics.enabled=true // <2> +quarkus.otel.exporter.otlp.metrics.endpoint=http://localhost:4317 // <3> +quarkus.otel.exporter.otlp.metrics.headers=authorization=Bearer my_secret // <4> +---- + +<1> All metrics created from the application will include an OpenTelemetry `Resource` indicating the metrics was created by the `myservice` application. If not set, it will default to the artifact id. +<2> Enable the OpenTelemetry metrics. Must be set at build time. +<3> gRPC endpoint to send the metrics. If not set, it will default to `http://localhost:4317`. +<4> Optional gRPC headers commonly used for authentication. + +To configure the connection using the same properties for all signals, please check the base xref:opentelemetry.adoc#create-the-configuration[configuration section of the OpenTelemetry guide]. + +To disable particular parts of OpenTelemetry, you can set the properties listed in this xref:opentelemetry.adoc#disable-all-or-parts-of-the-opentelemetry-extension[section of the OpenTelemetry guide]. + +== Run the application + +First we need to start a system to visualise the OpenTelemetry data. + +=== See the data + +==== Grafana-OTel-LGTM dev service +You can use the xref:observability-devservices-lgtm.adoc[Grafana-OTel-LGTM] devservice. + +This Dev service includes a Grafana for visualizing data, Loki to store logs, Tempo to store traces and Prometheus to store metrics. Also provides and OTel collector to receive the data. + +==== Logging exporter + +You can output all metrics to the console by setting the exporter to `logging` in the `application.properties` file: +[source, properties] +---- +quarkus.otel.metrics.exporter=logging <1> +quarkus.otel.metric.export.interval=10000ms <2> +---- + +<1> Set the exporter to `logging`. Normally you don't need to set this. The default is `cdi`. +<2> Set the interval to export the metrics. The default is `1m`, which is too long for debugging. + +=== Start the application + +Now we are ready to run our application. If using `application.properties` to configure the tracer: + +include::{includes}/devtools/dev.adoc[] + +or if configuring the OTLP gRPC endpoint via JVM arguments: + +:dev-additional-parameters: -Djvm.args="-Dquarkus.otel.exporter.otlp.endpoint=http://localhost:4317" +include::{includes}/devtools/dev.adoc[] +:!dev-additional-parameters: + +With the OpenTelemetry Collector, the Jaeger system and the application running, you can make a request to the provided endpoint: + +[source,shell] +---- +$ curl http://localhost:8080/hello-metrics +hello-metrics +---- + +When using the logger exporter, metrics will be printed to the console. This is a pretty printed example: +[source,json] +---- +{ + "metric": "ImmutableMetricData", + "resource": { + "Resource": { + "schemaUrl": null, + "attributes": { <1> + "host.name": "myhost", + "service.name": "myservice ", + "service.version": "1.0.0-SNAPSHOT", + "telemetry.sdk.language": "java", + "telemetry.sdk.name": "opentelemetry", + "telemetry.sdk.version": "1.32.0", + "webengine.name": "Quarkus", + "webengine.version": "999-SNAPSHOT" + } + }, + "instrumentationScopeInfo": { + "InstrumentationScopeInfo": { <2> + "name": "io.quarkus.opentelemetry", + "version": null, + "schemaUrl": null, + "attributes": {} + } + }, + "name": "hello-metrics", <3> + "description": "hello-metrics", + "unit": "invocations", + "type": "LONG_SUM", + "data": { + "ImmutableSumData": { + "points": [ + { + "ImmutableLongPointData": { + "startEpochNanos": 1720622136612378000, + "epochNanos": 1720622246618331000, + "attributes": {}, + "value": 3, <4> + "exemplars": [ <5> + { + "ImmutableLongExemplarData": { + "filteredAttributes": {}, + "epochNanos": 1720622239362357000, + "spanContext": { + "ImmutableSpanContext": { + "traceId": "d91951e50b0641552a76889c5356467c", + "spanId": "168af8b7102d0556", + "traceFlags": "01", + "traceState": "ArrayBasedTraceState", + "entries": [], + "remote": false, + "valid": true + }, + "value": 1 + } + } + } + ] + } + } + ], + "monotonic": true, + "aggregationTemporality": "CUMULATIVE" + } + } + } +} +---- +<1> Resource attributes common to all telemetry data. +<2> Instrumentation scope is allways `io.quarkus.opentelemetry` +<3> The name, description and unit of the metric you defined in the constructor of the `MetricResource` class. +<4> The value of the metric. 3 invocations were made until now. +<5> Exemplars additional tracing information about the metric. In this case, the traceId and spanId of one os the request that triggered the metric, since it was last sent. + +Hit `CTRL+C` or type `q` to stop the application. + +== Create your own metrics + +=== OpenTelemetry Metrics vs Micrometer Metrics + +Metrics are single numerical measurements, often have additional data captured with them. This ancillary data is used to group or aggregate metrics for analysis. + +Pretty much like in the xref:telemetry-micrometer.adoc#create-your-own-metrics[Quarkus Micrometer extension], you can create your own metrics using the OpenTelemetry API and the concepts are analogous. + +The OpenTelemetry API provides a `Meter` interface to create metrics instead of a Registry. The `Meter` interface is the entry point for creating metrics. It provides methods to create counters, gauges, and histograms. + +Attributes can be added to metrics to add dimensions, pretty much like tags in Micrometer. + +=== Obtain a reference to the Meter + +Use one of the following methods to obtain a reference to a Meter: + +==== Use CDI Constructor injection + +[source,java] +---- +@Path("/hello-metrics") +public class MetricResource { + + private final Meter meter; + + public MetricResource(Meter meter) { + this.meter = meter; + } +} +---- +Pretty much like in the xref:metric-resource-class[example above]. + +==== Member variable using the `@Inject` annotation + +[source,java] +---- +@Inject +Meter meter; +---- + +=== Counters + +Counters can be used to measure non-negative, increasing values. + +[source, java] +---- +LongCounter counter = meter.counterBuilder("hello-metrics") // <1> + .setDescription("hello-metrics") // optional + .setUnit("invocations") // optional + .build(); + +counter.add(1, // <2> + Attributes.of(AttributeKey.stringKey("attribute.name"), "attribute value")); // optional <3> +---- + +<1> Create a `LongCounter` named `hello-metrics` with a description and unit. +<2> Increment the counter by one. +<3> Add an attribute to the counter. This will create a dimension called `attribute.name` with value `attribute value`. + +IMPORTANT: Each unique combination of metric name and dimension produces a unique time series. Using an unbounded set of dimensional data (many different values like a userId) can lead to a "cardinality explosion", an exponential increase in the creation of new time series. Avoid! + +OpenTelemetry provides many other types of Counters: `LongUpDownCounter`, `DoubleCounter`, `DoubleUpDownCounter` and also Observable, async counters like `ObservableLongCounter`, `ObservableDoubleCounter`, `ObservableLongUpDownCounter` and `ObservableDoubleUpDownCounter`. + +For more details please refer to the https://opentelemetry.io/docs/languages/java/instrumentation/#using-counters[OpenTelemetry Java documentation about Counters]. + +=== Gauges +Observable Gauges should be used to measure non-additive values. A value that can increase or decrease over time, like the speedometer on a car. Gauges can be useful when monitoring the statistics for a cache or collection. + +With this metric you provide a function to be periodically probed by a callback. The value returned by the function is the value of the gauge. + +The default gauge records `Double`values, but if you want to record `Long` values, you can use + +[source, java] +---- +meter.gaugeBuilder("jvm.memory.total") // <1> + .setDescription("Reports JVM memory usage.") + .setUnit("byte") + .ofLongs() // <2> + .buildWithCallback( // <3> + result -> result.record( + Runtime.getRuntime().totalMemory(), // <4> + Attributes.empty())); // optional <5> + +---- +<1> Create a `Gauge` named `jvm.memory.total` with a description and unit. +<2> If you want to record `Long` values you need this builder method because the default gauge records `Double` values. +<3> Build the gauge with a callback. An imperative builder is also available. +<4> Register the function to call to get the value of the gauge. +<5> No added attributes, this time. + +=== Histograms +Histograms are synchronous instruments used to measure a distribution of values over time. +It is intended for statistics such as histograms, summaries, and percentile. +The request duration and response payload size are good uses for a histogram. + + +On this section we have a new class, the `HistogramResource` that will create a `LongHistogram`. + +[source, java] +---- +package org.acme; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.Meter; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.jboss.logging.Logger; + +import java.util.Arrays; + +@Path("/roll-dice") +public class HistogramResource { + + private static final Logger LOG = Logger.getLogger(HistogramResource.class); + + private final LongHistogram rolls; + + public HistogramResource(Meter meter) { + rolls = meter.histogramBuilder("hello.roll.dice") // <1> + .ofLongs() // <2> + .setDescription("A distribution of the value of the rolls.") + .setExplicitBucketBoundariesAdvice(Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L)) // <3> + .setUnit("points") + .build(); + } + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String helloGauge() { + var roll = roll(); + rolls.record(roll, // <4> + Attributes.of(AttributeKey.stringKey("attribute.name"), "value")); // <5> + LOG.info("roll-dice: " + roll); + return "" + roll; + } + + public long roll() { + return (long) (Math.random() * 6) + 1; + } +} +---- +<1> Create a `LongHistogram` named `hello.roll.dice` with a description and unit. +<2> If you want to record `Long` values you need this builder method because the default histogram records `Double` values. +<3> Set the explicit bucket boundaries for the histogram. The boundaries are inclusive. +<4> Record the value of the roll. +<5> Add an attribute to the histogram. This will create a dimension called `attribute.name` with value `value`. + +IMPORTANT: Beware of cardinality explosion. + +We can invoke the endpoint with a curl command. +[source,shell] +---- +$ curl http://localhost:8080/roll-dice +2 +---- + +If we execute 4 consecutive requests, with results *2,2,3 and 4* this will produce the following output. The `Resource` and `InstrumentationScopeInfo` data are ignored for brevity. +[source,json] +---- +//... +name=hello.roll.dice, +description=A distribution of the value of the rolls., // <1> +unit=points, +type=HISTOGRAM, +data=ImmutableHistogramData{ + aggregationTemporality=CUMULATIVE, // <2> + points=[ + ImmutableHistogramPointData{ + getStartEpochNanos=1720632058272341000, + getEpochNanos=1720632068279567000, + getAttributes={attribute.name="value"}, // <3> + getSum=11.0, // <4> + getCount=4, // <5> + hasMin=true, + getMin=2.0, // <6> + hasMax=true, + getMax=4.0, // <7> + getBoundaries=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0], // <8> + getCounts=[0, 2, 1, 1, 0, 0, 0, 0], // <9> + getExemplars=[ // <10> + ImmutableDoubleExemplarData{ + filteredAttributes={}, + epochNanos=1720632063049392000, + spanContext=ImmutableSpanContext{ + traceId=a22b43a600682ca7320516081eca998b, + spanId=645aa49f219181d0, + traceFlags=01, + traceState=ArrayBasedTraceState{entries=[]}, + remote=false, + valid=true + }, + value=2.0 // <11> + }, + //... exemplars for values 3 and 4 omitted for brevity + ] + } + ] +} +---- +<1> The name, description and unit of the metric you defined in the constructor of the `HistogramResource` class. +<2> The aggregation temporality of the histogram. +<3> The attribute added to the histogram when the values were recorded. +<4> The sum of the values recorded. +<5> The number of values recorded. +<6> The minimum value recorded. +<7> The maximum value recorded. +<8> The explicit bucket boundaries for the histogram. +<9> The number of values recorded in each bucket. +<10> The list of exemplars with tracing data for the values recorded. We only show 1 of 3 exemplars for brevity. +<11> One of the 2 calls made with the value 2. + +=== Differences with the Micrometer API + +- Timers and Distribution Summaries are not available in the OpenTelemetry API. Instead, use Histograms. +- The OpenTelemetry API does not define annotations for metrics like Micrometer's `@Counted`, `@Timed` or `@MeterTag` You need to manually create the metrics. +- OpenTelemetry uses their own https://opentelemetry.io/docs/specs/semconv/[Semantic Conventions] to name metrics and attributes. + +=== Resource +See the main xref:opentelemetry.adoc#resource[OpenTelemetry Guide resources] section. + +== Additional instrumentation + +Automatic metrics are not yet provided by the Quarkus OpenTelemetry extension. We plan to bridge the existing Quarkus Micrometer extension metrics to OpenTelemetry in the future. + +== Exporters +See the main xref:opentelemetry.adoc#exporters[OpenTelemetry Guide exporters] section. + +[[configuration-reference]] +== OpenTelemetry Configuration Reference + +See the main xref:opentelemetry.adoc#configuration-reference[OpenTelemetry Guide configuration] reference. \ No newline at end of file diff --git a/docs/src/main/asciidoc/opentelemetry-tracing.adoc b/docs/src/main/asciidoc/opentelemetry-tracing.adoc new file mode 100644 index 0000000000000..088dbbf14a020 --- /dev/null +++ b/docs/src/main/asciidoc/opentelemetry-tracing.adoc @@ -0,0 +1,622 @@ +//// +This guide is maintained in the main Quarkus repository +and pull requests should be submitted there: +https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc +//// += Using OpenTelemetry Tracing +include::_attributes.adoc[] +:categories: observability +:summary: This guide explains how your Quarkus application can utilize OpenTelemetry Tracing to provide distributed tracing for interactive web applications. +:topics: observability,opentelemetry,tracing +:extensions: io.quarkus:quarkus-opentelemetry + +This guide explains how your Quarkus application can utilize https://opentelemetry.io/[OpenTelemetry] (OTel) to provide +distributed tracing for interactive web applications. + +[NOTE] +==== +- The xref:opentelemetry.adoc[OpenTelemetry Guide] is available with signal independent information about the OpenTelemetry extension. +- If you search more information about OpenTelemetry Metrics, please refer to the xref:opentelemetry-metrics.adoc[OpenTelemetry Metrics Guide]. +==== + +== Prerequisites + +:prerequisites-docker-compose: +include::{includes}/prerequisites.adoc[] + +== Architecture + +In this guide, we create a straightforward REST application to demonstrate distributed tracing. + +== Solution + +We recommend that you follow the instructions in the next sections and create the application step by step. +However, you can skip right to the completed example. + +Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. + +The solution is located in the `opentelemetry-quickstart` link:{quickstarts-tree-url}/opentelemetry-quickstart[directory]. + +== Creating the Maven project + +First, we need a new project. Create a new project with the following command: + +:create-app-artifact-id: opentelemetry-quickstart +:create-app-extensions: rest,quarkus-opentelemetry +include::{includes}/devtools/create-app.adoc[] + +This command generates the Maven project and imports the `quarkus-opentelemetry` extension, +which includes the default OpenTelemetry support, +and a gRPC span exporter for https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md[OTLP]. + +If you already have your Quarkus project configured, you can add the `quarkus-opentelemetry` extension +to your project by running the following command in your project base directory: + +:add-extension-extensions: opentelemetry +include::{includes}/devtools/extension-add.adoc[] + +This will add the following to your build file: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-opentelemetry + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.quarkus:quarkus-opentelemetry") +---- + +=== Examine the Jakarta REST resource + +Create a `src/main/java/org/acme/opentelemetry/TracedResource.java` file with the following content: + +[source,java] +---- +package org.acme.opentelemetry; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.jboss.logging.Logger; + +@Path("/hello") +public class TracedResource { + + private static final Logger LOG = Logger.getLogger(TracedResource.class); + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + LOG.info("hello"); + return "hello"; + } +} +---- + +Notice that there is no tracing specific code included in the application. By default, requests sent to this +endpoint will be traced without any required code changes. + +=== Create the configuration + + +:opentelemetry-config: +include::{includes}/opentelemetry-config.adoc[] + +== Run the application + +First we need to start a system to visualise the OpenTelemetry data. +We have 2 options: + +* Start an all-in-one Grafana OTel LGTM system for traces and metrics. +* Jaeger system just for traces. + +=== Grafana OTel LGTM option + +* Take a look at: xref:observability-devservices-lgtm.adoc[Getting Started with Grafana-OTel-LGTM]. + +This features a Quarkus Dev service including a Grafana for visualizing data, Loki to store logs, Tempo to store traces and Prometheus to store metrics. Also provides and OTel collector to receive the data. + +=== Jaeger to see traces option + +Configure and start the https://opentelemetry.io/docs/collector/[OpenTelemetry Collector] to receive, process and export telemetry data to https://www.jaegertracing.io/[Jaeger] that will display the captured traces. + +[NOTE] +==== +Jaeger-all-in-one includes the Jaeger agent, an OTel collector, and the query service/UI. +You do not need to install a separated collector. You can directly send the trace data to Jaeger (after enabling OTLP receivers there, see e.g. this +https://medium.com/jaegertracing/introducing-native-support-for-opentelemetry-in-jaeger-eb661be8183c[blog entry] for details). +==== + +Start the OpenTelemetry Collector and Jaeger system via the following `docker-compose.yml` file that you can launch via `docker-compose up -d`: + +[source,yaml,subs="attributes"] +---- +version: "2" +services: + + # Jaeger + jaeger-all-in-one: + image: jaegertracing/all-in-one:latest + ports: + - "16686:16686" # Jaeger UI + - "14268:14268" # Receive legacy OpenTracing traces, optional + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver, not yet used by Quarkus, optional + - "14250:14250" # Receive from external otel-collector, optional + environment: + - COLLECTOR_OTLP_ENABLED=true +---- +You should remove the optional ports you don't need them. + +=== Start the application + +Now we are ready to run our application. If using `application.properties` to configure the tracer: + +include::{includes}/devtools/dev.adoc[] + +or if configuring the OTLP gRPC endpoint via JVM arguments: + +:dev-additional-parameters: -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317" +include::{includes}/devtools/dev.adoc[] +:!dev-additional-parameters: + +With the OpenTelemetry Collector, the Jaeger system and the application running, you can make a request to the provided endpoint: + +[source,shell] +---- +$ curl http://localhost:8080/hello +hello +---- + +When the first request has been submitted, you will be able to see the tracing information in the logs: + +[source] +---- +10:49:02 INFO traceId=, parentId=, spanId=, sampled= [io.quarkus] (main) Installed features: [cdi, opentelemetry, resteasy-client, resteasy, smallrye-context-propagation, vertx] +10:49:03 INFO traceId=17ceb8429b9f25b0b879fa1503259456, parentId=3125c8bee75b7ad6, spanId=58ce77c86dd23457, sampled=true [or.ac.op.TracedResource] (executor-thread-1) hello +10:49:03 INFO traceId=ad23acd6d9a4ed3d1de07866a52fa2df, parentId=, spanId=df13f5b45cf4d1e2, sampled=true [or.ac.op.TracedResource] (executor-thread-0) hello +---- + + +Then visit the http://localhost:16686[Jaeger UI] to see the tracing information. + +Hit `CTRL+C` or type `q` to stop the application. + +=== JDBC + +The https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/jdbc/library[JDBC instrumentation] will add a span for each JDBC queries done by your application, to enable it, add the following dependency to your build file: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.opentelemetry.instrumentation + opentelemetry-jdbc + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.opentelemetry.instrumentation:opentelemetry-jdbc") +---- + +As it uses a dedicated JDBC datasource wrapper, you must enable telemetry for your datasource: + +[source, properties] +---- +# enable tracing +quarkus.datasource.jdbc.telemetry=true + +# configure datasource +quarkus.datasource.db-kind=postgresql +quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydatabase +---- + +== Additional configuration +Some use cases will require custom configuration of OpenTelemetry. +These sections will outline what is necessary to properly configure it. + +=== ID Generator +The OpenTelemetry extension will use by default a random https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#id-generators[ID Generator] +when creating the trace and span identifier. + +Some vendor-specific protocols need a custom ID Generator, you can override the default one by creating a producer. +The OpenTelemetry extension will detect the `IdGenerator` CDI bean and will use it when configuring the tracer producer. + +[source,java] +---- +@Singleton +public class CustomConfiguration { + + /** Creates a custom IdGenerator for OpenTelemetry */ + @Produces + @Singleton + public IdGenerator idGenerator() { + return AwsXrayIdGenerator.getInstance(); + } +} +---- + +=== Propagators +OpenTelemetry propagates cross-cutting concerns through https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md[propagators] that will share an underlying `Context` for storing state and accessing +data across the lifespan of a distributed transaction. + +By default, the OpenTelemetry extension enables the https://www.w3.org/TR/trace-context/[W3C Trace Context] and the https://www.w3.org/TR/baggage/[W3C Baggage] +propagators, you can however choose any of the supported OpenTelemetry propagators by setting the `propagators` config that is described in the <>. + +==== Additional Propagators + +* The `b3`, `b3multi`, `jaeger` and `ottrace` propagators will need the https://github.com/open-telemetry/opentelemetry-java/tree/main/extensions/trace-propagators[trace-propagators] +extension to be added as a dependency to your project. + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.opentelemetry + opentelemetry-extension-trace-propagators + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.opentelemetry:opentelemetry-extension-trace-propagators") +---- + +* The `xray` propagator will need the https://github.com/open-telemetry/opentelemetry-java-contrib/tree/main/aws-xray-propagator[aws] +extension to be added as a dependency to your project. + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.opentelemetry.contrib + opentelemetry-aws-xray-propagator + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") +---- + +==== Customise Propagator + +To customise the propagation header you can implement the `TextMapPropagatorCustomizer` interface. This can be used, as an example, to restrict propagation of OpenTelemetry trace headers and prevent potentially sensitive data to be sent to third party systems. + +```java +/** + * /** + * Meant to be implemented by a CDI bean that provides arbitrary customization for the TextMapPropagator + * that are to be registered with OpenTelemetry + */ +public interface TextMapPropagatorCustomizer { + + TextMapPropagator customize(Context context); + + interface Context { + TextMapPropagator propagator(); + + ConfigProperties otelConfigProperties(); + } +} +``` + +=== Resource + +See the main xref:opentelemetry.adoc#resource[OpenTelemetry Guide resources] section. + +==== End User attributes + +When enabled, Quarkus adds OpenTelemetry End User attributes as Span attributes. +Before you enable this feature, verify that Quarkus Security extension is present and configured. +More information about the Quarkus Security can be found in the xref:security-overview.adoc[Quarkus Security overview]. + +The attributes are only added when authentication has already happened on a best-efforts basis. +Whether the End User attributes are added as Span attributes depends on authentication and authorization configuration of your Quarkus application. +If you create custom Spans prior to the authentication, Quarkus cannot add the End User attributes to them. +Quarkus is only able to add the attributes to the Span that is current after the authentication has been finished. +Another important consideration regarding custom Spans is active CDI request context that is used to propagate Quarkus `SecurityIdentity`. +In principle, Quarkus is able to add the End User attributes when the CDI request context has been activated for you before the custom Spans are created. + +[source,application.properties] +---- +quarkus.otel.traces.eusp.enabled=true <1> +quarkus.http.auth.proactive=true <2> +---- +<1> Enable the End User Attributes feature so that the `SecurityIdentity` principal and roles are added as Span attributes. +The End User attributes are personally identifiable information, therefore make sure you want to export them before you enable this feature. +<2> Optionally enable proactive authentication. +The best possible results are achieved when proactive authentication is enabled because the authentication happens sooner. +A good way to determine whether proactive authentication should be enabled in your Quarkus application is to read the Quarkus xref:security-proactive-authentication.adoc[Proactive authentication] guide. + +IMPORTANT: This feature is not supported when a custom xref:security-customization.adoc#jaxrs-security-context[Jakarta REST SecurityContexts] is used. + +[[sampler]] +=== Sampler +A https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling[sampler] decides whether a trace should be discarded or forwarded, effectively managing noise and reducing overhead by limiting the number of collected traces sent to the collector. + +Quarkus comes equipped with a https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#built-in-samplers[built-in sampler], and you also have the option to create your custom sampler. + +To use the built-in sampler, you can configure it by setting the desired sampler parameters as detailed in the <>. As an example, you can configure the sampler to retain 50% of the traces: +[source,application.properties] +---- +# build time property only: +quarkus.otel.traces.sampler=traceidratio +# Runtime property: +quarkus.otel.traces.sampler.arg=0.5 +---- +[TIP] +==== + +An interesting use case for the sampler is to activate and deactivate tracing export at runtime, acording to this example: +[source,application.properties] +---- +# build time property only: +quarkus.otel.traces.sampler=traceidratio +# On (default). All traces are exported: +quarkus.otel.traces.sampler.arg=1.0 +# Off. No traces are exported: +quarkus.otel.traces.sampler.arg=0.0 +---- +==== + +[NOTE] +==== +Quarkus 3.0 introduced breaking changes on the configuration. + +Sampler related property names and values change to comply with the latest Java OpenTelemetry SDK. During a transition period it will be possible to set the new configuration values in the old property because we are mapping `quarkus.opentelemetry.tracer.sampler` -> `quarkus.otel.traces.sampler`. + +If the sampler is parent based, there is no need to set, the now dropped property, `quarkus.opentelemetry.tracer.sampler.parent-based`. + +The values you need to set on `quarkus.opentelemetry.tracer.sampler` are now: + +|=== +|Old Sampler config value |New Sampler config value|New Sampler config value (Parent based) + +|`on` +|`always_on` +|`parentbased_always_on` + +|`off` +|`always_off` +|`parentbased_always_off` + +|`ratio` +|`traceidratio` +|`parentbased_traceidratio` +|=== +==== + +If you need to use a custom sampler there are now 2 different ways: + +==== Sampler CDI Producer + +You can create a sampler CDI producer. The Quarkus OpenTelemetry extension will detect the `Sampler` CDI bean and will use it when configuring the Tracer. + +[source,java] +---- +@Singleton +public class CustomConfiguration { + + /** Creates a custom sampler for OpenTelemetry */ + @Produces + @Singleton + public Sampler sampler() { + return JaegerRemoteSampler.builder() + .setServiceName("my-service") + .build(); + } +} +---- + +==== OTel Sampler SPI + +This will use the SPI hooks available with the OTel Autoconfiguration. +You can create a simple Sampler class: +[source,java] +---- +public class CustomSPISampler implements Sampler { + @Override + public SamplingResult shouldSample(Context context, + String s, + String s1, + SpanKind spanKind, + Attributes attributes, + List list) { + // Do some sampling here + return Sampler.alwaysOn().shouldSample(context, s, s1, spanKind, attributes, list); + } + + @Override + public String getDescription() { + return "custom-spi-sampler-description"; + } +} + +---- +Then a Sampler Provider: +[source,java] +---- +public class CustomSPISamplerProvider implements ConfigurableSamplerProvider { + @Override + public Sampler createSampler(ConfigProperties configProperties) { + return new CustomSPISampler(); + } + + @Override + public String getName() { + return "custom-spi-sampler"; + } +} +---- +Write the SPI loader text file at `resources/META-INF/services` with name `io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider` containing the full qualified name of the `CustomSPISamplerProvider` class. + +Then activate on the configuration: +[source,properties] +---- +quarkus.otel.traces.sampler=custom-spi-sampler +---- + +As you can see, CDI is much simpler to work with. + +== Additional instrumentation + +Some Quarkus extensions will require additional code to ensure traces are propagated to subsequent execution. +These sections will outline what is necessary to propagate traces across process boundaries. + +The instrumentation documented in this section has been tested with Quarkus and works in both standard and native mode. + +=== CDI + +Annotating a method in any CDI aware bean with the `io.opentelemetry.instrumentation.annotations.WithSpan` +annotation will create a new Span and establish any required relationships with the current Trace context. + +Annotating a method in any CDI aware bean with the `io.opentelemetry.instrumentation.annotations.AddingSpanAttributes` will not create a new span but will add annotated method parameters to attributes in the current span. + +If a method is annotated by mistake with `@AddingSpanAttributes` and `@WithSpan` annotations, the `@WithSpan` annotation will take precedence. + +Method parameters can be annotated with the `io.opentelemetry.instrumentation.annotations.SpanAttribute` annotation to +indicate which method parameters should be part of the span. The parameter name can be customized as well. + +Example: +[source,java] +---- +@ApplicationScoped +class SpanBean { + @WithSpan + void span() { + + } + + @WithSpan("name") + void spanName() { + + } + + @WithSpan(kind = SERVER) + void spanKind() { + + } + + @WithSpan + void spanArgs(@SpanAttribute(value = "arg") String arg) { + + } + + @AddingSpanAttributes + void addArgumentToExistingSpan(@SpanAttribute(value = "arg") String arg) { + + } +} +---- + +=== Available OpenTelemetry CDI injections + +As per MicroProfile Telemetry Tracing specification, Quarkus supports the CDI injections of the +following classes: + +* `io.opentelemetry.api.OpenTelemetry` +* `io.opentelemetry.api.trace.Tracer` +* `io.opentelemetry.api.trace.Span` +* `io.opentelemetry.api.baggage.Baggage` + +You can inject these classes in any CDI enabled bean. For instance, the `Tracer` is particularly useful to start custom spans: + +[source,java] +---- +@Inject +Tracer tracer; + +... + +public void tracedWork() { + Span span = tracer.spanBuilder("My custom span") + .setAttribute("attr", "attr.value") + .setParent(Context.current().with(Span.current())) + .setSpanKind(SpanKind.INTERNAL) + .startSpan(); + + // traced work + + span.end(); +} +---- + +=== Quarkus Messaging - Kafka + +When using the Quarkus Messaging extension for Kafka, +we are able to propagate the span into the Kafka Record with: + +[source,java] +---- +TracingMetadata tm = TracingMetadata.withPrevious(Context.current()); +Message out = Message.of(...).withMetadata(tm); +---- + +The above creates a `TracingMetadata` object we can add to the `Message` being produced, +which retrieves the OpenTelemetry `Context` to extract the current span for propagation. + +=== Quarkus Security Events + +Quarkus supports exporting of the xref:security-customization.adoc#observe-security-events[Security events] as OpenTelemetry Span events. + +[source,application.properties] +---- +quarkus.otel.security-events.enabled=true <1> +---- +<1> Export Quarkus Security events as OpenTelemetry Span events. + +== Exporters + +See the main xref:opentelemetry.adoc#exporters[OpenTelemetry Guide exporters] section. + +[[quarkus-extensions-using-opentelemetry]] +== Quarkus core extensions instrumented with OpenTelemetry tracing + +* https://quarkus.io/extensions/io.quarkus/quarkus-agroal[`quarkus-agroal`] +* https://quarkus.io/guides/grpc-getting-started[`quarkus-grpc`] +* https://quarkus.io/guides/redis[`quarkus-redis-client`] +* https://quarkus.io/extensions/io.quarkus/quarkus-rest-client-jaxrs[`quarkus-rest-client-jaxrs`] +* https://quarkus.io/guides/rest[`quarkus-rest`] +* https://quarkus.io/guides/resteasy[`quarkus-resteasy-jackson`] +* https://quarkus.io/guides/resteasy-client[`quarkus-resteasy-client`] +* https://quarkus.io/guides/scheduler[`quarkus-scheduler`] +* https://quarkus.io/guides/smallrye-graphql[`quarkus-smallrye-graphql`] +* https://quarkus.io/extensions/io.quarkus/quarkus-mongodb-client[`quarkus-mongodb-client`] +* https://quarkus.io/extensions/io.quarkus/quarkus-messaging[`quarkus-messaging`] +** AMQP 1.0 +** RabbitMQ +** Kafka +** Pulsar +* https://quarkus.io/guides/vertx[`quarkus-vertx`] (http requests) + + +=== Disable parts of the automatic tracing + +Automatic tracing instrumentation parts can be disabled by setting `quarkus.otel.instrument.*` properties to `false`. + +Examples: +[source,properties] +---- +quarkus.otel.instrument.grpc=false +quarkus.otel.instrument.messaging=false +quarkus.otel.instrument.resteasy-client=false +quarkus.otel.instrument.rest=false +quarkus.otel.instrument.resteasy=false +---- + +[[configuration-reference]] +== OpenTelemetry Configuration Reference + +See the main xref:opentelemetry.adoc#configuration-reference[OpenTelemetry Guide configuration] reference. diff --git a/docs/src/main/asciidoc/opentelemetry.adoc b/docs/src/main/asciidoc/opentelemetry.adoc index bc5cb1e2c72fa..f3a79b928b7dd 100644 --- a/docs/src/main/asciidoc/opentelemetry.adoc +++ b/docs/src/main/asciidoc/opentelemetry.adoc @@ -5,57 +5,68 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// = Using OpenTelemetry include::_attributes.adoc[] +:diataxis-type: reference :categories: observability -:summary: This guide explains how your Quarkus application can utilize OpenTelemetry to provide distributed tracing for interactive web applications. +:summary: This guide explains how your Quarkus application can utilize OpenTelemetry to provide observability for interactive web applications. :topics: observability,opentelemetry :extensions: io.quarkus:quarkus-opentelemetry This guide explains how your Quarkus application can utilize https://opentelemetry.io/[OpenTelemetry] (OTel) to provide -distributed tracing for interactive web applications. +Observability for interactive web applications. + +On these page we show the signal independent features of the extension. [NOTE] ==== -- OpenTelemetry Metrics and Logging are not yet supported. -- Quarkus now supports the OpenTelemetry Autoconfiguration for Traces. The configurations match what you can see at -https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md[OpenTelemetry SDK Autoconfigure] -with the `quarkus.*` prefix. -- Extensions and the libraries they provide, are directly instrumented in Quarkus. The *use of the https://opentelemetry.io/docs/instrumentation/java/automatic/[OpenTelemetry Agent] is not needed nor recommended* due to context propagation issues between imperative and reactive libraries. -- If you come from the legacy OpenTracing extension, there is a xref:telemetry-opentracing-to-otel-tutorial.adoc[guide to help with the migration]. -- Current Semantic Conventions for HTTP will soon change and the current conventions are deprecated for removal soon. Please move to the new conventions by seetinh the new property `quarkus.otel.semconv-stability.opt-in` to `http`, for the new conventions or `http/dup` to produce duplicated old and new conventions. Please check the <> for more details and full set of changes at the https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/migration-guide.md#summary-of-changes[HTTP semantic convention stability migration guide]. +- The old OpenTelemetry guide has been split into this generic guide, the xref:opentelemetry-tracing.adoc[OpenTelemetry Tracing Guide] and the new xref:opentelemetry-metrics.adoc[OpenTelemetry Metrics Guide] has been created. +- OpenTelemetry Logging is not yet supported. +- The use of *the https://opentelemetry.io/docs/instrumentation/java/automatic/[OpenTelemetry Agent] is not needed nor recommended*. Quarkus Extensions and the libraries they provide, are directly instrumented. That agent doesn't work with native mode. ==== -== Prerequisites +== Introduction +https://opentelemetry.io/docs/what-is-opentelemetry/[OpenTelemetry] is an Observability framework and toolkit designed to create and manage telemetry data such as traces, metrics, and logs. Crucially, OpenTelemetry is vendor- and tool-agnostic. + +Quarkus provides manual and automatic instrumentation for tracing and manual instrumentation capabilities for metrics. + +This will allow Quarkus based applications to be observable by tools and services supporting OpenTelemetry. + +[NOTE] +==== +Automatic metrics instrumentation in Quarkus is done by the xref:telemetry-micrometer.adoc[Quarkus Micrometer extension]. We plan to provide, in the future, a bridge for those metrics to be available in OpenTelemetry as well. +==== -:prerequisites-docker-compose: -include::{includes}/prerequisites.adoc[] +Quarkus supports the OpenTelemetry Autoconfiguration. The configurations match what you can see at +https://opentelemetry.io/docs/languages/java/configuration/[OpenTelemetry SDK Autoconfigure] +with the `quarkus.*` prefix. -== Architecture +This guide provides a crosscutting explanation of the OpenTelemetry extension and how to use it. If you need details about any particular signal (tracing or metrics), please refer to the signal specific guide. -In this guide, we create a straightforward REST application to demonstrate distributed tracing. +With the introduction of OpenTelemetry Metrics, the original, single page guide had to be split according to signal types, as follows: -== Solution +=== xref:opentelemetry-tracing.adoc[OpenTelemetry Tracing Guide] -We recommend that you follow the instructions in the next sections and create the application step by step. -However, you can skip right to the completed example. +The tracing functionality is supported and *on* by default. -Clone the Git repository: `git clone {quickstarts-clone-url}`, or download an {quickstarts-archive-url}[archive]. +=== xref:opentelemetry-metrics.adoc[OpenTelemetry Metrics Guide] -The solution is located in the `opentelemetry-quickstart` link:{quickstarts-tree-url}/opentelemetry-quickstart[directory]. +==== Enable Metrics +The metrics functionality is experimental and *off* by default. You will need to activate it by setting: -== Creating the Maven project +[source,properties] +---- +quarkus.otel.metrics.enabled=true +---- +At build time on your `application.properties` file. -First, we need a new project. Create a new project with the following command: +==== Manual instrumentation only +For now only manual instrumentation is supported. You can use the OpenTelemetry API to create and record metrics but Quarkus is not yet providing automatic instrumentation for metrics. -:create-app-artifact-id: opentelemetry-quickstart -:create-app-extensions: rest,quarkus-opentelemetry -include::{includes}/devtools/create-app.adoc[] +In the future, we plan to bridge current Micrometer metrics to OpenTelemetry and maintain compatibility when possible. -This command generates the Maven project and imports the `quarkus-opentelemetry` extension, -which includes the default OpenTelemetry support, -and a gRPC span exporter for https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md[OTLP]. +== Using the extension -If you already have your Quarkus project configured, you can add the `quarkus-opentelemetry` extension -to your project by running the following command in your project base directory: +If you already have your Quarkus project, you can add the `quarkus-opentelemetry` extension +to it by running the following command in your project base directory: :add-extension-extensions: opentelemetry include::{includes}/devtools/extension-add.adoc[] @@ -77,307 +88,119 @@ This will add the following to your build file: implementation("io.quarkus:quarkus-opentelemetry") ---- -=== Examine the Jakarta REST resource - -Create a `src/main/java/org/acme/opentelemetry/TracedResource.java` file with the following content: - -[source,java] ----- -package org.acme.opentelemetry; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import org.jboss.logging.Logger; - -@Path("/hello") -public class TracedResource { - - private static final Logger LOG = Logger.getLogger(TracedResource.class); - - @GET - @Produces(MediaType.TEXT_PLAIN) - public String hello() { - LOG.info("hello"); - return "hello"; - } -} ----- - -Notice that there is no tracing specific code included in the application. By default, requests sent to this -endpoint will be traced without any required code changes. === Create the configuration -There are no mandatory configurations for the extension to work. - -If you need to change any of the default property values, here is an example on how to configure the default OTLP gRPC Exporter within the application, using the `src/main/resources/application.properties` file: - -[source,properties] ----- -quarkus.application.name=myservice // <1> -quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317 // <2> -quarkus.otel.exporter.otlp.traces.headers=authorization=Bearer my_secret // <3> -quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} [%c{2.}] (%t) %s%e%n // <4> - -# Alternative to the console log -quarkus.http.access-log.pattern="...traceId=%{X,traceId} spanId=%{X,spanId}" // <5> ----- - -<1> All spans created from the application will include an OpenTelemetry `Resource` indicating the span was created by the `myservice` application. If not set, it will default to the artifact id. -<2> gRPC endpoint to send spans. If not set, it will default to `http://localhost:4317`. -<3> Optional gRPC headers commonly used for authentication -<4> Add tracing information into log messages. -<5> You can also only put the trace info into the access log. In this case you must omit the info in the console log format. - -[NOTE] -==== -All configurations have been updated from `quarkus.opentelemetry.\*` -> `quarkus.otel.*` - -The legacy configurations are now deprecated but will still work during a transition period. -==== +:opentelemetry-config: +include::{includes}/opentelemetry-config.adoc[] === Disable all or parts of the OpenTelemetry extension -Once you add the dependency, the extension will be enabled by default but there are a few ways to disable the OpenTelemetry extension globally or partially. +Once you add the dependency, the extension will generate tracing data by default. To enable metrics or disable the OpenTelemetry extension globally or partially these are the properties to use (they are extracted from the config reference bellow): |=== -|Property name |Default value |Description +|Affected Signal | Property name |Default value |Description +| All |`quarkus.otel.enabled` |true |If false, disable the OpenTelemetry usage at *build* time. +| All |`quarkus.otel.sdk.disabled` |false |Comes from the OpenTelemetry autoconfiguration. If true, will disable the OpenTelemetry SDK usage at *runtime*. -|`quarkus.otel.traces.enabled` -|true -|If false, disable the OpenTelemetry tracing usage at *build* time. - +| All output |`quarkus.otel.exporter.otlp.enabled` |true -|If false will disable the default OTLP exporter at *build* time. -|=== - -If you need to enable or disable the exporter at runtime, you can use the <> because it has the ability to filter out all the spans if needed. - - -== Run the application - -First we need to start a system to visualise the OpenTelemetry data. -We have 2 options: - -* Start an all-in-one Grafana OTel LGTM system for traces and metrics. -* Jaeger system just for traces. - -=== Grafana OTel LGTM option - -* Take a look at: xref:observability-devservices-lgtm.adoc[Getting Started with Grafana-OTel-LGTM]. - -This features a Quarkus Dev service including a Grafana for visualizing data, Loki to store logs, Tempo to store traces and Prometheus to store metrics. Also provides and OTel collector to receive the data. - -=== Jaeger to see traces option - -Configure and start the https://opentelemetry.io/docs/collector/[OpenTelemetry Collector] to receive, process and export telemetry data to https://www.jaegertracing.io/[Jaeger] that will display the captured traces. - -[NOTE] -==== -Jaeger-all-in-one includes the Jaeger agent, an OTel collector, and the query service/UI. -You do not need to install a separated collector. You can directly send the trace data to Jaeger (after enabling OTLP receivers there, see e.g. this -https://medium.com/jaegertracing/introducing-native-support-for-opentelemetry-in-jaeger-eb661be8183c[blog entry] for details). -==== - -Start the OpenTelemetry Collector and Jaeger system via the following `docker-compose.yml` file that you can launch via `docker-compose up -d`: - -[source,yaml,subs="attributes"] ----- -version: "2" -services: - - # Jaeger - jaeger-all-in-one: - image: jaegertracing/all-in-one:latest - ports: - - "16686:16686" # Jaeger UI - - "14268:14268" # Receive legacy OpenTracing traces, optional - - "4317:4317" # OTLP gRPC receiver - - "4318:4318" # OTLP HTTP receiver, not yet used by Quarkus, optional - - "14250:14250" # Receive from external otel-collector, optional - environment: - - COLLECTOR_OTLP_ENABLED=true ----- -You should remove the optional ports you don't need them. - -=== Start the application - -Now we are ready to run our application. If using `application.properties` to configure the tracer: - -include::{includes}/devtools/dev.adoc[] +|Deprecated for removal. -or if configuring the OTLP gRPC endpoint via JVM arguments: +If false will disable the default OTLP exporter at *build* time. -:dev-additional-parameters: -Djvm.args="-Dquarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317" -include::{includes}/devtools/dev.adoc[] -:!dev-additional-parameters: - -With the OpenTelemetry Collector, the Jaeger system and the application running, you can make a request to the provided endpoint: - -[source,shell] ----- -$ curl http://localhost:8080/hello -hello ----- - -When the first request has been submitted, you will be able to see the tracing information in the logs: - -[source] ----- -10:49:02 INFO traceId=, parentId=, spanId=, sampled= [io.quarkus] (main) Installed features: [cdi, opentelemetry, resteasy-client, resteasy, smallrye-context-propagation, vertx] -10:49:03 INFO traceId=17ceb8429b9f25b0b879fa1503259456, parentId=3125c8bee75b7ad6, spanId=58ce77c86dd23457, sampled=true [or.ac.op.TracedResource] (executor-thread-1) hello -10:49:03 INFO traceId=ad23acd6d9a4ed3d1de07866a52fa2df, parentId=, spanId=df13f5b45cf4d1e2, sampled=true [or.ac.op.TracedResource] (executor-thread-0) hello ----- - - -Then visit the http://localhost:16686[Jaeger UI] to see the tracing information. - -Hit `CTRL+C` or type `q` to stop the application. - -=== JDBC - -The https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/jdbc/library[JDBC instrumentation] will add a span for each JDBC queries done by your application, to enable it, add the following dependency to your build file: - -[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] -.pom.xml ----- - - io.opentelemetry.instrumentation - opentelemetry-jdbc - ----- +| Traces +|`quarkus.otel.traces.enabled` +|true +|If false, disable the OpenTelemetry tracing usage at *build* time. -[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] -.build.gradle ----- -implementation("io.opentelemetry.instrumentation:opentelemetry-jdbc") ----- +| Traces output +|`quarkus.otel.traces.exporter` +|cdi +|List of exporters to be used for tracing, separated by commas. Has one of the values from _ExporterType_: `otlp`, `cdi`, `none`. This is a *build* time property and setting it to `none` will disable tracing data output. -As it uses a dedicated JDBC datasource wrapper, you must enable telemetry for your datasource: +| Metrics +|`quarkus.otel.metrics.enabled` +|false +|Metrics are disabled by default at *build* time because they are experimental. -[source, properties] ----- -# enable tracing -quarkus.datasource.jdbc.telemetry=true +| Metrics output +|`quarkus.otel.metrics.exporter` +|cdi +|List of exporters to be used for metrics, separated by commas. Has one of the values from _ExporterType_: `otlp`, `cdi`, `none`. This is a *build* time property and setting it to `none` will disable metrics data output. +|=== -# configure datasource -quarkus.datasource.db-kind=postgresql -quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/mydatabase ----- +If you need to enable or disable the exporter at runtime, you can use the xref:opentelemetry-tracing.adoc#sampler[sampler] because it has the ability to filter out all the spans if needed. -== Additional configuration -Some use cases will require custom configuration of OpenTelemetry. -These sections will outline what is necessary to properly configure it. +Particular instrumentation components can be disabled in tracing, like ignore client requests but keep server ones. For more details, please check the xref:opentelemetry-tracing.adoc[OpenTelemetry Tracing Guide]. -=== ID Generator -The OpenTelemetry extension will use by default a random https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#id-generators[ID Generator] -when creating the trace and span identifier. +=== Resource +A https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#resources[resource] is a representation +of the entity that is producing telemetry, it adds attributes to the exported trace or metric to characterize who is producing the telemetry. Quarkus follows the https://opentelemetry.io/docs/languages/java/configuration/#resources[resources auto-configuration] specified by the Java OpenTelemetry SDK. -Some vendor-specific protocols need a custom ID Generator, you can override the default one by creating a producer. -The OpenTelemetry extension will detect the `IdGenerator` CDI bean and will use it when configuring the tracer producer. +==== Default +The following attributes are added by default to resources. -[source,java] ----- -@Singleton -public class CustomConfiguration { +|=== +|Attribute name|Content example|Origin - /** Creates a custom IdGenerator for OpenTelemetry */ - @Produces - @Singleton - public IdGenerator idGenerator() { - return AwsXrayIdGenerator.getInstance(); - } -} ----- +|service.name +|"opentelemetry-quickstart" +|Value comes from the artifactId, from the `quarkus.application.name` property or from `quarkus.otel.resource.attributes=service.name=cart` property. -=== Propagators -OpenTelemetry propagates cross-cutting concerns through https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md[propagators] that will share an underlying `Context` for storing state and accessing -data across the lifespan of a distributed transaction. +|host.name +|"myHost" +|Resolved at startup -By default, the OpenTelemetry extension enables the https://www.w3.org/TR/trace-context/[W3C Trace Context] and the https://www.w3.org/TR/baggage/[W3C Baggage] -propagators, you can however choose any of the supported OpenTelemetry propagators by setting the `propagators` config that is described in the <>. +|service.version +|"1.0-SNAPSHOT" +|Resolved at build time from the artifact version -==== Additional Propagators +|telemetry.sdk.language +|"java" +|Static value -* The `b3`, `b3multi`, `jaeger` and `ottrace` propagators will need the https://github.com/open-telemetry/opentelemetry-java/tree/main/extensions/trace-propagators[trace-propagators] -extension to be added as a dependency to your project. +|telemetry.sdk.name +|"opentelemetry" +|Resolved at build time -[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] -.pom.xml ----- - - io.opentelemetry - opentelemetry-extension-trace-propagators - ----- +|telemetry.sdk.version +|"1.32.0" +|Resolved at build time -[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] -.build.gradle ----- -implementation("io.opentelemetry:opentelemetry-extension-trace-propagators") ----- +|webengine.name +|"Quarkus" +|Static value -* The `xray` propagator will need the https://github.com/open-telemetry/opentelemetry-java-contrib/tree/main/aws-xray-propagator[aws] -extension to be added as a dependency to your project. +|webengine.version +|"999-SNAPSHOT" +|Quarkus version resolved at build time +|=== -[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] -.pom.xml ----- - - io.opentelemetry.contrib - opentelemetry-aws-xray-propagator - ----- +==== Using configuration +You can add additional attributes by setting the `quarkus.otel.resource.attributes` config property that is described in the <>. +Since this property can be overridden at runtime, the OpenTelemetry extension will pick up its value following the order of precedence that +is described in the xref:config-reference.adoc#configuration-sources[Quarkus Configuration Reference]. -[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] -.build.gradle +[source,properties] ---- -implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") +quarkus.otel.resource.attributes=deployment.environment=dev,service.name=cart,service.namespace=shopping ---- -==== Customise Propagator - -To customise the propagation header you can implement the `TextMapPropagatorCustomizer` interface. This can be used, as an example, to restrict propagation of OpenTelemetry trace headers and prevent potentially sensitive data to be sent to third party systems. - -```java -/** - * /** - * Meant to be implemented by a CDI bean that provides arbitrary customization for the TextMapPropagator - * that are to be registered with OpenTelemetry - */ -public interface TextMapPropagatorCustomizer { - - TextMapPropagator customize(Context context); - - interface Context { - TextMapPropagator propagator(); - - ConfigProperties otelConfigProperties(); - } -} -``` - -=== Resource -A https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#resources[resource] is a representation -of the entity that is producing telemetry, it adds attributes to the exported trace to characterize who is producing the trace. - -You can add attributes by setting the `resource-attributes` tracer config that is described in the <>. -Since this property can be overridden at runtime, the OpenTelemetry extension will pick up its value following the order of precedence that -is described in the xref:config-reference.adoc#configuration-sources[Quarkus Configuration Reference]. +This will add the attributes for `deployment.environment`, `service.name` and `service.namespace` to the resource and be included in traces and metrics. +==== Using CDI beans If by any means you need to use a custom resource or one that is provided by one of the https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions[OpenTelemetry SDK Extensions] -you can create multiple resource producers. The OpenTelemetry extension will detect the `Resource` CDI beans and will merge them when configuring the tracer producer. +you can create multiple resource producers. The OpenTelemetry extension will detect the `Resource` CDI beans and will merge them when configuring the OTel SDK. [source,java] ---- @@ -398,299 +221,81 @@ public class CustomConfiguration { } ---- -==== End User attributes +=== Semantic conventions -When enabled, Quarkus adds OpenTelemetry End User attributes as Span attributes. -Before you enable this feature, verify that Quarkus Security extension is present and configured. -More information about the Quarkus Security can be found in the xref:security-overview.adoc[Quarkus Security overview]. +OpenTelemetry provides a set of https://opentelemetry.io/docs/specs/semconv/http/http-spans/[semantic conventions] to standardize the data collected by the instrumentation. -The attributes are only added when authentication has already happened on a best-efforts basis. -Whether the End User attributes are added as Span attributes depends on authentication and authorization configuration of your Quarkus application. -If you create custom Spans prior to the authentication, Quarkus cannot add the End User attributes to them. -Quarkus is only able to add the attributes to the Span that is current after the authentication has been finished. -Another important consideration regarding custom Spans is active CDI request context that is used to propagate Quarkus `SecurityIdentity`. -In principle, Quarkus is able to add the End User attributes when the CDI request context has been activated for you before the custom Spans are created. - -[source,application.properties] ----- -quarkus.otel.traces.eusp.enabled=true <1> -quarkus.http.auth.proactive=true <2> ----- -<1> Enable the End User Attributes feature so that the `SecurityIdentity` principal and roles are added as Span attributes. -The End User attributes are personally identifiable information, therefore make sure you want to export them before you enable this feature. -<2> Optionally enable proactive authentication. -The best possible results are achieved when proactive authentication is enabled because the authentication happens sooner. -A good way to determine whether proactive authentication should be enabled in your Quarkus application is to read the Quarkus xref:security-proactive-authentication.adoc[Proactive authentication] guide. - -IMPORTANT: This feature is not supported when a custom xref:security-customization.adoc#jaxrs-security-context[Jakarta REST SecurityContexts] is used. - -[[sampler]] -=== Sampler -A https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling[sampler] decides whether a trace should be discarded or forwarded, effectively managing noise and reducing overhead by limiting the number of collected traces sent to the collector. - -Quarkus comes equipped with a https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#built-in-samplers[built-in sampler], and you also have the option to create your custom sampler. - -To use the built-in sampler, you can configure it by setting the desired sampler parameters as detailed in the <>. As an example, you can configure the sampler to retain 50% of the traces: -[source,application.properties] ----- -# build time property only: -quarkus.otel.traces.sampler=traceidratio -# Runtime property: -quarkus.otel.traces.sampler.arg=0.5 ----- -[TIP] -==== - -An interesting use case for the sampler is to activate and deactivate tracing export at runtime, acording to this example: -[source,application.properties] ----- -# build time property only: -quarkus.otel.traces.sampler=traceidratio -# On (default). All traces are exported: -quarkus.otel.traces.sampler.arg=1.0 -# Off. No traces are exported: -quarkus.otel.traces.sampler.arg=0.0 ----- -==== - -[NOTE] -==== -Quarkus 3.0 introduced breaking changes on the configuration. - -Sampler related property names and values change to comply with the latest Java OpenTelemetry SDK. During a transition period it will be possible to set the new configuration values in the old property because we are mapping `quarkus.opentelemetry.tracer.sampler` -> `quarkus.otel.traces.sampler`. - -If the sampler is parent based, there is no need to set, the now dropped property, `quarkus.opentelemetry.tracer.sampler.parent-based`. - -The values you need to set on `quarkus.opentelemetry.tracer.sampler` are now: - -|=== -|Old Sampler config value |New Sampler config value|New Sampler config value (Parent based) - -|`on` -|`always_on` -|`parentbased_always_on` - -|`off` -|`always_off` -|`parentbased_always_off` - -|`ratio` -|`traceidratio` -|`parentbased_traceidratio` -|=== -==== - -If you need to use a custom sampler there are now 2 different ways: - -==== Sampler CDI Producer - -You can create a sampler CDI producer. The Quarkus OpenTelemetry extension will detect the `Sampler` CDI bean and will use it when configuring the Tracer. - -[source,java] ----- -@Singleton -public class CustomConfiguration { - - /** Creates a custom sampler for OpenTelemetry */ - @Produces - @Singleton - public Sampler sampler() { - return JaegerRemoteSampler.builder() - .setServiceName("my-service") - .build(); - } -} ----- - -==== OTel Sampler SPI - -This will use the SPI hooks available with the OTel Autoconfiguration. -You can create a simple Sampler class: -[source,java] ----- -public class CustomSPISampler implements Sampler { - @Override - public SamplingResult shouldSample(Context context, - String s, - String s1, - SpanKind spanKind, - Attributes attributes, - List list) { - // Do some sampling here - return Sampler.alwaysOn().shouldSample(context, s, s1, spanKind, attributes, list); - } - - @Override - public String getDescription() { - return "custom-spi-sampler-description"; - } -} +When creating manual instrumentation, while naming metrics or attributes you should follow those conventions and not create new names to represent existing conventions. This will make data correlation easier to perform across services. ----- -Then a Sampler Provider: -[source,java] ----- -public class CustomSPISamplerProvider implements ConfigurableSamplerProvider { - @Override - public Sampler createSampler(ConfigProperties configProperties) { - return new CustomSPISampler(); - } - - @Override - public String getName() { - return "custom-spi-sampler"; - } -} ----- -Write the SPI loader text file at `resources/META-INF/services` with name `io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider` containing the full qualified name of the `CustomSPISamplerProvider` class. - -Then activate on the configuration: -[source,properties] ----- -quarkus.otel.traces.sampler=custom-spi-sampler ----- - -As you can see, CDI is much simpler to work with. - -== Additional instrumentation - -Some Quarkus extensions will require additional code to ensure traces are propagated to subsequent execution. -These sections will outline what is necessary to propagate traces across process boundaries. - -The instrumentation documented in this section has been tested with Quarkus and works in both standard and native mode. - -=== CDI - -Annotating a method in any CDI aware bean with the `io.opentelemetry.instrumentation.annotations.WithSpan` -annotation will create a new Span and establish any required relationships with the current Trace context. - -Annotating a method in any CDI aware bean with the `io.opentelemetry.instrumentation.annotations.AddingSpanAttributes` will not create a new span but will add annotated method parameters to attributes in the current span. - -If a method is annotated by mistake with `@AddingSpanAttributes` and `@WithSpan` annotations, the `@WithSpan` annotation will take precedence. - -Method parameters can be annotated with the `io.opentelemetry.instrumentation.annotations.SpanAttribute` annotation to -indicate which method parameters should be part of the span. The parameter name can be customized as well. - -Example: -[source,java] ----- -@ApplicationScoped -class SpanBean { - @WithSpan - void span() { - - } +== Exporters - @WithSpan("name") - void spanName() { +=== Default - } +The Quarkus OpenTelemetry extension uses its own signal exporters built on top of Vert.x for optimal performance and maintainability. - @WithSpan(kind = SERVER) - void spanKind() { +The exporter is automatically wired with CDI, that's why the `quarkus.otel.traces.exporter` and `quarkus.otel.metrics.exporter` properties default to `cdi`. - } +The `quarkus.otel.exporter.otlp.protocol` defaults to `grpc` but `http/protobuf` can also be used. - @WithSpan - void spanArgs(@SpanAttribute(value = "arg") String arg) { +NOTE: If you change the protocol, you also need to change the port in the endpoint. The default port for `grpc` is `4317` and for `http/protobuf` is `4318`. - } +=== On Quarkiverse +Additional exporters will be available in the Quarkiverse https://docs.quarkiverse.io/quarkus-opentelemetry-exporter/dev/index.html[quarkus-opentelemetry-exporter] project. - @AddingSpanAttributes - void addArgumentToExistingSpan(@SpanAttribute(value = "arg") String arg) { +Currently, are available the following exporters (may be outdated) for: - } -} ----- +- Legacy Jaeger +- Microsoft Azure +- Google Cloud -=== Available OpenTelemetry CDI injections +Also on Quarkiverse, the https://docs.quarkiverse.io/quarkus-amazon-services/dev/opentelemetry.html[Quarkus AWS SDK has integration with OpenTelemetry]. -As per MicroProfile Telemetry Tracing specification, Quarkus supports the CDI injections of the -following classes: +=== Logging exporter (for debugging) -* `io.opentelemetry.api.OpenTelemetry` -* `io.opentelemetry.api.trace.Tracer` -* `io.opentelemetry.api.trace.Span` -* `io.opentelemetry.api.baggage.Baggage` +You can output all metrics to the console, for debugging/development purposes. -You can inject these classes in any CDI enabled bean. For instance, the `Tracer` is particularly useful to start custom spans: +IMPORTANT: Don't use this in production. -[source,java] +You will need to add the following dependency to your project: +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml ---- -@Inject -Tracer tracer; - -... - -public void tracedWork() { - Span span = tracer.spanBuilder("My custom span") - .setAttribute("attr", "attr.value") - .setParent(Context.current().with(Span.current())) - .setSpanKind(SpanKind.INTERNAL) - .startSpan(); - - // traced work - - span.end(); -} + + io.opentelemetry + opentelemetry-exporter-logging + ---- -=== Quarkus Messaging - Kafka - -When using the Quarkus Messaging extension for Kafka, -we are able to propagate the span into the Kafka Record with: - -[source,java] +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle ---- -TracingMetadata tm = TracingMetadata.withPrevious(Context.current()); -Message out = Message.of(...).withMetadata(tm); +implementation("io.opentelemetry:opentelemetry-exporter-logging") ---- -The above creates a `TracingMetadata` object we can add to the `Message` being produced, -which retrieves the OpenTelemetry `Context` to extract the current span for propagation. - -=== Quarkus Security Events - -Quarkus supports exporting of the xref:security-customization.adoc#observe-security-events[Security events] as OpenTelemetry Span events. -[source,application.properties] +Then, setting the exporter to `logging` in the `application.properties` file: +[source, properties] ---- -quarkus.otel.security-events.enabled=true <1> +quarkus.otel.metrics.exporter=logging <1> +quarkus.otel.metric.export.interval=10000ms <2> +quarkus.otel.traces.exporter=logging <3> ---- -<1> Export Quarkus Security events as OpenTelemetry Span events. -== Exporters +<1> Set the metrics exporter to `logging`. Normally you don't need to set this. The default is `cdi`. +<2> Set the interval to export the metrics. The default is `1m`, which is too long for debugging. +<3> Set the traces exporter to `logging`. Normally you don't need to set this. The default is `cdi`. -=== Default +== Visualizing the data -The Quarkus OpenTelemetry extension uses its own exporter built on top of Vert.x for optimal performance and maintainability. +We recommend the xref:observability-devservices-lgtm.adoc[Getting Started with Grafana-OTel-LGTM]. -The exporter is automatically wired with CDI, that's why the `quarkus.otel.traces.exporter` property defaults to `cdi`. +This provides a Quarkus Dev service using an "all-in-one" https://github.com/grafana/docker-otel-lgtm[Grafana OTel LGTM]. -The `quarkus.otel.exporter.otlp.traces.protocol` default to `grpc` and `http/protobuf` can also be used. +Grafana is used to visualize data, Loki to store logs, Tempo to store traces and Prometheus to store metrics. Also provides and OTel collector to receive the data. -=== On Quarkiverse -Additional exporters will be available in the Quarkiverse https://docs.quarkiverse.io/quarkus-opentelemetry-exporter/dev/index.html[quarkus-opentelemetry-exporter] project. +This provides an easy way to visualize all OpenTelemetry data generated by the application. -[[quarkus-extensions-using-opentelemetry]] -== Quarkus core extensions instrumented with OpenTelemetry tracing - -* https://quarkus.io/extensions/io.quarkus/quarkus-agroal[`quarkus-agroal`] -* https://quarkus.io/guides/grpc-getting-started[`quarkus-grpc`] -* https://quarkus.io/guides/redis[`quarkus-redis-client`] -* https://quarkus.io/extensions/io.quarkus/quarkus-rest-client-jaxrs[`quarkus-rest-client-jaxrs`] -* https://quarkus.io/guides/rest[`quarkus-rest`] -* https://quarkus.io/guides/resteasy[`quarkus-resteasy-jackson`] -* https://quarkus.io/guides/resteasy-client[`quarkus-resteasy-client`] -* https://quarkus.io/guides/scheduler[`quarkus-scheduler`] -* https://quarkus.io/guides/smallrye-graphql[`quarkus-smallrye-graphql`] -* https://quarkus.io/extensions/io.quarkus/quarkus-mongodb-client[`quarkus-mongodb-client`] -* https://quarkus.io/extensions/io.quarkus/quarkus-messaging[`quarkus-messaging`] -** AMQP 1.0 -** RabbitMQ -** Kafka -** Pulsar -* https://quarkus.io/guides/vertx[`quarkus-vertx`] (http requests) +You can also use the xref:logging-exporter-for-debugging[logging exporter] to output all traces and metrics to the console. [[configuration-reference]] == OpenTelemetry Configuration Reference @@ -702,7 +307,4 @@ adding the usual `quarkus.*` prefix. Quarkus OpenTelemetry configuration properties now have the `quarkus.otel.*` prefix. -*The legacy properties* with prefix `quarkus.opentelemetry.*` are currently being mapped to the new ones as a default, during a transition period. See Default column in the details below. - - include::{generated-dir}/config/quarkus-opentelemetry.adoc[leveloffset=+1, opts=optional] diff --git a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java index ab745941977be..4664cb0d67faf 100644 --- a/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java +++ b/extensions/opentelemetry/deployment/src/main/java/io/quarkus/opentelemetry/deployment/OpenTelemetryProcessor.java @@ -2,7 +2,6 @@ import static io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem.SPI_ROOT; import static io.quarkus.opentelemetry.runtime.OpenTelemetryRecorder.OPEN_TELEMETRY_DRIVER; -import static io.quarkus.opentelemetry.runtime.OpenTelemetryUtil.*; import static java.util.stream.Collectors.toList; import java.io.IOException; @@ -104,7 +103,7 @@ AdditionalBeanBuildItem ensureProducerIsRetained() { return AdditionalBeanBuildItem.builder() .setUnremovable() .addBeanClasses( - AutoConfiguredOpenTelemetrySdkBuilderCustomizer.ResourceCustomizer.class, + AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracingResourceCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.SamplerCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracerProviderCustomizer.class, AutoConfiguredOpenTelemetrySdkBuilderCustomizer.MetricProviderCustomizer.class, diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterBadEndpointTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterBadEndpointTest.java index 7d4048c0501c0..231c31457bec7 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterBadEndpointTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterBadEndpointTest.java @@ -15,7 +15,7 @@ public class OtlpExporterBadEndpointTest { static final QuarkusUnitTest config = new QuarkusUnitTest() .withEmptyApplication() .overrideConfigKey("quarkus.otel.traces.exporter", "cdi") - .overrideConfigKey("quarkus.otel.exporter.otlp.traces.legacy-endpoint", "httz://nada:zero") + .overrideConfigKey("quarkus.otel.exporter.otlp.endpoint", "httz://nada:zero") .setExpectedException(IllegalArgumentException.class); @Inject diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterConfigTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterConfigTest.java index 3692683973ad0..4c252cb11f7af 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterConfigTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterConfigTest.java @@ -16,8 +16,10 @@ public class OtlpExporterConfigTest { static final QuarkusUnitTest TEST = new QuarkusUnitTest() .withEmptyApplication() .overrideConfigKey("quarkus.otel.traces.exporter", "cdi") + .overrideConfigKey("quarkus.otel.exporter.otlp.protocol", "wrong") .overrideConfigKey("quarkus.otel.exporter.otlp.traces.protocol", "http/protobuf") - .overrideConfigKey("quarkus.otel.exporter.otlp.traces.legacy-endpoint", "http://localhost ") + .overrideConfigKey("quarkus.otel.exporter.otlp.traces.endpoint", "http://localhost ") + .overrideConfigKey("quarkus.otel.metrics.exporter", "none") .overrideConfigKey("quarkus.otel.bsp.schedule.delay", "50") .overrideConfigKey("quarkus.otel.bsp.export.timeout", "PT1S"); @@ -26,7 +28,9 @@ public class OtlpExporterConfigTest { @Test void config() { - assertTrue(config.traces().legacyEndpoint().isPresent()); - assertEquals("http://localhost", config.traces().legacyEndpoint().get().trim()); + assertTrue(config.traces().protocol().isPresent()); + assertEquals("http/protobuf", config.traces().protocol().get().trim()); + assertTrue(config.traces().endpoint().isPresent()); + assertEquals("http://localhost", config.traces().endpoint().get().trim()); } } diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterOverrideConfigTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterOverrideConfigTest.java new file mode 100644 index 0000000000000..ff982fcd7de08 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/exporter/otlp/OtlpExporterOverrideConfigTest.java @@ -0,0 +1,34 @@ +package io.quarkus.opentelemetry.deployment.exporter.otlp; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig; +import io.quarkus.test.QuarkusUnitTest; + +public class OtlpExporterOverrideConfigTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.otel.traces.exporter", "cdi") + .overrideConfigKey("quarkus.otel.exporter.otlp.protocol", "http/protobuf") + .overrideConfigKey("quarkus.otel.exporter.otlp.endpoint", "http://localhost ") + .overrideConfigKey("quarkus.otel.bsp.schedule.delay", "50") + .overrideConfigKey("quarkus.otel.bsp.export.timeout", "PT1S"); + + @Inject + OtlpExporterRuntimeConfig config; + + @Test + void config() { + assertTrue(config.traces().protocol().isPresent()); + assertEquals("http/protobuf", config.traces().protocol().get().trim()); + assertTrue(config.traces().endpoint().isPresent()); + assertEquals("http://localhost", config.traces().endpoint().get().trim()); + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java index aa7360206490b..ccc5ec9d172d2 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java @@ -41,7 +41,7 @@ public interface AutoConfiguredOpenTelemetrySdkBuilderCustomizer { void customize(AutoConfiguredOpenTelemetrySdkBuilder builder); @Singleton - final class ResourceCustomizer implements AutoConfiguredOpenTelemetrySdkBuilderCustomizer { + final class TracingResourceCustomizer implements AutoConfiguredOpenTelemetrySdkBuilderCustomizer { private final ApplicationConfig appConfig; private final OTelBuildConfig oTelBuildConfig; @@ -49,7 +49,7 @@ final class ResourceCustomizer implements AutoConfiguredOpenTelemetrySdkBuilderC private final Instance delayedAttributes; private final List resources; - public ResourceCustomizer(ApplicationConfig appConfig, + public TracingResourceCustomizer(ApplicationConfig appConfig, OTelBuildConfig oTelBuildConfig, OTelRuntimeConfig oTelRuntimeConfig, @Any Instance delayedAttributes, @@ -66,7 +66,8 @@ public void customize(AutoConfiguredOpenTelemetrySdkBuilder builder) { builder.addResourceCustomizer(new BiFunction<>() { @Override public Resource apply(Resource existingResource, ConfigProperties configProperties) { - if (oTelBuildConfig.traces().enabled().orElse(TRUE)) { + if (oTelBuildConfig.traces().enabled().orElse(TRUE) || + oTelBuildConfig.metrics().enabled().orElse(TRUE)) { Resource consolidatedResource = existingResource.merge( Resource.create(delayedAttributes.get())); @@ -82,13 +83,7 @@ public boolean test(String sn) { }) .orElse(null); - // must be resolved at startup, once. - String hostname = null; - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - hostname = "unknown"; - } + String hostname = getHostname(); // Merge resource instances with env attributes Resource resource = resources.stream() @@ -259,4 +254,16 @@ public ConfigProperties otelConfigProperties() { } } } + + private static String getHostname() { + // must be resolved at startup, once. + String hostname = null; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + hostname = "unknown"; + } + return hostname; + } + } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/HierarchicalOTelConnectionConfigInterceptor.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/HierarchicalOTelConnectionConfigInterceptor.java new file mode 100644 index 0000000000000..bd011e5a4c684 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/HierarchicalOTelConnectionConfigInterceptor.java @@ -0,0 +1,92 @@ +package io.quarkus.opentelemetry.runtime.config; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.function.Function; + +import jakarta.annotation.Priority; + +import io.smallrye.config.ConfigSourceInterceptorContext; +import io.smallrye.config.ConfigValue; +import io.smallrye.config.FallbackConfigSourceInterceptor; +import io.smallrye.config.Priorities; + +@Priority(Priorities.LIBRARY + 300 + 5) +public class HierarchicalOTelConnectionConfigInterceptor extends FallbackConfigSourceInterceptor { + + // The properties that are shared between the traces and metrics configuration + // string after "quarkus.otel.exporter.otlp." + private final static List PROPERTY_NAMES = List.of( + "endpoint", + "headers", + "compression", + "timeout", + "protocol", + "key-cert.keys", + "key-cert.certs", + "trust-cert.certs", + "tls-configuration-name", + "proxy-options.enabled", + "proxy-options.username", + "proxy-options.password", + "proxy-options.port", + "proxy-options.host"); + + static final String BASE = "quarkus.otel.exporter.otlp."; + static final String TRACES = BASE + "traces."; + static final String METRICS = BASE + "metrics."; + + private static final MappingFunction mappingFunction = new MappingFunction(); + + public HierarchicalOTelConnectionConfigInterceptor() { + super(mappingFunction); + } + + @Override + public Iterator iterateNames(final ConfigSourceInterceptorContext context) { + Set names = new HashSet<>(); + Iterator namesIterator = context.iterateNames(); + while (namesIterator.hasNext()) { + String name = namesIterator.next(); + String fallback = mappingFunction.apply(name); + // We only include the used property, so if it is a fallback (not mapped), it will be reported as unknown + if (fallback != null) { + ConfigValue nameValue = context.proceed(name); + ConfigValue fallbackValue = context.proceed(fallback); + if (nameValue == null) { + names.add(fallback); + } else if (fallbackValue == null) { + names.add(name); + } else if (nameValue.getConfigSourceOrdinal() >= fallbackValue.getConfigSourceOrdinal()) { + names.add(name); + } else { + names.add(fallback); + } + } else { + names.add(name); + } + } + return names.iterator(); + } + + static class MappingFunction implements Function { + @Override + public String apply(String name) { + if (name.startsWith(TRACES)) { + String property = name.substring(TRACES.length()); + if (PROPERTY_NAMES.contains(property)) { + return BASE + property; + } + } + if (name.startsWith(METRICS)) { + String property = name.substring(METRICS.length()); + if (PROPERTY_NAMES.contains(property)) { + return BASE + property; + } + } + return name; + } + } +} diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfig.java index 8c6ac92c6c330..0c9075b32c9bf 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterConfig.java @@ -1,16 +1,12 @@ package io.quarkus.opentelemetry.runtime.config.runtime.exporter; -import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig.DEFAULT_GRPC_BASE_URI; - import java.time.Duration; import java.util.List; import java.util.Optional; import java.util.OptionalInt; -import io.quarkus.runtime.annotations.ConfigDocDefault; import io.quarkus.runtime.annotations.ConfigGroup; import io.smallrye.config.WithDefault; -import io.smallrye.config.WithName; @ConfigGroup public interface OtlpExporterConfig { @@ -18,10 +14,7 @@ public interface OtlpExporterConfig { /** * OTLP Exporter specific. Will override otel.exporter.otlp.endpoint, if set. *

- * Fallbacks to the legacy property quarkus.opentelemetry.tracer.exporter.otlp.endpoint< or - * defaults to {@value OtlpExporterRuntimeConfig#DEFAULT_GRPC_BASE_URI}. */ - @WithDefault(DEFAULT_GRPC_BASE_URI) Optional endpoint(); /** @@ -41,7 +34,6 @@ public interface OtlpExporterConfig { * Sets the maximum time to wait for the collector to process an exported batch of spans. If * unset, defaults to {@value OtlpExporterRuntimeConfig#DEFAULT_TIMEOUT_SECS}s. */ - @WithDefault("10s") Duration timeout(); /** @@ -50,19 +42,16 @@ public interface OtlpExporterConfig { *

* Currently, only {@code grpc} and {@code http/protobuf} are allowed. */ - @WithDefault(Protocol.GRPC) Optional protocol(); /** * Key/cert configuration in the PEM format. */ - @WithName("key-cert") KeyCert keyCert(); /** * Trust configuration in the PEM format. */ - @WithName("trust-cert") TrustCert trustCert(); /** @@ -99,7 +88,6 @@ interface ProxyConfig { /** * Set proxy port. */ - @ConfigDocDefault("3128") OptionalInt port(); /** diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterRuntimeConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterRuntimeConfig.java index d2d308c841296..111e6550e014b 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterRuntimeConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterRuntimeConfig.java @@ -1,11 +1,14 @@ package io.quarkus.opentelemetry.runtime.config.runtime.exporter; +import java.time.Duration; +import java.util.List; import java.util.Optional; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; /** * From quarkus.otel.exporter.otlp.traces.endpoint - * is recommended. + * Sets the OTLP endpoint for connecting all signals. If unset, defaults to + * {@value OtlpExporterRuntimeConfig#DEFAULT_GRPC_BASE_URI}. */ + @Override @WithDefault(DEFAULT_GRPC_BASE_URI) Optional endpoint(); + /** + * Key-value pairs to be used as headers associated with gRPC requests. + * The format is similar to the {@code OTEL_EXPORTER_OTLP_HEADERS} environment variable, + * a list of key-value pairs separated by the "=" character. i.e.: key1=value1,key2=value2 + */ + @Override + Optional> headers(); + + /** + * Sets the method used to compress payloads. If unset, compression is disabled. Currently + * supported compression methods include `gzip` and `none`. + */ + @Override + Optional compression(); + + /** + * Sets the maximum time to wait for the collector to process an exported batch of spans. If + * unset, defaults to {@value OtlpExporterRuntimeConfig#DEFAULT_TIMEOUT_SECS}s. + */ + @Override + @WithDefault("10s") + Duration timeout(); + + /** + * OTLP defines the encoding of telemetry data and the protocol used to exchange data between the client and the + * server. Depending on the exporter, the available protocols will be different. + *

+ * Currently, only {@code grpc} and {@code http/protobuf} are allowed. + */ + @Override + @WithDefault(OtlpExporterConfig.Protocol.GRPC) + Optional protocol(); + + /** + * Key/cert configuration in the PEM format. + */ + @Override + @WithName("key-cert") + OtlpExporterConfig.KeyCert keyCert(); + + /** + * Trust configuration in the PEM format. + */ + @Override + @WithName("trust-cert") + OtlpExporterConfig.TrustCert trustCert(); + + /** + * The name of the TLS configuration to use. + *

+ * If not set and the default TLS configuration is configured ({@code quarkus.tls.*}) then that will be used. + * If a name is configured, it uses the configuration from {@code quarkus.tls..*} + * If a name is configured, but no TLS configuration is found with that name then an error will be thrown. + */ + @Override + Optional tlsConfigurationName(); + + /** + * Set proxy options + */ + @Override + OtlpExporterConfig.ProxyConfig proxyOptions(); + /** * OTLP traces exporter configuration. */ diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterTracesConfig.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterTracesConfig.java index 12eac3685da91..4371c78b44111 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterTracesConfig.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/config/runtime/exporter/OtlpExporterTracesConfig.java @@ -1,22 +1,7 @@ package io.quarkus.opentelemetry.runtime.config.runtime.exporter; -import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig.DEFAULT_GRPC_BASE_URI; - -import java.util.Optional; - import io.quarkus.runtime.annotations.ConfigGroup; -import io.smallrye.config.WithDefault; -import io.smallrye.config.WithName; @ConfigGroup public interface OtlpExporterTracesConfig extends OtlpExporterConfig { - - /** - * See {@link OtlpExporterTracesConfig#endpoint} - */ - // @WithConverter(TrimmedStringConverter.class) - @Deprecated - @WithName("legacy-endpoint") - @WithDefault(DEFAULT_GRPC_BASE_URI) - Optional legacyEndpoint(); } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterRecorder.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterRecorder.java index 607c5f03ab95e..4d7d4956b61d5 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterRecorder.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OTelExporterRecorder.java @@ -1,6 +1,5 @@ package io.quarkus.opentelemetry.runtime.exporter.otlp; -import static io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram; import static io.quarkus.opentelemetry.runtime.config.build.ExporterType.Constants.OTLP_VALUE; import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfig.Protocol.GRPC; import static io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfig.Protocol.HTTP_PROTOBUF; @@ -67,13 +66,12 @@ public class OTelExporterRecorder { public static final String BASE2EXPONENTIAL_AGGREGATION_NAME = AggregationUtil .aggregationName(Aggregation.base2ExponentialBucketHistogram()); - public static final String EXPLICIT_BUCKET_AGGREGATION_NAME = AggregationUtil.aggregationName(explicitBucketHistogram()); public Function, LateBoundBatchSpanProcessor> batchSpanProcessorForOtlp( OTelRuntimeConfig otelRuntimeConfig, OtlpExporterRuntimeConfig exporterRuntimeConfig, Supplier vertx) { - URI baseUri = getBaseUri(exporterRuntimeConfig); // do the creation and validation here in order to preserve backward compatibility + URI baseUri = getTracesUri(exporterRuntimeConfig); // do the creation and validation here in order to preserve backward compatibility return new Function<>() { @Override public LateBoundBatchSpanProcessor apply( @@ -180,7 +178,7 @@ public Function, MetricExporter> crea OtlpExporterRuntimeConfig exporterRuntimeConfig, Supplier vertx) { - final URI baseUri = getBaseUri(exporterRuntimeConfig); + final URI baseUri = getMetricsUri(exporterRuntimeConfig); return new Function<>() { @Override @@ -311,22 +309,37 @@ private static Map populateTracingExportHttpHeaders(OtlpExporter return headersMap; } - private URI getBaseUri(OtlpExporterRuntimeConfig exporterRuntimeConfig) { - String endpoint = resolveEndpoint(exporterRuntimeConfig).trim(); // FIXME must be signal independent + private URI getTracesUri(OtlpExporterRuntimeConfig exporterRuntimeConfig) { + String endpoint = resolveTraceEndpoint(exporterRuntimeConfig); if (endpoint.isEmpty()) { return null; } return ExporterBuilderUtil.validateEndpoint(endpoint); } - static String resolveEndpoint(final OtlpExporterRuntimeConfig runtimeConfig) { - String endpoint = runtimeConfig.traces().legacyEndpoint() + private URI getMetricsUri(OtlpExporterRuntimeConfig exporterRuntimeConfig) { + String endpoint = resolveTraceEndpoint(exporterRuntimeConfig); + if (endpoint.isEmpty()) { + return null; + } + return ExporterBuilderUtil.validateEndpoint(endpoint); + } + + static String resolveTraceEndpoint(final OtlpExporterRuntimeConfig runtimeConfig) { + String endpoint = runtimeConfig.traces().endpoint() + .filter(OTelExporterRecorder::excludeDefaultEndpoint) + .orElse(runtimeConfig.endpoint() + .filter(OTelExporterRecorder::excludeDefaultEndpoint) + .orElse(DEFAULT_GRPC_BASE_URI)); + return endpoint.trim(); + } + + static String resolveMetricEndpoint(final OtlpExporterRuntimeConfig runtimeConfig) { + String endpoint = runtimeConfig.metrics().endpoint() .filter(OTelExporterRecorder::excludeDefaultEndpoint) - .orElse(runtimeConfig.traces().endpoint() + .orElse(runtimeConfig.endpoint() .filter(OTelExporterRecorder::excludeDefaultEndpoint) - .orElse(runtimeConfig.endpoint() - .filter(OTelExporterRecorder::excludeDefaultEndpoint) - .orElse(DEFAULT_GRPC_BASE_URI))); + .orElse(DEFAULT_GRPC_BASE_URI)); return endpoint.trim(); } diff --git a/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceInterceptor b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceInterceptor new file mode 100644 index 0000000000000..55782f5c4d6ec --- /dev/null +++ b/extensions/opentelemetry/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceInterceptor @@ -0,0 +1 @@ +io.quarkus.opentelemetry.runtime.config.HierarchicalOTelConnectionConfigInterceptor diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/config/HierarchicalOTelConnectionConfigInterceptorTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/config/HierarchicalOTelConnectionConfigInterceptorTest.java new file mode 100644 index 0000000000000..e509ebb35805a --- /dev/null +++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/config/HierarchicalOTelConnectionConfigInterceptorTest.java @@ -0,0 +1,62 @@ +package io.quarkus.opentelemetry.runtime.config; + +import static io.quarkus.opentelemetry.runtime.config.HierarchicalOTelConnectionConfigInterceptor.BASE; +import static io.quarkus.opentelemetry.runtime.config.HierarchicalOTelConnectionConfigInterceptor.METRICS; +import static io.quarkus.opentelemetry.runtime.config.HierarchicalOTelConnectionConfigInterceptor.MappingFunction; +import static io.quarkus.opentelemetry.runtime.config.HierarchicalOTelConnectionConfigInterceptor.TRACES; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +class HierarchicalOTelConnectionConfigInterceptorTest { + + private final static Map FALLBACKS = new HashMap<>(); + + static { + FALLBACKS.put(TRACES + "endpoint", BASE + "endpoint"); + FALLBACKS.put(METRICS + "endpoint", BASE + "endpoint"); + FALLBACKS.put(TRACES + "headers", BASE + "headers"); + FALLBACKS.put(METRICS + "headers", BASE + "headers"); + FALLBACKS.put(TRACES + "compression", BASE + "compression"); + FALLBACKS.put(METRICS + "compression", BASE + "compression"); + FALLBACKS.put(TRACES + "timeout", BASE + "timeout"); + FALLBACKS.put(METRICS + "timeout", BASE + "timeout"); + FALLBACKS.put(TRACES + "protocol", BASE + "protocol"); + FALLBACKS.put(METRICS + "protocol", BASE + "protocol"); + FALLBACKS.put(TRACES + "key-cert.keys", BASE + "key-cert.keys"); + FALLBACKS.put(METRICS + "key-cert.keys", BASE + "key-cert.keys"); + FALLBACKS.put(TRACES + "key-cert.certs", BASE + "key-cert.certs"); + FALLBACKS.put(METRICS + "key-cert.certs", BASE + "key-cert.certs"); + FALLBACKS.put(TRACES + "trust-cert.certs", BASE + "trust-cert.certs"); + FALLBACKS.put(METRICS + "trust-cert.certs", BASE + "trust-cert.certs"); + FALLBACKS.put(TRACES + "tls-configuration-name", BASE + "tls-configuration-name"); + FALLBACKS.put(METRICS + "tls-configuration-name", BASE + "tls-configuration-name"); + FALLBACKS.put(TRACES + "proxy-options.enabled", BASE + "proxy-options.enabled"); + FALLBACKS.put(METRICS + "proxy-options.enabled", BASE + "proxy-options.enabled"); + FALLBACKS.put(TRACES + "proxy-options.username", BASE + "proxy-options.username"); + FALLBACKS.put(METRICS + "proxy-options.username", BASE + "proxy-options.username"); + FALLBACKS.put(TRACES + "proxy-options.password", BASE + "proxy-options.password"); + FALLBACKS.put(METRICS + "proxy-options.password", BASE + "proxy-options.password"); + FALLBACKS.put(TRACES + "proxy-options.port", BASE + "proxy-options.port"); + FALLBACKS.put(METRICS + "proxy-options.port", BASE + "proxy-options.port"); + FALLBACKS.put(TRACES + "proxy-options.host", BASE + "proxy-options.host"); + FALLBACKS.put(METRICS + "proxy-options.host", BASE + "proxy-options.host"); + } + + @Test + void testMapping() { + MappingFunction mappingFunction = new MappingFunction(); + for (String propertyName : FALLBACKS.keySet()) { + assertEquals(FALLBACKS.get(propertyName), mappingFunction.apply(propertyName)); + } + } + + @Test + void testNotMapping() { + MappingFunction mappingFunction = new MappingFunction(); + assertEquals("something", mappingFunction.apply("something")); + } +} diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/HttpClientOptionsConsumerTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/HttpClientOptionsConsumerTest.java index 16b7a6c815499..907c467ad6900 100644 --- a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/HttpClientOptionsConsumerTest.java +++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/HttpClientOptionsConsumerTest.java @@ -56,11 +56,6 @@ public Optional endpoint() { return Optional.of("http://localhost:4317"); } - @Override - public Optional legacyEndpoint() { - return Optional.empty(); - } - @Override public Optional> headers() { return Optional.empty(); diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OtlpExporterProviderTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OtlpExporterProviderTest.java index 7275aecf8b81b..625baaa74a337 100644 --- a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OtlpExporterProviderTest.java +++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/exporter/otlp/OtlpExporterProviderTest.java @@ -18,67 +18,132 @@ class OtlpExporterProviderTest { @Test - public void resolveEndpoint_legacyWins() { + public void resolveTraceEndpoint_newWins() { + assertEquals("http://localhost:2222/", + OTelExporterRecorder.resolveTraceEndpoint(createOtlpExporterRuntimeConfig( + "http://localhost:1111/", + "http://localhost:2222/"))); + } + + @Test + public void resolveTraceEndpoint_globalWins() { assertEquals("http://localhost:1111/", - OTelExporterRecorder.resolveEndpoint(createOtlpExporterRuntimeConfig( - DEFAULT_GRPC_BASE_URI, + OTelExporterRecorder.resolveTraceEndpoint(createOtlpExporterRuntimeConfig( "http://localhost:1111/", + DEFAULT_GRPC_BASE_URI))); + } + + @Test + public void resolveTraceEndpoint_legacyTraceWins() { + assertEquals("http://localhost:2222/", + OTelExporterRecorder.resolveTraceEndpoint(createOtlpExporterRuntimeConfig( + DEFAULT_GRPC_BASE_URI, "http://localhost:2222/"))); } @Test - public void resolveEndpoint_newWins() { + public void resolveTraceEndpoint_legacyGlobalWins() { + assertEquals(DEFAULT_GRPC_BASE_URI, + OTelExporterRecorder.resolveTraceEndpoint(createOtlpExporterRuntimeConfig( + DEFAULT_GRPC_BASE_URI, + null))); + } + + @Test + public void resolveTraceEndpoint_testIsSet() { + assertEquals(DEFAULT_GRPC_BASE_URI, + OTelExporterRecorder.resolveTraceEndpoint(createOtlpExporterRuntimeConfig( + null, + null))); + } + + @Test + public void resolveMetricEndpoint_newWins() { assertEquals("http://localhost:2222/", - OTelExporterRecorder.resolveEndpoint(createOtlpExporterRuntimeConfig( + OTelExporterRecorder.resolveMetricEndpoint(createOtlpExporterRuntimeConfig( "http://localhost:1111/", - DEFAULT_GRPC_BASE_URI, "http://localhost:2222/"))); } @Test - public void resolveEndpoint_globalWins() { + public void resolveMetricEndpoint_globalWins() { assertEquals("http://localhost:1111/", - OTelExporterRecorder.resolveEndpoint(createOtlpExporterRuntimeConfig( + OTelExporterRecorder.resolveMetricEndpoint(createOtlpExporterRuntimeConfig( "http://localhost:1111/", - DEFAULT_GRPC_BASE_URI, DEFAULT_GRPC_BASE_URI))); } @Test - public void resolveEndpoint_legacyTraceWins() { + public void resolveMetricEndpoint_legacyTraceWins() { assertEquals("http://localhost:2222/", - OTelExporterRecorder.resolveEndpoint(createOtlpExporterRuntimeConfig( + OTelExporterRecorder.resolveMetricEndpoint(createOtlpExporterRuntimeConfig( DEFAULT_GRPC_BASE_URI, - null, "http://localhost:2222/"))); } @Test - public void resolveEndpoint_legacyGlobalWins() { + public void resolveMetricEndpoint_legacyGlobalWins() { assertEquals(DEFAULT_GRPC_BASE_URI, - OTelExporterRecorder.resolveEndpoint(createOtlpExporterRuntimeConfig( + OTelExporterRecorder.resolveMetricEndpoint(createOtlpExporterRuntimeConfig( DEFAULT_GRPC_BASE_URI, - null, null))); } @Test - public void resolveEndpoint_testIsSet() { + public void resolveMetricEndpoint_testIsSet() { assertEquals(DEFAULT_GRPC_BASE_URI, - OTelExporterRecorder.resolveEndpoint(createOtlpExporterRuntimeConfig( - null, + OTelExporterRecorder.resolveMetricEndpoint(createOtlpExporterRuntimeConfig( null, null))); } - private OtlpExporterRuntimeConfig createOtlpExporterRuntimeConfig(String exporterGlobal, String legacyTrace, - String newTrace) { + private OtlpExporterRuntimeConfig createOtlpExporterRuntimeConfig(String exporterGlobal, String newTrace) { return new OtlpExporterRuntimeConfig() { @Override public Optional endpoint() { return Optional.ofNullable(exporterGlobal); } + @Override + public Optional> headers() { + return Optional.empty(); + } + + @Override + public Optional compression() { + return Optional.empty(); + } + + @Override + public Duration timeout() { + return null; + } + + @Override + public Optional protocol() { + return Optional.empty(); + } + + @Override + public KeyCert keyCert() { + return null; + } + + @Override + public TrustCert trustCert() { + return null; + } + + @Override + public Optional tlsConfigurationName() { + return Optional.empty(); + } + + @Override + public ProxyConfig proxyOptions() { + return null; + } + @Override public OtlpExporterTracesConfig traces() { return new OtlpExporterTracesConfig() { @@ -87,11 +152,6 @@ public Optional endpoint() { return Optional.ofNullable(newTrace); } - @Override - public Optional legacyEndpoint() { - return Optional.ofNullable(legacyTrace); - } - @Override public Optional> headers() { return Optional.empty(); From a314efbd546a17a95b7edc475d14308cccedaa93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 21:43:11 +0000 Subject: [PATCH 91/94] Bump elasticsearch-opensource-components.version from 8.14.2 to 8.14.3 Bumps `elasticsearch-opensource-components.version` from 8.14.2 to 8.14.3. Updates `org.elasticsearch.client:elasticsearch-rest-client` from 8.14.2 to 8.14.3 - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.14.2...v8.14.3) Updates `co.elastic.clients:elasticsearch-java` from 8.14.2 to 8.14.3 - [Release notes](https://github.com/elastic/elasticsearch-java/releases) - [Changelog](https://github.com/elastic/elasticsearch-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch-java/compare/v8.14.2...v8.14.3) Updates `org.elasticsearch.client:elasticsearch-rest-client-sniffer` from 8.14.2 to 8.14.3 - [Release notes](https://github.com/elastic/elasticsearch/releases) - [Changelog](https://github.com/elastic/elasticsearch/blob/main/CHANGELOG.md) - [Commits](https://github.com/elastic/elasticsearch/compare/v8.14.2...v8.14.3) --- updated-dependencies: - dependency-name: org.elasticsearch.client:elasticsearch-rest-client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: co.elastic.clients:elasticsearch-java dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.elasticsearch.client:elasticsearch-rest-client-sniffer 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 53fdddfcf0dc0..8a1a65c85e587 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -102,7 +102,7 @@ 7.0.2.Final 2.4 8.0.0.Final - 8.14.2 + 8.14.3 2.2.21 2.2.5.Final 2.2.2.Final From e3fe818bffbe622e2201377e39339b9bc2836d79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 21:55:16 +0000 Subject: [PATCH 92/94] Bump de.flapdoodle.embed:de.flapdoodle.embed.mongo from 4.15.0 to 4.16.0 Bumps [de.flapdoodle.embed:de.flapdoodle.embed.mongo](https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo) from 4.15.0 to 4.16.0. - [Commits](https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo/compare/de.flapdoodle.embed.mongo-4.15.0...de.flapdoodle.embed.mongo-4.16.0) --- updated-dependencies: - dependency-name: de.flapdoodle.embed:de.flapdoodle.embed.mongo 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 53fdddfcf0dc0..2b7b7655e6cf2 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -171,7 +171,7 @@ 0.34.1 3.26.1 0.3.0 - 4.15.0 + 4.16.0 6.1.SP2 3.2.SP2 6.2 From 558697974b986abe73afa7b4633c1b6df65895ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 21:55:42 +0000 Subject: [PATCH 93/94] Bump com.gradle:quarkus-build-caching-extension from 1.3 to 1.4 Bumps [com.gradle:quarkus-build-caching-extension](https://github.com/gradle/develocity-build-config-samples) from 1.3 to 1.4. - [Release notes](https://github.com/gradle/develocity-build-config-samples/releases) - [Commits](https://github.com/gradle/develocity-build-config-samples/compare/v1.3...v1.4) --- updated-dependencies: - dependency-name: com.gradle:quarkus-build-caching-extension dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .mvn/extensions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 6af21e8dbd3be..4b4eceacd0068 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -12,7 +12,7 @@ com.gradle quarkus-build-caching-extension - 1.3 + 1.4 io.quarkus.develocity From cf67cc1e6a89af4e92fa32bfe1fb73dd23c66b82 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Fri, 12 Jul 2024 11:26:16 +0200 Subject: [PATCH 94/94] WebSockets Next: broadcasting fixes - intentionally ignore 'WebSocket is closed' failures - do not fail fast but collect all failures --- .../websockets/next/runtime/Endpoints.java | 2 +- .../next/runtime/WebSocketConnectionImpl.java | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/Endpoints.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/Endpoints.java index 26bf46d6421a8..12a2b327fa6b1 100644 --- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/Endpoints.java +++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/Endpoints.java @@ -291,7 +291,7 @@ private static boolean isSecurityFailure(Throwable throwable) { || throwable instanceof ForbiddenException; } - private static boolean isWebSocketIsClosedFailure(Throwable throwable, WebSocketConnectionBase connection) { + static boolean isWebSocketIsClosedFailure(Throwable throwable, WebSocketConnectionBase connection) { if (!connection.isClosed()) { return false; } diff --git a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectionImpl.java b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectionImpl.java index d1d4cad07638e..de23dd4779d78 100644 --- a/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectionImpl.java +++ b/extensions/websockets-next/runtime/src/main/java/io/quarkus/websockets/next/runtime/WebSocketConnectionImpl.java @@ -219,18 +219,26 @@ public Uni sendPong(Buffer data) { throw new UnsupportedOperationException(); } - private Uni doSend(BiFunction> function, M message) { + private Uni doSend(BiFunction> sendFunction, M message) { Set connections = connectionManager.getConnections(generatedEndpointClass); if (connections.isEmpty()) { return Uni.createFrom().voidItem(); } List> unis = new ArrayList<>(connections.size()); for (WebSocketConnection connection : connections) { - if (connection.isOpen() && (filter == null || filter.test(connection))) { - unis.add(function.apply(connection, message)); + if (connection.isOpen() + && (filter == null || filter.test(connection))) { + unis.add(sendFunction.apply(connection, message) + // Intentionally ignore 'WebSocket is closed' failures + // It might happen that the connection is closed in the mean time + .onFailure(t -> Endpoints.isWebSocketIsClosedFailure(t, (WebSocketConnectionBase) connection)) + .recoverWithNull()); } } - return unis.isEmpty() ? Uni.createFrom().voidItem() : Uni.join().all(unis).andFailFast().replaceWithVoid(); + if (unis.isEmpty()) { + return Uni.createFrom().voidItem(); + } + return Uni.join().all(unis).andCollectFailures().replaceWithVoid(); } }