From e60829822c74852d7f646be244f943620f79b862 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 20 Sep 2022 16:10:16 +0300 Subject: [PATCH 01/81] Make RESTEasy Reactive work with Optional temporal types Fixes: #28089 (cherry picked from commit becda000946494c427b9600c73b836425932b118) --- .../common/processor/EndpointIndexer.java | 13 ++++--- .../processor/ServerEndpointIndexer.java | 36 ++++++++++--------- .../test/simple/LocalDateTimeParamTest.java | 16 +++++++++ 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java index 41ee64f4ea399..6c6e9e5f22932 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java @@ -148,7 +148,7 @@ public abstract class EndpointIndexer SUPPORT_TEMPORAL_PARAMS = Set.of(INSTANT, LOCAL_DATE, LOCAL_TIME, LOCAL_DATE_TIME, + protected static final Set SUPPORT_TEMPORAL_PARAMS = Set.of(INSTANT, LOCAL_DATE, LOCAL_TIME, LOCAL_DATE_TIME, OFFSET_TIME, OFFSET_DATE_TIME, ZONED_DATE_TIME); @@ -1283,8 +1283,8 @@ && isContextType(paramType.asClassType())) { currentClassInfo, actualEndpointInfo, index); } - handleOptionalParam(existingConverters, errorLocation, hasRuntimeConverters, builder, elementType, - genericElementType); + handleOptionalParam(existingConverters, anns, errorLocation, hasRuntimeConverters, builder, elementType, + genericElementType, currentMethodInfo); } builder.setOptional(true); } else if (convertible) { @@ -1382,8 +1382,11 @@ protected void handleSortedSetParam(Map existingConverters, Stri boolean hasRuntimeConverters, PARAM builder, String elementType) { } - protected void handleOptionalParam(Map existingConverters, String errorLocation, - boolean hasRuntimeConverters, PARAM builder, String elementType, String genericElementType) { + protected void handleOptionalParam(Map existingConverters, + Map parameterAnnotations, + String errorLocation, + boolean hasRuntimeConverters, PARAM builder, String elementType, String genericElementType, + MethodInfo currentMethodInfo) { } protected void handleSetParam(Map existingConverters, String errorLocation, boolean hasRuntimeConverters, 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 d73ba9d913056..b64b2b9ddab57 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 @@ -335,8 +335,11 @@ protected void handleSortedSetParam(Map existingConverters, Stri builder.setConverter(new SortedSetConverter.SortedSetSupplier(converter)); } - protected void handleOptionalParam(Map existingConverters, String errorLocation, - boolean hasRuntimeConverters, ServerIndexedParameter builder, String elementType, String genericElementType) { + protected void handleOptionalParam(Map existingConverters, + Map parameterAnnotations, + String errorLocation, + boolean hasRuntimeConverters, ServerIndexedParameter builder, String elementType, String genericElementType, + MethodInfo currentMethodInfo) { ParameterConverterSupplier converter = null; if (genericElementType != null) { @@ -352,6 +355,9 @@ protected void handleOptionalParam(Map existingConverters, Strin converter = new SortedSetConverter.SortedSetSupplier(genericTypeConverter); builder.setSingle(false); } + } else if (SUPPORT_TEMPORAL_PARAMS.contains(DotName.createSimple(elementType))) { + converter = determineTemporalConverter(DotName.createSimple(elementType), parameterAnnotations, + currentMethodInfo); } if (converter == null) { @@ -395,6 +401,11 @@ protected String handleTrailingSlash(String path) { protected void handleTemporalParam(ServerIndexedParameter builder, DotName paramType, Map parameterAnnotations, MethodInfo currentMethodInfo) { + builder.setConverter(determineTemporalConverter(paramType, parameterAnnotations, currentMethodInfo)); + } + + private ParameterConverterSupplier determineTemporalConverter(DotName paramType, + Map parameterAnnotations, MethodInfo currentMethodInfo) { String format = null; String dateTimeFormatterProviderClassName = null; @@ -418,8 +429,7 @@ protected void handleTemporalParam(ServerIndexedParameter builder, DotName param "'java.time.Instant' types must not be annotated with '@DateFormat'", currentMethodInfo)); } - builder.setConverter(new InstantParamConverter.Supplier()); - return; + return new InstantParamConverter.Supplier(); } if ((format != null) && (dateTimeFormatterProviderClassName != null)) { @@ -432,23 +442,17 @@ protected void handleTemporalParam(ServerIndexedParameter builder, DotName param } if (LOCAL_DATE.equals(paramType)) { - builder.setConverter(new LocalDateParamConverter.Supplier(format, dateTimeFormatterProviderClassName)); - return; + return new LocalDateParamConverter.Supplier(format, dateTimeFormatterProviderClassName); } else if (LOCAL_DATE_TIME.equals(paramType)) { - builder.setConverter(new LocalDateTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName)); - return; + return new LocalDateTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName); } else if (LOCAL_TIME.equals(paramType)) { - builder.setConverter(new LocalTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName)); - return; + return new LocalTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName); } else if (OFFSET_DATE_TIME.equals(paramType)) { - builder.setConverter(new OffsetDateTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName)); - return; + return new OffsetDateTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName); } else if (OFFSET_TIME.equals(paramType)) { - builder.setConverter(new OffsetTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName)); - return; + return new OffsetTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName); } else if (ZONED_DATE_TIME.equals(paramType)) { - builder.setConverter(new ZonedDateTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName)); - return; + return new ZonedDateTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName); } throw new RuntimeException( diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/LocalDateTimeParamTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/LocalDateTimeParamTest.java index 89a0d5eba0be9..3e79191243710 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/LocalDateTimeParamTest.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/LocalDateTimeParamTest.java @@ -2,6 +2,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Optional; import javax.ws.rs.FormParam; import javax.ws.rs.GET; @@ -33,6 +34,15 @@ public void localDateTimeAsQueryParam() { .then().statusCode(200).body(Matchers.equalTo("hello#1984")); } + @Test + public void localDateTimeAsOptionalQueryParam() { + RestAssured.get("/hello/optional?date=1984-08-08T01:02:03") + .then().statusCode(200).body(Matchers.equalTo("hello#1984")); + + RestAssured.get("/hello/optional") + .then().statusCode(200).body(Matchers.equalTo("hello#2022")); + } + @Test public void localDateTimeAsPathParam() { RestAssured.get("/hello/1995-09-21 01:02:03") @@ -53,6 +63,12 @@ public String helloQuery(@RestQuery LocalDateTime date) { return "hello#" + date.getYear(); } + @Path("optional") + @GET + public String helloOptionalQuery(@RestQuery Optional date) { + return "hello#" + date.orElse(LocalDateTime.of(2022, 1, 1, 0, 0)).getYear(); + } + @GET @Path("{date}") public String helloPath(@RestPath @DateFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime date) { From b3a2ca82d162999a92f22a79d2be1b7865b76627 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 21 Sep 2022 11:30:39 +0300 Subject: [PATCH 02/81] Ensure that @QuarkusIntegrationTest does not leave dandling processes Fixes: #28114 (cherry picked from commit 25cf7c61602ffea08d9ae83565659ce7f9c29a04) --- .../main/java/io/quarkus/test/common/DefaultJarLauncher.java | 2 +- .../java/io/quarkus/test/common/DefaultNativeImageLauncher.java | 2 +- .../src/main/java/io/quarkus/test/common/LauncherUtil.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/DefaultJarLauncher.java b/test-framework/common/src/main/java/io/quarkus/test/common/DefaultJarLauncher.java index 10958e167ad83..c2bc57724a5d2 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/DefaultJarLauncher.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/DefaultJarLauncher.java @@ -165,6 +165,6 @@ public void includeAsSysProps(Map systemProps) { @Override public void close() { - quarkusProcess.destroy(); + LauncherUtil.destroyProcess(quarkusProcess); } } diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/DefaultNativeImageLauncher.java b/test-framework/common/src/main/java/io/quarkus/test/common/DefaultNativeImageLauncher.java index e61359e020238..33345980da115 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/DefaultNativeImageLauncher.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/DefaultNativeImageLauncher.java @@ -267,6 +267,6 @@ public void includeAsSysProps(Map systemProps) { @Override public void close() { - quarkusProcess.destroy(); + LauncherUtil.destroyProcess(quarkusProcess); } } diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/LauncherUtil.java b/test-framework/common/src/main/java/io/quarkus/test/common/LauncherUtil.java index d07cf27b0016a..76c2701fadded 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/LauncherUtil.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/LauncherUtil.java @@ -110,7 +110,7 @@ private static void ensureProcessIsAlive(Process quarkusProcess) { * Try to destroy the process normally a few times * and resort to forceful destruction if necessary */ - private static void destroyProcess(Process quarkusProcess) { + static void destroyProcess(Process quarkusProcess) { quarkusProcess.destroy(); int i = 0; while (i++ < 10) { From 309c8a38f25486dc4aa098966e178a048743fbc1 Mon Sep 17 00:00:00 2001 From: Adler Fleurant <2609856+AdlerFleurant@users.noreply.github.com> Date: Wed, 14 Sep 2022 16:07:10 -0400 Subject: [PATCH 03/81] Load main sourceSet only if present for project dependencies Treating Extension Dependencies as optional Fixes #27950 Signed-off-by: Adler Fleurant <2609856+AdlerFleurant@users.noreply.github.com> (cherry picked from commit 827f778c9ab71478b19ce230ac5a5265bfdba592) --- .../gradle-application-plugin/build.gradle | 4 + .../gradle/gradle-application-plugin/pom.xml | 12 ++ .../io/quarkus/gradle/QuarkusPluginTest.java | 76 +++++++++++- .../DeploymentClasspathBuilder.java | 10 +- .../gradle/tasks/ValidateExtensionTask.java | 27 ++--- devtools/gradle/gradle-model/build.gradle | 1 + .../ConditionalDependenciesEnabler.java | 108 ++++++++---------- .../tooling/dependency/DependencyUtils.java | 92 ++++++++------- 8 files changed, 203 insertions(+), 127 deletions(-) diff --git a/devtools/gradle/gradle-application-plugin/build.gradle b/devtools/gradle/gradle-application-plugin/build.gradle index 2f24773c33488..c7bba6e4c532f 100644 --- a/devtools/gradle/gradle-application-plugin/build.gradle +++ b/devtools/gradle/gradle-application-plugin/build.gradle @@ -33,3 +33,7 @@ pluginBundle { vcsUrl = 'https://github.com/quarkusio/quarkus' tags = ['quarkus', 'quarkusio', 'graalvm'] } + +test { + systemProperty 'kotlin_version', project.kotlin_version +} \ No newline at end of file diff --git a/devtools/gradle/gradle-application-plugin/pom.xml b/devtools/gradle/gradle-application-plugin/pom.xml index cefcb1fc66b3c..fa6bf4c85e9a5 100644 --- a/devtools/gradle/gradle-application-plugin/pom.xml +++ b/devtools/gradle/gradle-application-plugin/pom.xml @@ -54,6 +54,18 @@ quarkus-devmode-test-utils test + + org.jetbrains.kotlin + kotlin-gradle-plugin + ${kotlin.version} + test + + + org.checkerframework + checker-qual + + + diff --git a/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/QuarkusPluginTest.java b/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/QuarkusPluginTest.java index 0f29e3f3c2e51..000e71d0745b1 100644 --- a/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/QuarkusPluginTest.java +++ b/devtools/gradle/gradle-application-plugin/src/test/java/io/quarkus/gradle/QuarkusPluginTest.java @@ -1,10 +1,15 @@ package io.quarkus.gradle; import static org.assertj.core.api.Assertions.assertThat; +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +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.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -16,12 +21,18 @@ import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskContainer; import org.gradle.testfixtures.ProjectBuilder; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import io.quarkus.gradle.extension.QuarkusPluginExtension; public class QuarkusPluginTest { + @TempDir + Path testProjectDir; + @Test public void shouldCreateTasks() { Project project = ProjectBuilder.builder().build(); @@ -67,7 +78,7 @@ public void shouldMakeQuarkusDevAndQuarkusBuildDependOnClassesTask() { } @Test - public void shouldReturnMutlipleOutputSourceDirectories() { + public void shouldReturnMultipleOutputSourceDirectories() { Project project = ProjectBuilder.builder().build(); project.getPluginManager().apply(QuarkusPlugin.ID); project.getPluginManager().apply("java"); @@ -84,7 +95,68 @@ public void shouldReturnMutlipleOutputSourceDirectories() { } - private static final List getDependantProvidedTaskName(Task task) { + @Test + public void shouldNotFailOnProjectDependenciesWithoutMain() throws IOException { + var kotlinVersion = System.getProperty("kotlin_version", "1.7.10"); + var settingFile = testProjectDir.resolve("settings.gradle.kts"); + var mppProjectDir = testProjectDir.resolve("mpp"); + var quarkusProjectDir = testProjectDir.resolve("quarkus"); + var mppBuild = mppProjectDir.resolve("build.gradle.kts"); + var quarkusBuild = quarkusProjectDir.resolve("build.gradle.kts"); + Files.createDirectory(mppProjectDir); + Files.createDirectory(quarkusProjectDir); + Files.writeString(settingFile, + "rootProject.name = \"quarkus-mpp-sample\"\n" + + "\n" + + "include(\n" + + " \"mpp\",\n" + + " \"quarkus\"\n" + + ")"); + + Files.writeString(mppBuild, + "buildscript {\n" + + " repositories {\n" + + " mavenLocal()\n" + + " mavenCentral()\n" + + " }\n" + + " dependencies {\n" + + " classpath(\"org.jetbrains.kotlin:kotlin-gradle-plugin:" + kotlinVersion + "\")\n" + + " }\n" + + "}\n" + + "\n" + + "apply(plugin = \"org.jetbrains.kotlin.multiplatform\")\n" + + "\n" + + "repositories {\n" + + " mavenCentral()\n" + + "}\n" + + "\n" + + "configure{\n" + + " jvm()\n" + + "}"); + + Files.writeString(quarkusBuild, + "plugins {\n" + + " id(\"io.quarkus\")\n" + + "}\n" + + "\n" + + "repositories {\n" + + " mavenCentral()\n" + + "}\n" + + "\n" + + "dependencies {\n" + + " implementation(project(\":mpp\"))\n" + + "}"); + + BuildResult result = GradleRunner.create() + .withPluginClasspath() + .withProjectDir(testProjectDir.toFile()) + .withArguments("quarkusGenerateCode") + .build(); + + assertEquals(SUCCESS, result.task(":quarkus:quarkusGenerateCode").getOutcome()); + } + + private static List getDependantProvidedTaskName(Task task) { List dependantTaskNames = new ArrayList<>(); for (Object t : task.getDependsOn()) { try { diff --git a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/dependency/DeploymentClasspathBuilder.java b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/dependency/DeploymentClasspathBuilder.java index 5508c2e8e1a7f..1c01060da4c65 100644 --- a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/dependency/DeploymentClasspathBuilder.java +++ b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/dependency/DeploymentClasspathBuilder.java @@ -43,9 +43,7 @@ public void exportDeploymentClasspath(String configurationName) { dependencies); } else { DependencyUtils.requireDeploymentDependency(deploymentConfigurationName, extension, dependencies); - if (!alreadyProcessed.add(extension.getExtensionId())) { - continue; - } + alreadyProcessed.add(extension.getExtensionId()); } } }); @@ -73,9 +71,9 @@ private Set collectQuarkusExtensions(ResolvedDependency dep } Set extensions = new LinkedHashSet<>(); for (ResolvedArtifact moduleArtifact : dependency.getModuleArtifacts()) { - ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, moduleArtifact); - if (extension != null) { - extensions.add(extension); + var optionalExtension = DependencyUtils.getOptionalExtensionInfo(project, moduleArtifact); + if (optionalExtension.isPresent()) { + extensions.add(optionalExtension.get()); return extensions; } } diff --git a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ValidateExtensionTask.java b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ValidateExtensionTask.java index be96802daf78d..f2a1bb28cac0a 100644 --- a/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ValidateExtensionTask.java +++ b/devtools/gradle/gradle-extension-plugin/src/main/java/io/quarkus/extension/gradle/tasks/ValidateExtensionTask.java @@ -2,7 +2,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import javax.inject.Inject; @@ -18,7 +20,6 @@ import io.quarkus.bootstrap.model.AppArtifactKey; import io.quarkus.extension.gradle.QuarkusExtensionConfiguration; import io.quarkus.gradle.tooling.dependency.DependencyUtils; -import io.quarkus.gradle.tooling.dependency.ExtensionDependency; public class ValidateExtensionTask extends DefaultTask { @@ -62,13 +63,7 @@ public void validateExtension() { deploymentModuleKeys); deploymentModuleKeys.removeAll(existingDeploymentModuleKeys); - boolean hasErrors = false; - if (!invalidRuntimeArtifacts.isEmpty()) { - hasErrors = true; - } - if (!deploymentModuleKeys.isEmpty()) { - hasErrors = true; - } + boolean hasErrors = !invalidRuntimeArtifacts.isEmpty() || !deploymentModuleKeys.isEmpty(); if (hasErrors) { printValidationErrors(invalidRuntimeArtifacts, deploymentModuleKeys); @@ -76,15 +71,13 @@ public void validateExtension() { } private List collectRuntimeExtensionsDeploymentKeys(Set runtimeArtifacts) { - List runtimeExtensions = new ArrayList<>(); - for (ResolvedArtifact resolvedArtifact : runtimeArtifacts) { - ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(getProject(), resolvedArtifact); - if (extension != null) { - runtimeExtensions.add(new AppArtifactKey(extension.getDeploymentModule().getGroupId(), - extension.getDeploymentModule().getArtifactId())); - } - } - return runtimeExtensions; + return runtimeArtifacts.stream() + .map(resolvedArtifact -> DependencyUtils.getOptionalExtensionInfo(getProject(), resolvedArtifact)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(extension -> new AppArtifactKey(extension.getDeploymentModule().getGroupId(), + extension.getDeploymentModule().getArtifactId())) + .collect(Collectors.toList()); } private List findExtensionInConfiguration(Set deploymentArtifacts, diff --git a/devtools/gradle/gradle-model/build.gradle b/devtools/gradle/gradle-model/build.gradle index 15e648b26cae7..29e30073f46cf 100644 --- a/devtools/gradle/gradle-model/build.gradle +++ b/devtools/gradle/gradle-model/build.gradle @@ -3,6 +3,7 @@ dependencies { implementation "io.quarkus:quarkus-bootstrap-gradle-resolver:${version}" implementation "org.jetbrains.kotlin:kotlin-gradle-plugin-api:${kotlin_version}" testImplementation "io.quarkus:quarkus-devtools-testing:${version}" + testImplementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" } task sourcesJar(type: Jar, dependsOn: classes) { diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java index 6ab5650d7b5a9..7cd6180ccbfd6 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/dependency/ConditionalDependenciesEnabler.java @@ -6,7 +6,10 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -37,8 +40,7 @@ public class ConditionalDependenciesEnabler { private final Set existingArtifacts = new HashSet<>(); private final List unsatisfiedConditionalDeps = new ArrayList<>(); - public ConditionalDependenciesEnabler(Project project, LaunchMode mode, - Configuration platforms) { + public ConditionalDependenciesEnabler(Project project, LaunchMode mode, Configuration platforms) { this.project = project; this.enforcedPlatforms = platforms; @@ -59,7 +61,7 @@ public ConditionalDependenciesEnabler(Project project, LaunchMode mode, final Dependency conditionalDep = unsatisfiedConditionalDeps.get(i); // Try to resolve it with the latest evolved graph available if (resolveConditionalDependency(conditionalDep)) { - // Mark the resolution as a success so we know the graph evolved + // Mark the resolution as a success, so we know the graph has evolved satisfiedConditionalDeps = true; unsatisfiedConditionalDeps.remove(i); } else { @@ -88,23 +90,25 @@ private void reset() { } private void collectConditionalDependencies(Set runtimeArtifacts) { - // For every artifact in the dependency graph: - for (ResolvedArtifact artifact : runtimeArtifacts) { - // Add to master list of artifacts: - existingArtifacts.add(getKey(artifact)); - ExtensionDependency extension = DependencyUtils.getExtensionInfoOrNull(project, artifact); - // If this artifact represents an extension: - if (extension != null) { - // Add to master list of accepted extensions: - allExtensions.put(extension.getExtensionId(), extension); - for (Dependency conditionalDep : extension.getConditionalDependencies()) { - // If the dependency is not present yet in the graph, queue it for resolution later - if (!exists(conditionalDep)) { - queueConditionalDependency(extension, conditionalDep); - } - } - } - } + addToMasterList(runtimeArtifacts); + var artifactExtensions = getArtifactExtensions(runtimeArtifacts); + allExtensions.putAll(artifactExtensions); + artifactExtensions.forEach((ignored, extension) -> queueAbsentExtensionConditionalDependencies(extension)); + } + + private void addToMasterList(Set artifacts) { + artifacts.stream().map(ConditionalDependenciesEnabler::getKey).forEach(existingArtifacts::add); + } + + private Map getArtifactExtensions(Set runtimeArtifacts) { + return runtimeArtifacts.stream() + .flatMap(artifact -> DependencyUtils.getOptionalExtensionInfo(project, artifact).stream()) + .collect(Collectors.toMap(ExtensionDependency::getExtensionId, Function.identity())); + } + + private void queueAbsentExtensionConditionalDependencies(ExtensionDependency extension) { + extension.getConditionalDependencies().stream().filter(dep -> !exists(dep)) + .forEach(dep -> queueConditionalDependency(extension, dep)); } private boolean resolveConditionalDependency(Dependency conditionalDep) { @@ -112,51 +116,31 @@ private boolean resolveConditionalDependency(Dependency conditionalDep) { final Configuration conditionalDeps = createConditionalDependenciesConfiguration(project, conditionalDep); Set resolvedArtifacts = conditionalDeps.getResolvedConfiguration().getResolvedArtifacts(); - boolean satisfied = false; - // Resolved artifacts don't have great linking back to the original artifact, so I think - // this loop is trying to find the artifact that represents the original conditional - // dependency - for (ResolvedArtifact artifact : resolvedArtifacts) { - if (conditionalDep.getName().equals(artifact.getName()) - && conditionalDep.getVersion().equals(artifact.getModuleVersion().getId().getVersion()) - && artifact.getModuleVersion().getId().getGroup().equals(conditionalDep.getGroup())) { - // Once the dependency is found, reload the extension info from within - final ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact); - // Now check if this conditional dependency is resolved given the latest graph evolution - if (extensionDependency != null && (extensionDependency.getDependencyConditions().isEmpty() - || exist(extensionDependency.getDependencyConditions()))) { - satisfied = true; - enableConditionalDependency(extensionDependency.getExtensionId()); - break; - } - } + boolean isConditionalDependencyResolved = resolvedArtifacts.stream() + .filter(artifact -> areEquals(conditionalDep, artifact)) + .flatMap(artifact -> DependencyUtils.getOptionalExtensionInfo(project, artifact).stream()) + .filter(extension -> extension.getDependencyConditions().isEmpty() + || exist(extension.getDependencyConditions())) + .findFirst().map(extension -> { + enableConditionalDependency(extension.getExtensionId()); + return true; + }).orElse(false); + + if (isConditionalDependencyResolved) { + addToMasterList(resolvedArtifacts); + var artifactExtensions = getArtifactExtensions(resolvedArtifacts); + artifactExtensions.forEach((id, extension) -> extension.setConditional(true)); + allExtensions.putAll(artifactExtensions); + artifactExtensions.forEach((ignored, extension) -> queueAbsentExtensionConditionalDependencies(extension)); } - // No resolution (yet); give up. - if (!satisfied) { - return false; - } + return isConditionalDependencyResolved; + } - // The conditional dependency resolved! Let's now add all of /its/ dependencies - for (ResolvedArtifact artifact : resolvedArtifacts) { - // First add the artifact to the master list - existingArtifacts.add(getKey(artifact)); - ExtensionDependency extensionDependency = DependencyUtils.getExtensionInfoOrNull(project, artifact); - if (extensionDependency == null) { - continue; - } - // If this artifact represents an extension, mark this one as a conditional extension - extensionDependency.setConditional(true); - // Add to the master list of accepted extensions - allExtensions.put(extensionDependency.getExtensionId(), extensionDependency); - for (Dependency cd : extensionDependency.getConditionalDependencies()) { - // Add any unsatisfied/unresolved conditional dependencies of this dependency to the queue - if (!exists(cd)) { - queueConditionalDependency(extensionDependency, cd); - } - } - } - return satisfied; + private boolean areEquals(Dependency dependency, ResolvedArtifact artifact) { + return dependency.getName().equals(artifact.getName()) + && Objects.equals(dependency.getVersion(), artifact.getModuleVersion().getId().getVersion()) + && artifact.getModuleVersion().getId().getGroup().equals(dependency.getGroup()); } private void queueConditionalDependency(ExtensionDependency extension, Dependency conditionalDep) { diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java index 5d0f37b1793e3..d68064a42277b 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/dependency/DependencyUtils.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.Properties; import org.gradle.api.GradleException; @@ -73,48 +74,59 @@ public static String asDependencyNotation(ArtifactCoords artifactCoords) { return String.join(":", artifactCoords.getGroupId(), artifactCoords.getArtifactId(), artifactCoords.getVersion()); } - public static ExtensionDependency getExtensionInfoOrNull(Project project, ResolvedArtifact artifact) { - ModuleVersionIdentifier artifactId = artifact.getModuleVersion().getId(); - File artifactFile = artifact.getFile(); - - if (artifact.getId().getComponentIdentifier() instanceof ProjectComponentIdentifier) { - final Project projectDep = project.getRootProject().findProject( - ((ProjectComponentIdentifier) artifact.getId().getComponentIdentifier()).getProjectPath()); - SourceSetContainer sourceSets = projectDep == null ? null - : projectDep.getExtensions().findByType(SourceSetContainer.class); - if (sourceSets != null) { - SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); - File resourcesDir = mainSourceSet.getOutput().getResourcesDir(); - Path descriptorPath = resourcesDir.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH); - if (Files.exists(descriptorPath)) { - return loadExtensionInfo(project, descriptorPath, artifactId, projectDep); - } - } - } + public static Optional getOptionalExtensionInfo(Project project, ResolvedArtifact artifact) { + return loadExtensionDependencyFromProject(artifact, project) + .or(() -> loadExtensionDependencyFromDir(artifact, project)) + .or(() -> loadExtensionDependencyFromJar(artifact, project)); + } - if (!artifactFile.exists()) { - return null; - } - if (artifactFile.isDirectory()) { - Path descriptorPath = artifactFile.toPath().resolve(BootstrapConstants.DESCRIPTOR_PATH); - if (Files.exists(descriptorPath)) { - return loadExtensionInfo(project, descriptorPath, artifactId, null); - } - } else if (ArtifactCoords.TYPE_JAR.equals(artifact.getExtension())) { - try (FileSystem artifactFs = ZipUtils.newFileSystem(artifactFile.toPath())) { - Path descriptorPath = artifactFs.getPath(BootstrapConstants.DESCRIPTOR_PATH); - if (Files.exists(descriptorPath)) { - return loadExtensionInfo(project, descriptorPath, artifactId, null); - } - } catch (IOException e) { - throw new GradleException("Failed to read " + artifactFile, e); - } - } - return null; + private static Optional loadExtensionDependencyFromProject(ResolvedArtifact artifact, + Project project) { + Optional projectDep = Optional.of(artifact.getId().getComponentIdentifier()) + .filter(ProjectComponentIdentifier.class::isInstance) + .map(ProjectComponentIdentifier.class::cast) + .map(ProjectComponentIdentifier::getProjectPath) + .map(projectPath -> project.getRootProject().findProject(projectPath)); + + return projectDep + .map(Project::getExtensions) + .map(container -> container.findByType(SourceSetContainer.class)) + .map(container -> container.findByName(SourceSet.MAIN_SOURCE_SET_NAME)) + .map(it -> it.getOutput().getResourcesDir()) + .map(File::toPath) + .flatMap(resourceDir -> loadOptionalExtensionInfo(project, resourceDir, artifact.getModuleVersion().getId(), + projectDep.get())); + } + + private static Optional loadExtensionDependencyFromDir(ResolvedArtifact artifact, Project project) { + return Optional.of(artifact.getFile().toPath()).filter(Files::exists) + .flatMap(path -> loadOptionalExtensionInfo(project, path, artifact.getModuleVersion().getId(), null)); + } + + private static Optional loadExtensionDependencyFromJar(ResolvedArtifact artifact, Project project) { + return Optional.of(artifact) + .filter(it -> ArtifactCoords.TYPE_JAR.equals(it.getExtension())) + .filter(it -> Files.exists(it.getFile().toPath())) + .flatMap(it -> { + try (FileSystem artifactFs = ZipUtils.newFileSystem(it.getFile().toPath())) { + return loadOptionalExtensionInfo(project, artifactFs.getPath(""), artifact.getModuleVersion().getId(), + null); + } catch (IOException e) { + throw new GradleException("Failed to read " + it.getFile(), e); + } + }); + } + + private static Optional loadOptionalExtensionInfo(Project project, Path resourcePath, + ModuleVersionIdentifier extensionId, Project extensionProject) { + return Optional.of(resourcePath) + .map(path -> path.resolve(BootstrapConstants.DESCRIPTOR_PATH)) + .filter(Files::exists) + .map(descriptorPath -> loadExtensionInfo(project, descriptorPath, extensionId, extensionProject)); } private static ExtensionDependency loadExtensionInfo(Project project, Path descriptorPath, - ModuleVersionIdentifier exentionId, Project extensionProject) { + ModuleVersionIdentifier extensionId, Project extensionProject) { final Properties extensionProperties = new Properties(); try (BufferedReader reader = Files.newBufferedReader(descriptorPath)) { extensionProperties.load(reader); @@ -138,10 +150,10 @@ private static ExtensionDependency loadExtensionInfo(Project project, Path descr final ArtifactKey[] constraints = BootstrapUtils .parseDependencyCondition(extensionProperties.getProperty(BootstrapConstants.DEPENDENCY_CONDITION)); if (extensionProject != null) { - return new LocalExtensionDependency(extensionProject, exentionId, deploymentModule, conditionalDependencies, + return new LocalExtensionDependency(extensionProject, extensionId, deploymentModule, conditionalDependencies, constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); } - return new ExtensionDependency(exentionId, deploymentModule, conditionalDependencies, + return new ExtensionDependency(extensionId, deploymentModule, conditionalDependencies, constraints == null ? Collections.emptyList() : Arrays.asList(constraints)); } From f58c255253319bf84deede13d6d1386eb7361ea2 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 21 Sep 2022 12:50:06 +0200 Subject: [PATCH 04/81] Fix Agroal CDI setup (cherry picked from commit bd01e6a4bd69173367904db0f9649740433c6834) --- .../io/quarkus/agroal/deployment/AgroalProcessor.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java index e90502dc4dfe2..482f95f8cf1f5 100644 --- a/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java +++ b/extensions/agroal/deployment/src/main/java/io/quarkus/agroal/deployment/AgroalProcessor.java @@ -23,11 +23,13 @@ import io.quarkus.agroal.runtime.DataSourceSupport; import io.quarkus.agroal.runtime.DataSources; import io.quarkus.agroal.runtime.DataSourcesJdbcBuildTimeConfig; +import io.quarkus.agroal.runtime.JdbcDriver; import io.quarkus.agroal.runtime.TransactionIntegration; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.agroal.spi.JdbcDriverBuildItem; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.arc.deployment.UnremovableBeanBuildItem; import io.quarkus.arc.processor.DotNames; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.datasource.common.runtime.DataSourceUtil; @@ -197,7 +199,10 @@ void generateDataSourceSupportBean(AgroalRecorder recorder, SslNativeConfigBuildItem sslNativeConfig, Capabilities capabilities, BuildProducer additionalBeans, - BuildProducer syntheticBeanBuildItemBuildProducer) { + BuildProducer syntheticBeanBuildItemBuildProducer, + BuildProducer unremovableBeans) { + additionalBeans.produce(new AdditionalBeanBuildItem(JdbcDriver.class)); + if (aggregatedBuildTimeConfigBuildItems.isEmpty()) { // No datasource has been configured so bail out return; @@ -209,8 +214,8 @@ void generateDataSourceSupportBean(AgroalRecorder recorder, // add the @DataSource class otherwise it won't be registered as a qualifier additionalBeans.produce(AdditionalBeanBuildItem.builder().addBeanClass(DataSource.class).build()); - // add implementations of AgroalPoolInterceptor - additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(AgroalPoolInterceptor.class)); + // make AgroalPoolInterceptor beans unremovable, users still have to make them beans + unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(AgroalPoolInterceptor.class)); // create the DataSourceSupport bean that DataSourceProducer uses as a dependency DataSourceSupport dataSourceSupport = getDataSourceSupport(aggregatedBuildTimeConfigBuildItems, sslNativeConfig, From 457ef08d8dc3c796c09b9768affc779e2b2455a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Wed, 21 Sep 2022 16:30:06 +0200 Subject: [PATCH 05/81] Docs - do not collapse description on copy button click (cherry picked from commit 768a685bff7a596c8cfae93d44f0538135e3e8b4) --- docs/src/main/asciidoc/javascript/config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/main/asciidoc/javascript/config.js b/docs/src/main/asciidoc/javascript/config.js index 22e453b5b853c..898d7643f61cf 100644 --- a/docs/src/main/asciidoc/javascript/config.js +++ b/docs/src/main/asciidoc/javascript/config.js @@ -296,6 +296,11 @@ function makeCollapsibleHandler(descDiv, td, row, return; } + // don't collapse if the target is button with attribute "do-not-collapse" + if( (target.localName == 'button' && target.hasAttribute("do-not-collapse"))) { + return; + } + var isCollapsed = descDiv.classList.contains('description-collapsed'); if( isCollapsed ) { collapsibleSpan.childNodes.item(0).nodeValue = 'Show less'; From 1feb5d083aac7623b4983206387ada1c681c4ac2 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 21 Sep 2022 14:56:17 +0100 Subject: [PATCH 06/81] Add more OIDC debug messages and update the refresh token test (cherry picked from commit 050935a89fb20fde16bec501c835ad620d6c64da) --- .../runtime/CodeAuthenticationMechanism.java | 44 ++++++++++++------- .../it/keycloak/CustomTenantResolver.java | 4 ++ .../io/quarkus/it/keycloak/TenantRefresh.java | 20 +++++++++ .../src/main/resources/application.properties | 10 ++++- .../io/quarkus/it/keycloak/CodeFlowTest.java | 28 ++++++------ 5 files changed, 74 insertions(+), 32 deletions(-) create mode 100644 integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantRefresh.java diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java index 8eee36c73e17f..84576f9348c5f 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java @@ -130,6 +130,7 @@ private Uni processRedirectFromOidc(RoutingContext context, Oi OidcUtils.removeCookie(context, oidcTenantConfig, stateCookie.getName()); if (!isStateValid(requestParams, parsedStateCookieValue[0])) { + LOG.error("State verification has failed, completing the code flow with HTTP status 401"); return Uni.createFrom().failure(new AuthenticationCompletionException()); } @@ -164,10 +165,12 @@ public Uni apply(TenantConfigContext tenantContext) { LOG.debugf("Error URI: %s", finalErrorUri); return Uni.createFrom().failure(new AuthenticationRedirectException(finalErrorUri)); } else { + LOG.error( + "Authentication has failed but no error handler is found, completing the code flow with HTTP status 401"); return Uni.createFrom().failure(new AuthenticationCompletionException()); } } else { - LOG.debug("State cookie is present but neither 'code' nor 'error' query parameter is returned"); + LOG.error("State cookie is present but neither 'code' nor 'error' query parameter is returned"); return Uni.createFrom().failure(new AuthenticationCompletionException()); } @@ -241,7 +244,7 @@ public Uni apply(SecurityIdentity identity) { public Uni apply(Throwable t) { if (t instanceof AuthenticationRedirectException) { LOG.debug("Redirecting after the reauthentication"); - throw (AuthenticationRedirectException) t; + return Uni.createFrom().failure((AuthenticationRedirectException) t); } if (!(t instanceof TokenAutoRefreshException)) { @@ -250,17 +253,20 @@ public Uni apply(Throwable t) { .hasErrorCode(ErrorCodes.EXPIRED); if (!expired) { - LOG.debugf("Authentication failure: %s", t.getCause()); - throw new AuthenticationCompletionException(t.getCause()); + LOG.errorf("ID token verification failure: %s", t.getCause()); + return Uni.createFrom() + .failure(new AuthenticationCompletionException(t.getCause())); } if (session.getRefreshToken() == null) { LOG.debug( "Token has expired, token refresh is not possible because the refresh token is null"); - throw new AuthenticationFailedException(t.getCause()); + return Uni.createFrom() + .failure(new AuthenticationFailedException(t.getCause())); } if (!configContext.oidcConfig.token.refreshExpired) { LOG.debug("Token has expired, token refresh is not allowed"); - throw new AuthenticationFailedException(t.getCause()); + return Uni.createFrom() + .failure(new AuthenticationFailedException(t.getCause())); } LOG.debug("Token has expired, trying to refresh it"); return refreshSecurityIdentity(configContext, @@ -495,7 +501,8 @@ private PkceStateBean createPkceStateBean(TenantConfigContext configContext) { String codeChallenge = encoder.encodeToString(codeChallengeBytes); bean.setCodeChallenge(codeChallenge); } catch (Exception ex) { - throw new AuthenticationFailedException(ex); + LOG.errorf("Code challenge creation failure: %s", ex.getMessage()); + throw new AuthenticationCompletionException(ex); } return bean; @@ -541,15 +548,15 @@ private Uni performCodeFlow(IdentityProviderManager identityPr public Uni apply(final AuthorizationCodeTokens tokens, final Throwable tOuter) { if (tOuter != null) { - LOG.debugf("Exception during the code to token exchange: %s", tOuter.getMessage()); + LOG.errorf("Exception during the code to token exchange: %s", tOuter.getMessage()); return Uni.createFrom().failure(new AuthenticationCompletionException(tOuter)); } boolean internalIdToken = !configContext.oidcConfig.authentication.isIdTokenRequired().orElse(true); if (tokens.getIdToken() == null) { if (!internalIdToken) { - return Uni.createFrom() - .failure(new AuthenticationCompletionException("ID Token is not available")); + LOG.errorf("ID token is not available in the authorization code grant response"); + return Uni.createFrom().failure(new AuthenticationCompletionException()); } else { tokens.setIdToken(generateInternalIdToken(configContext.oidcConfig, null)); } @@ -616,7 +623,7 @@ public Throwable apply(Throwable tInner) { LOG.debugf("Starting the final redirect"); return tInner; } - LOG.debugf("ID token verification has failed: %s", tInner.getMessage()); + LOG.errorf("ID token verification has failed: %s", tInner.getMessage()); return new AuthenticationCompletionException(tInner); } }); @@ -638,9 +645,9 @@ private CodeAuthenticationStateBean getCodeAuthenticationBean(String[] parsedSta try { json = OidcUtils.decryptJson(parsedStateCookieValue[1], configContext.getPkceSecretKey()); } catch (Exception ex) { - LOG.tracef("State cookie value can not be decrypted for the %s tenant", + LOG.errorf("State cookie value can not be decrypted for the %s tenant", configContext.oidcConfig.tenantId.get()); - throw new AuthenticationFailedException(ex); + throw new AuthenticationCompletionException(ex); } bean.setRestorePath(json.getString(STATE_COOKIE_RESTORE_PATH)); bean.setCodeVerifier(json.getString(OidcConstants.PKCE_CODE_VERIFIER)); @@ -672,7 +679,7 @@ public Uni apply(Void t) { JsonObject idTokenJson = OidcUtils.decodeJwtContent(idToken); if (!idTokenJson.containsKey("exp") || !idTokenJson.containsKey("iat")) { - LOG.debug("ID Token is required to contain 'exp' and 'iat' claims"); + LOG.error("ID Token is required to contain 'exp' and 'iat' claims"); throw new AuthenticationCompletionException(); } long maxAge = idTokenJson.getLong("exp") - idTokenJson.getLong("iat"); @@ -779,7 +786,8 @@ private String encodeExtraStateValue(CodeAuthenticationStateBean extraStateValue try { return OidcUtils.encryptJson(json, configContext.getPkceSecretKey()); } catch (Exception ex) { - throw new AuthenticationFailedException(ex); + LOG.errorf("State containing the code verifier can not be encrypted: %s", ex.getMessage()); + throw new AuthenticationCompletionException(ex); } } else { return extraStateValue.getRestorePath(); @@ -857,7 +865,7 @@ private Uni refreshSecurityIdentity(TenantConfigContext config @Override public Uni apply(final AuthorizationCodeTokens tokens, final Throwable t) { if (t != null) { - LOG.errorf("ID token refresh has failed: %s", t.getMessage()); + LOG.debugf("ID token refresh has failed: %s", t.getMessage()); if (autoRefresh) { LOG.debug("Using the current SecurityIdentity since the ID token is still valid"); return Uni.createFrom().item(((TokenAutoRefreshException) t).getSecurityIdentity()); @@ -967,7 +975,9 @@ private Uni buildLogoutRedirectUriUni(RoutingContext context, TenantConfig .map(new Function() { @Override public Void apply(Void t) { - throw new AuthenticationRedirectException(buildLogoutRedirectUri(configContext, idToken, context)); + String logoutUri = buildLogoutRedirectUri(configContext, idToken, context); + LOG.debugf("Logout uri: %s"); + throw new AuthenticationRedirectException(logoutUri); } }); } diff --git a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/CustomTenantResolver.java b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/CustomTenantResolver.java index 1186847417960..44b184c5430e0 100644 --- a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/CustomTenantResolver.java +++ b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/CustomTenantResolver.java @@ -52,6 +52,10 @@ public String resolve(RoutingContext context) { return "tenant-autorefresh"; } + if (path.contains("tenant-refresh")) { + return "tenant-refresh"; + } + if (path.contains("tenant-https")) { if (context.getCookie("q_session_tenant-https_test") != null) { context.put("reauthenticated", "true"); diff --git a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantRefresh.java b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantRefresh.java new file mode 100644 index 0000000000000..aae7df4458b02 --- /dev/null +++ b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/TenantRefresh.java @@ -0,0 +1,20 @@ +package io.quarkus.it.keycloak; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import io.quarkus.security.Authenticated; +import io.vertx.ext.web.RoutingContext; + +@Path("/tenant-refresh") +public class TenantRefresh { + @Inject + RoutingContext context; + + @Authenticated + @GET + public String getTenantRefresh() { + return "Tenant Refresh, refreshed: " + (context.get("refresh_token_grant_response") != null); + } +} diff --git a/integration-tests/oidc-code-flow/src/main/resources/application.properties b/integration-tests/oidc-code-flow/src/main/resources/application.properties index 69b81d6eccce7..5b00192e9c8b6 100644 --- a/integration-tests/oidc-code-flow/src/main/resources/application.properties +++ b/integration-tests/oidc-code-flow/src/main/resources/application.properties @@ -74,10 +74,16 @@ quarkus.oidc.tenant-logout.client-id=quarkus-app quarkus.oidc.tenant-logout.credentials.secret=secret quarkus.oidc.tenant-logout.application-type=web-app quarkus.oidc.tenant-logout.authentication.cookie-path=/tenant-logout -quarkus.oidc.tenant-logout.authentication.session-age-extension=2M quarkus.oidc.tenant-logout.logout.path=/tenant-logout/logout quarkus.oidc.tenant-logout.logout.post-logout-path=/tenant-logout/post-logout -quarkus.oidc.tenant-logout.token.refresh-expired=true + +quarkus.oidc.tenant-refresh.auth-server-url=${keycloak.url}/realms/logout-realm +quarkus.oidc.tenant-refresh.client-id=quarkus-app +quarkus.oidc.tenant-refresh.credentials.secret=secret +quarkus.oidc.tenant-refresh.application-type=web-app +quarkus.oidc.tenant-refresh.authentication.cookie-path=/tenant-refresh +quarkus.oidc.tenant-refresh.authentication.session-age-extension=2M +quarkus.oidc.tenant-refresh.token.refresh-expired=true quarkus.oidc.tenant-autorefresh.auth-server-url=${keycloak.url}/realms/logout-realm quarkus.oidc.tenant-autorefresh.client-id=quarkus-app diff --git a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java index a574802dbb855..ede67aad0d2bb 100644 --- a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java +++ b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java @@ -429,40 +429,41 @@ public void testRPInitiatedLogout() throws IOException { @Test public void testTokenRefresh() throws IOException { try (final WebClient webClient = createWebClient()) { - HtmlPage page = webClient.getPage("http://localhost:8081/tenant-logout"); + HtmlPage page = webClient.getPage("http://localhost:8081/tenant-refresh"); assertEquals("Sign in to logout-realm", page.getTitleText()); HtmlForm loginForm = page.getForms().get(0); loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("Tenant Logout, refreshed: false", page.asText()); + assertEquals("Tenant Refresh, refreshed: false", page.asText()); - Cookie sessionCookie = getSessionCookie(webClient, "tenant-logout"); + Cookie sessionCookie = getSessionCookie(webClient, "tenant-refresh"); assertNotNull(sessionCookie); String idToken = getIdToken(sessionCookie); //wait now so that we reach the ID token timeout await().atMost(10, TimeUnit.SECONDS) - .pollInterval(Duration.ofSeconds(1)) + .pollInterval(Duration.ofSeconds(2)) .until(new Callable() { @Override public Boolean call() throws Exception { webClient.getOptions().setRedirectEnabled(false); WebResponse webResponse = webClient - .loadWebResponse(new WebRequest(URI.create("http://localhost:8081/tenant-logout").toURL())); + .loadWebResponse( + new WebRequest(URI.create("http://localhost:8081/tenant-refresh").toURL())); assertEquals(200, webResponse.getStatusCode()); // Should not redirect to OP but silently refresh token - Cookie newSessionCookie = getSessionCookie(webClient, "tenant-logout"); + Cookie newSessionCookie = getSessionCookie(webClient, "tenant-refresh"); assertNotNull(newSessionCookie); - return webResponse.getContentAsString().equals("Tenant Logout, refreshed: true") + return webResponse.getContentAsString().equals("Tenant Refresh, refreshed: true") && !idToken.equals(getIdToken(newSessionCookie)); } }); // local session refreshed and still valid - page = webClient.getPage("http://localhost:8081/tenant-logout"); - assertEquals("Tenant Logout, refreshed: false", page.asText()); - assertNotNull(getSessionCookie(webClient, "tenant-logout")); + page = webClient.getPage("http://localhost:8081/tenant-refresh"); + assertEquals("Tenant Refresh, refreshed: false", page.asText()); + assertNotNull(getSessionCookie(webClient, "tenant-refresh")); //wait now so that we reach the refresh timeout await().atMost(20, TimeUnit.SECONDS) @@ -472,12 +473,13 @@ public Boolean call() throws Exception { public Boolean call() throws Exception { webClient.getOptions().setRedirectEnabled(false); WebResponse webResponse = webClient - .loadWebResponse(new WebRequest(URI.create("http://localhost:8081/tenant-logout").toURL())); + .loadWebResponse( + new WebRequest(URI.create("http://localhost:8081/tenant-refresh").toURL())); // Should redirect to login page given that session is now expired at the OP int statusCode = webResponse.getStatusCode(); if (statusCode == 302) { - assertNull(getSessionCookie(webClient, "tenant-logout")); + assertNull(getSessionCookie(webClient, "tenant-refresh")); return true; } @@ -489,7 +491,7 @@ public Boolean call() throws Exception { webClient.getOptions().setRedirectEnabled(true); webClient.getCookieManager().clearCookies(); - page = webClient.getPage("http://localhost:8081/tenant-logout"); + page = webClient.getPage("http://localhost:8081/tenant-refresh"); assertNull(getSessionCookie(webClient, "tenant-logout")); assertEquals("Sign in to logout-realm", page.getTitleText()); webClient.getCookieManager().clearCookies(); From 1e2d4d077274ce70f045dffa2137590f69faa371 Mon Sep 17 00:00:00 2001 From: damienb-opt <36661255+damienb-opt@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:26:20 +0200 Subject: [PATCH 07/81] Provide details about failing MetaInfJandexReader reader.getIndexVersion() can throw UnsupportedVersion too Example: Build step io.quarkus.deployment.index.ApplicationArchiveBuildStep#build threw an exception: org.jboss.jandex.UnsupportedVersion: Can't read index version 11; this IndexReader only supports index versions 2-3,6-10 at org.jboss.jandex.IndexReader.initReader(IndexReader.java:87) at org.jboss.jandex.IndexReader.readVersion(IndexReader.java:140) at org.jboss.jandex.IndexReader.getIndexVersion(IndexReader.java:127) at io.quarkus.deployment.index.IndexingUtil$MetaInfJandexReader.apply(IndexingUtil.java:244) at io.quarkus.deployment.index.IndexingUtil$MetaInfJandexReader.apply(IndexingUtil.java:230) at io.quarkus.paths.PathTreeVisit.process(PathTreeVisit.java:40) (cherry picked from commit cb529576f2273c2079442cdfdea1dda90af817e1) --- .../io/quarkus/deployment/index/IndexingUtil.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java index 2030fca7c05cb..e00452508859e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/index/IndexingUtil.java @@ -241,13 +241,13 @@ public Index apply(PathVisit visit) { } try (InputStream in = Files.newInputStream(visit.getPath())) { IndexReader reader = new IndexReader(in); - if (reader.getIndexVersion() < REQUIRED_INDEX_VERSION) { - log.warnf( - "Re-indexing %s - at least Jandex 2.1 must be used to index an application dependency", - visit.getPath()); - return null; - } try { + if (reader.getIndexVersion() < REQUIRED_INDEX_VERSION) { + log.warnf( + "Re-indexing %s - at least Jandex 2.1 must be used to index an application dependency", + visit.getPath()); + return null; + } return reader.read(); } catch (UnsupportedVersion e) { throw new UnsupportedVersion( From df50a804696fb1009ae391ad9574425895741178 Mon Sep 17 00:00:00 2001 From: Yubao Liu Date: Tue, 20 Sep 2022 23:48:43 +0800 Subject: [PATCH 08/81] Support "INHERIT" for quarkus.jib.jvm-entrypoint and quarkus.jib.native-entrypoint jib-maven-plugin and jib-gradle-plugin both support this option to inherit entrypoint from base image, so make Quarkus learn it. Fixed https://github.com/quarkusio/quarkus/issues/28071 (cherry picked from commit 74290e4aa0571241411792849ead7843989cd3a7) --- .../image/jib/deployment/JibConfig.java | 8 +- .../image/jib/deployment/JibProcessor.java | 14 +++ .../invoker.properties | 1 + .../it/container-build-jib-inherit/pom.xml | 115 ++++++++++++++++++ .../src/main/java/org/acme/Hello.java | 16 +++ .../src/main/resources/application.properties | 8 ++ .../container-build-jib-inherit/verify.groovy | 37 ++++++ 7 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/invoker.properties create mode 100644 integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/pom.xml create mode 100644 integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/src/main/java/org/acme/Hello.java create mode 100644 integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/src/main/resources/application.properties create mode 100644 integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/verify.groovy diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibConfig.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibConfig.java index 16ca8f2a036c7..5d6e88167a7b2 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibConfig.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibConfig.java @@ -54,6 +54,8 @@ public class JibConfig { * If this is set, then it will be used as the entry point of the container image. * There are a few things to be aware of when creating an entry point *
    + *
  • Entrypoint "INHERIT" means to inherit entrypoint from base image, {@code jvmArguments} field is used for + * arguments
  • *
  • A valid entrypoint is jar package specific (see {@code quarkus.package.type})
  • *
  • A valid entrypoint depends on the location of both the launching scripts and the application jar file. To that * end it's helpful to remember that when {@code fast-jar} packaging is used (the default), all necessary application @@ -62,7 +64,7 @@ public class JibConfig { * jars * are unpacked under the {@code /app} directory * and that directory is used as the working directory.
  • - *
  • Even if the {@code jvmArguments} field is set, it is ignored completely
  • + *
  • Even if the {@code jvmArguments} field is set, it is ignored completely unless entrypoint is "INHERIT"
  • *
* * When this is not set, a proper default entrypoint will be constructed. @@ -77,10 +79,12 @@ public class JibConfig { * If this is set, then it will be used as the entry point of the container image. * There are a few things to be aware of when creating an entry point *
    + *
  • Entrypoint "INHERIT" means to inherit entrypoint from base image, {@code nativeArguments} field is used for + * arguments
  • *
  • A valid entrypoint depends on the location of both the launching scripts and the native binary file. To that end * it's helpful to remember that the native application is added to the {@code /work} directory and that and the same * directory is also used as the working directory
  • - *
  • Even if the {@code nativeArguments} field is set, it is ignored completely
  • + *
  • Even if the {@code nativeArguments} field is set, it is ignored completely unless entrypoint is "INHERIT"
  • *
* * When this is not set, a proper default entrypoint will be constructed. diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java index 41a20eff3c8f6..116905e2ff1fc 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java @@ -562,6 +562,8 @@ private JibContainerBuilder createContainerBuilderFromFastJar(String baseJvmImag .setEnvironment(getEnvironmentVariables(jibConfig)) .setLabels(allLabels(jibConfig, containerImageConfig, containerImageLabels)); + mayInheritEntrypoint(jibContainerBuilder, entrypoint, jibConfig.jvmArguments); + if (jibConfig.useCurrentTimestamp) { jibContainerBuilder.setCreationTime(now); } @@ -596,6 +598,15 @@ public JibContainerBuilder addLayer(JibContainerBuilder jibContainerBuilder, Lis return jibContainerBuilder.addFileEntriesLayer(layerConfigurationBuilder.build()); } + private void mayInheritEntrypoint(JibContainerBuilder jibContainerBuilder, List entrypoint, + List arguments) { + if (entrypoint.size() == 1 && "INHERIT".equals(entrypoint.get(0))) { + jibContainerBuilder + .setEntrypoint((List) null) + .setProgramArguments(arguments); + } + } + private List determineEffectiveJvmArguments(JibConfig jibConfig, Optional appCDSResult) { List effectiveJvmArguments = new ArrayList<>(jibConfig.jvmArguments); jibConfig.jvmAdditionalArguments.ifPresent(effectiveJvmArguments::addAll); @@ -666,6 +677,7 @@ private JibContainerBuilder createContainerBuilderFromLegacyJar(String baseJvmIm if (jibConfig.jvmEntrypoint.isPresent()) { jibContainerBuilder.setEntrypoint(jibConfig.jvmEntrypoint.get()); + mayInheritEntrypoint(jibContainerBuilder, jibConfig.jvmEntrypoint.get(), jibConfig.jvmArguments); } return jibContainerBuilder; @@ -702,6 +714,8 @@ private JibContainerBuilder createContainerBuilderFromNative(JibConfig jibConfig .setEnvironment(getEnvironmentVariables(jibConfig)) .setLabels(allLabels(jibConfig, containerImageConfig, containerImageLabels)); + mayInheritEntrypoint(jibContainerBuilder, entrypoint, jibConfig.nativeArguments.orElse(null)); + if (jibConfig.useCurrentTimestamp) { jibContainerBuilder.setCreationTime(Instant.now()); } diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/invoker.properties b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/invoker.properties new file mode 100644 index 0000000000000..5a1e6fd59b49c --- /dev/null +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/invoker.properties @@ -0,0 +1 @@ +invoker.goals=clean package -Dquarkus.container-image.build=true diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/pom.xml b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/pom.xml new file mode 100644 index 0000000000000..e3e1993602bb0 --- /dev/null +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + org.acme + container-build-jib-inherit + 0.1-SNAPSHOT + + UTF-8 + 3.0.0-M7 + 11 + UTF-8 + 11 + + + + + io.quarkus + quarkus-bom + @project.version@ + pom + import + + + + + + io.quarkus + quarkus-resteasy-reactive + + + io.quarkus + quarkus-container-image-jib + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + io.quarkus + quarkus-maven-plugin + @project.version@ + + + + build + + + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + native + + + native + + + + native + + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${native.surefire.skip} + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/src/main/java/org/acme/Hello.java b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/src/main/java/org/acme/Hello.java new file mode 100644 index 0000000000000..ad80766a17747 --- /dev/null +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/src/main/java/org/acme/Hello.java @@ -0,0 +1,16 @@ +package org.acme; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("/hello") +public class Hello { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "hello"; + } +} \ No newline at end of file diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/src/main/resources/application.properties b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/src/main/resources/application.properties new file mode 100644 index 0000000000000..22994823ba77b --- /dev/null +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/src/main/resources/application.properties @@ -0,0 +1,8 @@ +# Configuration file +# key = value +quarkus.package.type=fast-jar +quarkus.jib.jvm-arguments=java,-Djava.util.logging.manager=org.jboss.logmanager.LogManager,-jar,quarkus-run.jar +quarkus.jib.jvm-entrypoint=INHERIT +quarkus.jib.native-arguments=./application +quarkus.jib.native-entrypoint=INHERIT +quarkus.jib.working-directory=/app diff --git a/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/verify.groovy b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/verify.groovy new file mode 100644 index 0000000000000..9997de49e3f8c --- /dev/null +++ b/integration-tests/container-image/maven-invoker-way/src/it/container-build-jib-inherit/verify.groovy @@ -0,0 +1,37 @@ +import io.quarkus.deployment.util.ExecUtil + +import java.util.concurrent.ThreadLocalRandom + +try { + ExecUtil.exec("docker", "version", "--format", "'{{.Server.Version}}'") +} catch (Exception ignored) { + return +} + +String image = "${System.getProperty("user.name")}/container-build-jib-inherit:0.1-SNAPSHOT" +assert ExecUtil.exec("docker", "images", image) + +String containerName = "container-build-jib-inherit-" + ThreadLocalRandom.current().nextInt(10000) +int maxTimesToCheck = 10 +int i = 0 +int hostPort = 12345 +assert ExecUtil.exec("docker", "run", "-d", "-p", "$hostPort:8080", "--name", containerName, image) + +while (true) { + try { + def response = "http://localhost:$hostPort/hello".toURL().text + assert response == "hello" + break + } catch (IOException e) { + try { + Thread.sleep(2000) + } catch (InterruptedException ignored) { + } + if ((i++) >= maxTimesToCheck) { + throw new RuntimeException("Unable to determine if container is running", e) + } + } +} +assert ExecUtil.exec("docker", "stop", containerName) +assert ExecUtil.exec("docker", "rm", containerName) +assert ExecUtil.exec("docker", "rmi", image) From d937271536ad6a935e5be752bebd2164f1ec677c Mon Sep 17 00:00:00 2001 From: Ozan Gunalp Date: Wed, 21 Sep 2022 19:55:05 +0100 Subject: [PATCH 09/81] Document how to set a particular Apicurio registry serde version. Closes #25814 (cherry picked from commit 0cb8717708d0374f1708f700adaba5429ef0e61a) --- .../asciidoc/kafka-schema-registry-avro.adoc | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/docs/src/main/asciidoc/kafka-schema-registry-avro.adoc b/docs/src/main/asciidoc/kafka-schema-registry-avro.adoc index 054de5e36cda3..8c06be867948b 100644 --- a/docs/src/main/asciidoc/kafka-schema-registry-avro.adoc +++ b/docs/src/main/asciidoc/kafka-schema-registry-avro.adoc @@ -610,6 +610,81 @@ public class MovieResourceTest { } ---- +[[apicurio-versions-compatibility]] +== Using compatible versions of the Apicurio Registry + +The `quarkus-apicurio-registry-avro` extension depends on recent versions of Apicurio Registry client, +and most versions of Apicurio Registry server and client are backwards compatible. +For some you need to make sure that the client used by Serdes is compatible with the server. + +For example, with Apicurio dev service if you set the image name to use version `2.1.5.Final`: + +[source,properties] +---- +quarkus.apicurio-registry.devservices.image-name=quay.io/apicurio/apicurio-registry-mem:2.1.5.Final +---- + +You need to make sure that `apicurio-registry-serdes-avro-serde` dependency +and the REST client `apicurio-common-rest-client-vertx` dependency are set to compatible versions: + +[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] +.pom.xml +---- + + io.quarkus + quarkus-apicurio-registry-avro + + + io.apicurio + apicurio-common-rest-client-vertx + + + io.apicurio + apicurio-registry-serdes-avro-serde + + + + + io.apicurio + apicurio-registry-serdes-avro-serde + 2.1.5.Final + + + io.apicurio + apicurio-common-rest-client-jdk + + + + + io.apicurio + apicurio-common-rest-client-vertx + 0.1.5.Final + +---- + +[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] +.build.gradle +---- +dependencies { + implementation(platform("io.quarkus.platform:quarkus-bom:2.12.3.Final")) + + ... + + implementation("io.quarkus:quarkus-apicurio-registry-avro") + implementation("io.apicurio:apicurio-registry-serdes-avro-serde") { + exclude group: "io.apicurio", module: "apicurio-common-rest-client-jdk" + version { + strictly "2.1.5.Final" + } + } + implementation("io.apicurio:apicurio-common-rest-client-vertx") { + version { + strictly "0.1.5.Final" + } + } +} +---- + [[confluent]] == Using the Confluent Schema Registry From 5338ca9497cb019ac73d84c084c9dc0b6ec3786c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 21:35:55 +0000 Subject: [PATCH 10/81] Bump junit-bom from 5.9.0 to 5.9.1 Bumps [junit-bom](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit:junit-bom dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit c0833d2ccd98eb5ae7c223faeaa0afced230cc7a) --- bom/application/pom.xml | 2 +- independent-projects/arc/pom.xml | 2 +- independent-projects/qute/pom.xml | 2 +- independent-projects/resteasy-reactive/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 0779c44fefd91..76a6a5f6dc428 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -127,7 +127,7 @@ 11.5.7.0 1.2.6 4.5.1 - 5.9.0 + 5.9.1 1.5.0 6.14.2 13.0.10.Final diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml index 72b65cc970ce3..3935b5567745e 100644 --- a/independent-projects/arc/pom.xml +++ b/independent-projects/arc/pom.xml @@ -43,7 +43,7 @@ 2.0.2 1.3.3 2.4.3.Final - 5.9.0 + 5.9.1 3.8.6 3.23.1 3.5.0.Final diff --git a/independent-projects/qute/pom.xml b/independent-projects/qute/pom.xml index 4194241dfbfbb..d8bc2cefc25eb 100644 --- a/independent-projects/qute/pom.xml +++ b/independent-projects/qute/pom.xml @@ -40,7 +40,7 @@ 11 11 11 - 5.9.0 + 5.9.1 3.23.1 2.4.3.Final 1.1.0.Final diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index afae29c3a0045..54c84b2bb6f67 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -43,7 +43,7 @@ 2.0.2 2.4.3.Final 1.12.12 - 5.9.0 + 5.9.1 3.8.6 3.23.1 3.5.0.Final From de5f18dd6ae5f9d1bbb855a796149447be5efeec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 19:07:25 +0000 Subject: [PATCH 11/81] Bump junit-jupiter from 5.9.0 to 5.9.1 in /devtools/gradle Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit e7e05ca5405e4efd4242d45ea64db4955648cea5) --- devtools/gradle/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/gradle/build.gradle b/devtools/gradle/build.gradle index d800441c15f2d..5368eedb8580b 100644 --- a/devtools/gradle/build.gradle +++ b/devtools/gradle/build.gradle @@ -31,7 +31,7 @@ subprojects { implementation "io.quarkus:quarkus-bootstrap-core:${version}" testImplementation gradleTestKit() - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1' testImplementation 'org.assertj:assertj-core:3.23.1' } From 0b1e4cdfd25258a340714e2e66f2a060f9395f7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Sep 2022 21:50:06 +0000 Subject: [PATCH 12/81] Bump liquibase.version from 4.16.0 to 4.16.1 Bumps `liquibase.version` from 4.16.0 to 4.16.1. Updates `liquibase-core` from 4.16.0 to 4.16.1 - [Release notes](https://github.com/liquibase/liquibase/releases) - [Changelog](https://github.com/liquibase/liquibase/blob/master/changelog.txt) - [Commits](https://github.com/liquibase/liquibase/compare/v4.16.0...v4.16.1) Updates `liquibase-mongodb` from 4.16.0 to 4.16.1 - [Release notes](https://github.com/liquibase/liquibase-mongodb/releases) - [Changelog](https://github.com/liquibase/liquibase-mongodb/blob/main/RELEASE.md) - [Commits](https://github.com/liquibase/liquibase-mongodb/compare/liquibase-mongodb-4.16.0...liquibase-mongodb-4.16.1) --- updated-dependencies: - dependency-name: org.liquibase:liquibase-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.liquibase.ext:liquibase-mongodb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 1b39610d639a8c3f19e94a46f254c85668c0886b) --- 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 76a6a5f6dc428..0fe08f1b8daa3 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -158,7 +158,7 @@ 1.0.10 9.3.0 1.0.11 - 4.16.0 + 4.16.1 1.32 6.0.0 4.7.1 From 7dd14dbe847bc544d1cd26611153f0106e7fdf54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Sep 2022 00:42:20 +0000 Subject: [PATCH 13/81] Bump flyway.version from 9.3.0 to 9.3.1 Bumps `flyway.version` from 9.3.0 to 9.3.1. Updates `flyway-core` from 9.3.0 to 9.3.1 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-9.3.0...flyway-9.3.1) Updates `flyway-sqlserver` from 9.3.0 to 9.3.1 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-9.3.0...flyway-9.3.1) Updates `flyway-mysql` from 9.3.0 to 9.3.1 - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-9.3.0...flyway-9.3.1) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-sqlserver dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.flywaydb:flyway-mysql dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit de47433772b3132c015ee2f6aa23a66a0fddae3a) --- 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 0fe08f1b8daa3..a630d408c39d1 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -156,7 +156,7 @@ 3.2.0 4.2.0 1.0.10 - 9.3.0 + 9.3.1 1.0.11 4.16.1 1.32 From cada7db12e95d1dd4c495faaf6de7b227d1a499b Mon Sep 17 00:00:00 2001 From: Ozan Gunalp Date: Mon, 11 Jul 2022 18:48:21 +0300 Subject: [PATCH 14/81] Bump Smallrye Reactive Messaging to 3.19.1 Update Kafka documentation Update HR RM test with activate request context Added HR ORM RM IT (cherry picked from commit c086c038229ea161933d8626665199c3df7923c5) --- bom/application/pom.xml | 2 +- docs/src/main/asciidoc/kafka.adoc | 34 ++- .../src/main/resources/application.properties | 2 + integration-tests/pom.xml | 1 + .../reactive-messaging-hibernate-orm/pom.xml | 271 ++++++++++++++++++ .../main/java/io/quarkus/it/kafka/Fruit.java | 19 ++ .../io/quarkus/it/kafka/FruitProducer.java | 27 ++ .../io/quarkus/it/kafka/KafkaEndpoint.java | 23 ++ .../io/quarkus/it/kafka/KafkaReceivers.java | 32 +++ .../src/main/resources/application.properties | 13 + .../io/quarkus/it/kafka/KafkaConnectorIT.java | 8 + .../quarkus/it/kafka/KafkaConnectorTest.java | 60 ++++ .../io/quarkus/it/kafka/KafkaReceivers.java | 11 +- 13 files changed, 491 insertions(+), 12 deletions(-) create mode 100644 integration-tests/reactive-messaging-hibernate-orm/pom.xml create mode 100644 integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/Fruit.java create mode 100644 integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/FruitProducer.java create mode 100644 integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/KafkaEndpoint.java create mode 100644 integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/KafkaReceivers.java create mode 100644 integration-tests/reactive-messaging-hibernate-orm/src/main/resources/application.properties create mode 100644 integration-tests/reactive-messaging-hibernate-orm/src/test/java/io/quarkus/it/kafka/KafkaConnectorIT.java create mode 100644 integration-tests/reactive-messaging-hibernate-orm/src/test/java/io/quarkus/it/kafka/KafkaConnectorTest.java diff --git a/bom/application/pom.xml b/bom/application/pom.xml index a630d408c39d1..6191a1abcfe7f 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -52,7 +52,7 @@ 1.0.13 2.7.0 2.26.0 - 3.18.0 + 3.19.1 1.1.2 1.2.1 1.3.5 diff --git a/docs/src/main/asciidoc/kafka.adoc b/docs/src/main/asciidoc/kafka.adoc index efb06fd119ec2..a8281d1280a9a 100644 --- a/docs/src/main/asciidoc/kafka.adoc +++ b/docs/src/main/asciidoc/kafka.adoc @@ -282,7 +282,7 @@ Thus, you can use both. The first one provides more fine-grained tuning such as the worker pool to use and whether it preserves the order. The second one, used also with other reactive features of Quarkus, uses the default worker pool and preserves the order. -Detailed information on the usage of `@Blocking` annotation can be found in https://smallrye.io/smallrye-reactive-messaging/smallrye-reactive-messaging/3.1/advanced/blocking.html[SmallRye Reactive Messaging – Handling blocking execution]. +Detailed information on the usage of `@Blocking` annotation can be found in https://smallrye.io/smallrye-reactive-messaging/latest/concepts/blocking/[SmallRye Reactive Messaging – Handling blocking execution]. ==== [TIP] @@ -364,6 +364,9 @@ If high throughput is important for you, and you are not limited by the downstre - or set `enable.auto.commit` to true and annotate the consuming method with `@Acknowledgment(Acknowledgment.Strategy.NONE)`. ==== +Smallrye Reactive Messaging enables implementing custom commit strategies. +See https://smallrye.io/smallrye-reactive-messaging/latest/kafka/receiving-kafka-records/#acknowledgement[SmallRye Reactive Messaging documentation] for more information. + [[error-handling]] === Error Handling Strategies @@ -389,6 +392,9 @@ The record written on the dead letter queue contains a set of additional headers - *dead-letter-partition*: the original partition of the record (integer mapped to String) - *dead-letter-offset*: the original offset of the record (long mapped to String) +Smallrye Reactive Messaging enables implementing custom failure strategies. +See https://smallrye.io/smallrye-reactive-messaging/latest/kafka/receiving-kafka-records/#acknowledgement[SmallRye Reactive Messaging documentation] for more information. + ==== Retrying processing You can combine Reactive Messaging with https://github.com/smallrye/smallrye-fault-tolerance[SmallRye Fault Tolerance], and retry processing if it failed: @@ -926,7 +932,18 @@ The `io.smallrye.reactive.messaging.annotations.Emitter`, `io.smallrye.reactive. The new `Emitter.send` method returns a `CompletionStage` completed when the produced message is acknowledged. ==== -More information on how to use `Emitter` can be found in https://smallrye.io/smallrye-reactive-messaging/smallrye-reactive-messaging/3.1/emitter/emitter.html#_emitter_and_channel[SmallRye Reactive Messaging – Emitters and Channels] +[NOTE] +.Depreciation +==== +`MutinyEmitter#send(Message msg)` method is deprecated in favor of following methods receiving `Message` for emitting: + +* `> Uni sendMessage(M msg)` +* `> void sendMessageAndAwait(M msg)` +* `> Cancellable sendMessageAndForget(M msg)` + +==== + +More information on how to use `Emitter` can be found in https://smallrye.io/smallrye-reactive-messaging/latest/concepts/emitter/[SmallRye Reactive Messaging – Emitters and Channels] === Write Acknowledgement @@ -1254,11 +1271,6 @@ The `KafkaTransactions#withTransactionAndAck` method acks and nacks the message Nacked messages will be handled by the failure strategy of the incoming channel, (see <>). Configuring `failure-strategy=ignore` simply resets the Kafka consumer to the last committed offsets and resumes the consumption from there. -[NOTE] -==== -Redpanda does not yet support link:https://github.com/redpanda-data/redpanda/issues/3279[producer scalability for exactly-once processing]. -In order to use Kafka exactly-once processing with Quarkus you can configure Dev Services for Kafka to <>. -==== [[kafka-bare-clients]] == Accessing Kafka clients directly @@ -1996,10 +2008,14 @@ As described in <>, you need to add the `@Blocking` annotat See the xref:quarkus-reactive-architecture.adoc[Quarkus Reactive Architecture documentation] for further details on this topic. +== Channel Decorators + +SmallRye Reactive Messaging supports decorating incoming and outgoing channels for implementing cross-cutting concerns such as monitoring, tracing or message interception. For more information on implementing decorators and message interceptors see the http://smallrye.io/smallrye-reactive-messaging/3.19.1/concepts/decorators/[SmallRye Reactive Messaging documentation]. + [[kafka-configuration]] == Configuration Reference -More details about the SmallRye Reactive Messaging configuration can be found in the https://smallrye.io/smallrye-reactive-messaging/smallrye-reactive-messaging/3.1/kafka/kafka.html[SmallRye Reactive Messaging - Kafka Connector Documentation]. +More details about the SmallRye Reactive Messaging configuration can be found in the https://smallrye.io/smallrye-reactive-messaging/latest/kafka/kafka/#using-the-kafka-connector[SmallRye Reactive Messaging - Kafka Connector Documentation]. [TIP] ==== @@ -2336,7 +2352,7 @@ public class FruitStore { Mutiny.Session session; // <1> @Incoming("in") - public Uni consume(Fruit fruit) { + public Uni consume(Fruit entity) { return session.withTransaction(t -> { // <2> return entity.persistAndFlush() // <3> .replaceWithVoid(); // <4> diff --git a/integration-tests/kafka-avro-apicurio2/src/main/resources/application.properties b/integration-tests/kafka-avro-apicurio2/src/main/resources/application.properties index d4907bbe9f11b..a64c88f99975e 100644 --- a/integration-tests/kafka-avro-apicurio2/src/main/resources/application.properties +++ b/integration-tests/kafka-avro-apicurio2/src/main/resources/application.properties @@ -4,3 +4,5 @@ quarkus.log.category.\"org.apache.zookeeper\".level=WARN # enable health check quarkus.kafka.health.enabled=true + +quarkus.apicurio-registry.devservices.image-name=quay.io/apicurio/apicurio-registry-mem:2.2.5.Final diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index a408c41796ba6..99dbb209ec2b2 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -288,6 +288,7 @@ reactive-messaging-rabbitmq reactive-messaging-rabbitmq-dyn reactive-messaging-hibernate-reactive + reactive-messaging-hibernate-orm rest-client resteasy-reactive-kotlin rest-client-reactive diff --git a/integration-tests/reactive-messaging-hibernate-orm/pom.xml b/integration-tests/reactive-messaging-hibernate-orm/pom.xml new file mode 100644 index 0000000000000..aa7cacfac2ba7 --- /dev/null +++ b/integration-tests/reactive-messaging-hibernate-orm/pom.xml @@ -0,0 +1,271 @@ + + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-integration-test-reactive-messaging-hibernate-orm + Quarkus - Integration Tests - Reactive Messaging - Hibernate ORM + The Reactive Messaging with Kafka and Hibernate integration tests module + + + true + + + + + io.quarkus + quarkus-integration-test-class-transformer + + + io.quarkus + quarkus-integration-test-shared-library + + + + + io.quarkus + quarkus-resteasy-reactive + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.vertx + vertx-core + + + + + io.quarkus + quarkus-smallrye-health + + + + + io.quarkus + quarkus-kafka-client + + + io.quarkus + quarkus-smallrye-reactive-messaging-kafka + + + + + io.quarkus + quarkus-hibernate-orm-panache + + + io.quarkus + quarkus-jdbc-postgresql + + + + + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-test-kafka-companion + test + + + io.rest-assured + rest-assured + test + + + jakarta.xml.bind + jakarta.xml.bind-api + + + + + + + io.quarkus + quarkus-integration-test-class-transformer-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-kafka-client-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-resteasy-reactive-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-resteasy-reactive-jackson-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-smallrye-health-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-smallrye-reactive-messaging-kafka-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-hibernate-orm-panache-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-jdbc-postgresql-deployment + ${project.version} + pom + test + + + * + * + + + + + io.smallrye.reactive + smallrye-reactive-messaging-api + + + org.awaitility + awaitility + test + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + generate-code + generate-code-tests + + + + + + + maven-failsafe-plugin + + true + + + + + maven-surefire-plugin + + true + + + + + + + + test-kafka + + + test-containers + + + + + + maven-surefire-plugin + + false + + + + maven-failsafe-plugin + + false + + + + + + + + + diff --git a/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/Fruit.java b/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/Fruit.java new file mode 100644 index 0000000000000..da5f7a87c4829 --- /dev/null +++ b/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/Fruit.java @@ -0,0 +1,19 @@ +package io.quarkus.it.kafka; + +import javax.persistence.Entity; + +import io.quarkus.hibernate.orm.panache.PanacheEntity; + +@Entity +public class Fruit extends PanacheEntity { + + public String name; + + public Fruit(String name) { + this.name = name; + } + + public Fruit() { + // Jackson will use this constructor + } +} diff --git a/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/FruitProducer.java b/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/FruitProducer.java new file mode 100644 index 0000000000000..9921a1e2fd469 --- /dev/null +++ b/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/FruitProducer.java @@ -0,0 +1,27 @@ +package io.quarkus.it.kafka; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; + +import org.eclipse.microprofile.reactive.messaging.Channel; + +import io.smallrye.common.vertx.VertxContext; +import io.smallrye.mutiny.Uni; +import io.smallrye.reactive.messaging.MutinyEmitter; + +@Path("/kafka") +public class FruitProducer { + + @Channel("fruits-out") + MutinyEmitter emitter; + + @POST + @Path("/fruits") + @Consumes(MediaType.APPLICATION_JSON) + public Uni post(Fruit fruit) { + assert VertxContext.isOnDuplicatedContext(); + return emitter.send(fruit); + } +} diff --git a/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/KafkaEndpoint.java b/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/KafkaEndpoint.java new file mode 100644 index 0000000000000..35f7b90ed512d --- /dev/null +++ b/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/KafkaEndpoint.java @@ -0,0 +1,23 @@ +package io.quarkus.it.kafka; + +import java.util.List; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("/kafka") +public class KafkaEndpoint { + @Inject + KafkaReceivers receivers; + + @GET + @Path("/fruits") + @Produces(MediaType.APPLICATION_JSON) + public List getFruits() { + return receivers.getFruits(); + } + +} diff --git a/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/KafkaReceivers.java b/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/KafkaReceivers.java new file mode 100644 index 0000000000000..ad1ade1bff1d5 --- /dev/null +++ b/integration-tests/reactive-messaging-hibernate-orm/src/main/java/io/quarkus/it/kafka/KafkaReceivers.java @@ -0,0 +1,32 @@ +package io.quarkus.it.kafka; + +import java.util.List; +import java.util.concurrent.CompletionStage; + +import javax.enterprise.context.ApplicationScoped; +import javax.transaction.Transactional; + +import org.eclipse.microprofile.reactive.messaging.Channel; +import org.eclipse.microprofile.reactive.messaging.Incoming; +import org.eclipse.microprofile.reactive.messaging.Message; + +import io.smallrye.reactive.messaging.MutinyEmitter; + +@ApplicationScoped +public class KafkaReceivers { + + @Channel("fruits-persisted") + MutinyEmitter emitter; + + @Incoming("fruits-in") + @Transactional + public CompletionStage persist(Message fruit) { + fruit.getPayload().persist(); + return emitter.sendMessage(fruit).subscribeAsCompletionStage(); + } + + public List getFruits() { + return Fruit.listAll(); + } + +} diff --git a/integration-tests/reactive-messaging-hibernate-orm/src/main/resources/application.properties b/integration-tests/reactive-messaging-hibernate-orm/src/main/resources/application.properties new file mode 100644 index 0000000000000..165aa8b197ca4 --- /dev/null +++ b/integration-tests/reactive-messaging-hibernate-orm/src/main/resources/application.properties @@ -0,0 +1,13 @@ +quarkus.log.category.kafka.level=WARN +quarkus.log.category.\"org.apache.kafka\".level=WARN +quarkus.log.category.\"org.apache.zookeeper\".level=WARN + +# enable health check +quarkus.kafka.health.enabled=true + +kafka.auto.offset.reset=earliest + +quarkus.hibernate-orm.database.generation=drop-and-create + +mp.messaging.outgoing.fruits-out.topic=fruits +mp.messaging.incoming.fruits-in.topic=fruits diff --git a/integration-tests/reactive-messaging-hibernate-orm/src/test/java/io/quarkus/it/kafka/KafkaConnectorIT.java b/integration-tests/reactive-messaging-hibernate-orm/src/test/java/io/quarkus/it/kafka/KafkaConnectorIT.java new file mode 100644 index 0000000000000..21c6143667af1 --- /dev/null +++ b/integration-tests/reactive-messaging-hibernate-orm/src/test/java/io/quarkus/it/kafka/KafkaConnectorIT.java @@ -0,0 +1,8 @@ +package io.quarkus.it.kafka; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class KafkaConnectorIT extends KafkaConnectorTest { + +} diff --git a/integration-tests/reactive-messaging-hibernate-orm/src/test/java/io/quarkus/it/kafka/KafkaConnectorTest.java b/integration-tests/reactive-messaging-hibernate-orm/src/test/java/io/quarkus/it/kafka/KafkaConnectorTest.java new file mode 100644 index 0000000000000..5db5944fc335e --- /dev/null +++ b/integration-tests/reactive-messaging-hibernate-orm/src/test/java/io/quarkus/it/kafka/KafkaConnectorTest.java @@ -0,0 +1,60 @@ +package io.quarkus.it.kafka; + +import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.given; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.is; + +import java.util.List; + +import javax.ws.rs.core.Response; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.kafka.InjectKafkaCompanion; +import io.quarkus.test.kafka.KafkaCompanionResource; +import io.restassured.common.mapper.TypeRef; +import io.restassured.http.ContentType; +import io.smallrye.reactive.messaging.kafka.companion.KafkaCompanion; + +@QuarkusTest +@QuarkusTestResource(KafkaCompanionResource.class) +public class KafkaConnectorTest { + + protected static final TypeRef> TYPE_REF = new TypeRef>() { + }; + + @InjectKafkaCompanion + KafkaCompanion companion; + + @Test + public void testFruits() { + given().body(new Fruit("apple")).contentType(ContentType.JSON).when().post("/kafka/fruits").then() + .assertThat().statusCode(is(Response.Status.NO_CONTENT.getStatusCode())); + given().body(new Fruit("banana")).contentType(ContentType.JSON).when().post("/kafka/fruits").then() + .assertThat().statusCode(is(Response.Status.NO_CONTENT.getStatusCode())); + given().body(new Fruit("peach")).contentType(ContentType.JSON).when().post("/kafka/fruits").then() + .assertThat().statusCode(is(Response.Status.NO_CONTENT.getStatusCode())); + given().body(new Fruit("orange")).contentType(ContentType.JSON).when().post("/kafka/fruits").then() + .assertThat().statusCode(is(Response.Status.NO_CONTENT.getStatusCode())); + given().body(new Fruit("cherry")).contentType(ContentType.JSON).when().post("/kafka/fruits").then() + .assertThat().statusCode(is(Response.Status.NO_CONTENT.getStatusCode())); + given().body(new Fruit("pear")).contentType(ContentType.JSON).when().post("/kafka/fruits").then() + .assertThat().statusCode(is(Response.Status.NO_CONTENT.getStatusCode())); + + await().untilAsserted(() -> Assertions.assertEquals(6, get("/kafka/fruits").as(TYPE_REF).size())); + + for (ConsumerRecord record : companion + .consumeWithDeserializers(new StringDeserializer(), new StringDeserializer()) + .fromTopics("fruits-persisted", 6) + .awaitCompletion()) { + System.out.println(record); + } + } + +} diff --git a/integration-tests/reactive-messaging-hibernate-reactive/src/main/java/io/quarkus/it/kafka/KafkaReceivers.java b/integration-tests/reactive-messaging-hibernate-reactive/src/main/java/io/quarkus/it/kafka/KafkaReceivers.java index 2d5a5cd56f546..a52491f4ca07a 100644 --- a/integration-tests/reactive-messaging-hibernate-reactive/src/main/java/io/quarkus/it/kafka/KafkaReceivers.java +++ b/integration-tests/reactive-messaging-hibernate-reactive/src/main/java/io/quarkus/it/kafka/KafkaReceivers.java @@ -4,6 +4,7 @@ import java.util.Objects; import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.control.ActivateRequestContext; import javax.inject.Inject; import org.eclipse.microprofile.reactive.messaging.Incoming; @@ -11,6 +12,7 @@ import org.eclipse.microprofile.reactive.messaging.Outgoing; import org.hibernate.reactive.mutiny.Mutiny; +import io.quarkus.hibernate.reactive.panache.Panache; import io.smallrye.common.vertx.ContextLocals; import io.smallrye.common.vertx.VertxContext; import io.smallrye.mutiny.Uni; @@ -38,10 +40,15 @@ public Uni> persistFruit(Message fruit) { @Blocking @Incoming("fruits-persisted") + @ActivateRequestContext public Uni consumeFruit(Message fruit) { assert VertxContext.isOnDuplicatedContext(); - assert Objects.equals(ContextLocals.get("fruit-id").get(), fruit.getPayload().id); - return Uni.createFrom().completionStage(fruit.ack()); + Fruit payload = fruit.getPayload(); + assert Objects.equals(ContextLocals.get("fruit-id").get(), payload.id); + return Panache.withTransaction(() -> { + payload.name = "fruit-" + payload.name; + return payload.persist().chain(() -> Uni.createFrom().completionStage(fruit.ack())); + }); } public Uni> getFruits() { From 391e5e0b87a55191742cb5343b7cb280471fa27d Mon Sep 17 00:00:00 2001 From: Ozan Gunalp Date: Wed, 21 Sep 2022 20:48:44 +0100 Subject: [PATCH 15/81] Improve amqp test robustness by producing durable messages (cherry picked from commit f285603008087b6f2b2f63486069529eba993fe0) --- .../deployment/src/test/resources/application-secured.properties | 1 + .../deployment/src/test/resources/application.properties | 1 + 2 files changed, 2 insertions(+) diff --git a/extensions/smallrye-reactive-messaging-amqp/deployment/src/test/resources/application-secured.properties b/extensions/smallrye-reactive-messaging-amqp/deployment/src/test/resources/application-secured.properties index 93bedede04be4..8f2da7b189603 100644 --- a/extensions/smallrye-reactive-messaging-amqp/deployment/src/test/resources/application-secured.properties +++ b/extensions/smallrye-reactive-messaging-amqp/deployment/src/test/resources/application-secured.properties @@ -3,6 +3,7 @@ amqp-username=artemis amqp-password=artemis mp.messaging.outgoing.source.connector=smallrye-amqp +mp.messaging.outgoing.source.durable=true mp.messaging.incoming.in.connector=smallrye-amqp mp.messaging.incoming.in.address=source diff --git a/extensions/smallrye-reactive-messaging-amqp/deployment/src/test/resources/application.properties b/extensions/smallrye-reactive-messaging-amqp/deployment/src/test/resources/application.properties index 5506833771e38..f6e591ab27f7b 100644 --- a/extensions/smallrye-reactive-messaging-amqp/deployment/src/test/resources/application.properties +++ b/extensions/smallrye-reactive-messaging-amqp/deployment/src/test/resources/application.properties @@ -1,6 +1,7 @@ amqp-port=5672 mp.messaging.outgoing.source.connector=smallrye-amqp +mp.messaging.outgoing.source.durable=true mp.messaging.incoming.in.connector=smallrye-amqp mp.messaging.incoming.in.address=source From 1e235272fd587abbe3e0e4ee4d612630bb923710 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 22 Sep 2022 09:31:41 +0300 Subject: [PATCH 16/81] Prevent attempt to create Vert.x caching dir caching is disabled Closes: #28094 (cherry picked from commit 2652931b48fa7f4fdcd4a60d11abdd38f7edc8dc) --- .../vertx/core/runtime/VertxCoreRecorder.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) 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 1d4ff48c2413d..05d88e50dbd35 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 @@ -305,10 +305,15 @@ private static VertxOptions convertToVertxOptions(VertxConfiguration conf, Vertx initializeClusterOptions(conf, options); } + FileSystemOptions fileSystemOptions = new FileSystemOptions() + .setFileCachingEnabled(conf.caching) + .setClassPathResolvingEnabled(conf.classpathResolving); + String fileCacheDir = System.getProperty(CACHE_DIR_BASE_PROP_NAME); if (fileCacheDir == null) { File tmp = new File(System.getProperty("java.io.tmpdir", ".") + File.separator + VERTX_CACHE); - if (!tmp.isDirectory()) { + boolean cacheDirRequired = conf.caching || conf.classpathResolving; + if (!tmp.isDirectory() && cacheDirRequired) { if (!tmp.mkdirs()) { LOGGER.warnf("Unable to create Vert.x cache directory : %s", tmp.getAbsolutePath()); } @@ -322,26 +327,25 @@ private static VertxOptions convertToVertxOptions(VertxConfiguration conf, Vertx } } - File cache = getRandomDirectory(tmp); - LOGGER.debugf("Vert.x Cache configured to: %s", cache.getAbsolutePath()); - fileCacheDir = cache.getAbsolutePath(); - if (shutdown != null) { - shutdown.addLastShutdownTask(new Runnable() { - @Override - public void run() { - // Recursively delete the created directory and all the files - deleteDirectory(cache); - // We do not delete the vertx-cache directory on purpose, as it could be used concurrently by - // another application. In the worse case, it's just an empty directory. - } - }); + if (cacheDirRequired) { + File cache = getRandomDirectory(tmp); + LOGGER.debugf("Vert.x Cache configured to: %s", cache.getAbsolutePath()); + fileSystemOptions.setFileCacheDir(cache.getAbsolutePath()); + if (shutdown != null) { + shutdown.addLastShutdownTask(new Runnable() { + @Override + public void run() { + // Recursively delete the created directory and all the files + deleteDirectory(cache); + // We do not delete the vertx-cache directory on purpose, as it could be used concurrently by + // another application. In the worse case, it's just an empty directory. + } + }); + } } } - options.setFileSystemOptions(new FileSystemOptions() - .setFileCachingEnabled(conf.caching) - .setFileCacheDir(fileCacheDir) - .setClassPathResolvingEnabled(conf.classpathResolving)); + options.setFileSystemOptions(fileSystemOptions); options.setWorkerPoolSize(conf.workerPoolSize); options.setInternalBlockingPoolSize(conf.internalBlockingPoolSize); blockingThreadPoolSize = conf.internalBlockingPoolSize; From 642eaa4f1ae142c241759e23b6b40b8372986d46 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 22 Sep 2022 12:02:09 +0300 Subject: [PATCH 17/81] Take @NameBinding in the class hierarchy into account Fixes: #27979 (cherry picked from commit 66b59c4e5c7aafb35710db58ef8792869534529c) --- .../scanning/ClientEndpointIndexer.java | 5 +- .../common/processor/EndpointIndexer.java | 11 +- .../simple/NameBindingWithInterfaceTest.java | 104 ++++++++++++++++++ 3 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/NameBindingWithInterfaceTest.java diff --git a/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/scanning/ClientEndpointIndexer.java b/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/scanning/ClientEndpointIndexer.java index 01d7062bc8162..f3b2e27bfc172 100644 --- a/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/scanning/ClientEndpointIndexer.java +++ b/independent-projects/resteasy-reactive/client/processor/src/main/java/org/jboss/resteasy/reactive/client/processor/scanning/ClientEndpointIndexer.java @@ -69,7 +69,7 @@ public MaybeRestClientInterface createClientProxy(ClassInfo classInfo, } clazz.setPath(path); } - List methods = createEndpoints(classInfo, classInfo, new HashSet<>(), + List methods = createEndpoints(classInfo, classInfo, new HashSet<>(), new HashSet<>(), clazz.getPathParameters(), clazz.getPath(), false); clazz.getMethods().addAll(methods); @@ -101,7 +101,8 @@ protected void handleClientSubResource(ResourceMethod resourceMethod, MethodInfo throw new IllegalStateException("Subresource method returns an invalid type: " + method.returnType().name()); } - List endpoints = createEndpoints(subResourceClass, subResourceClass, new HashSet<>(), new HashSet<>(), + List endpoints = createEndpoints(subResourceClass, subResourceClass, + new HashSet<>(), new HashSet<>(), new HashSet<>(), "", false); resourceMethod.setSubResourceMethods(endpoints); } diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java index 6c6e9e5f22932..072551ecfebc7 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java @@ -283,7 +283,7 @@ public Optional createEndpoints(ClassInfo classInfo, boolean cons if (classLevelExceptionMappers != null) { clazz.setClassLevelExceptionMappers(classLevelExceptionMappers); } - List methods = createEndpoints(classInfo, classInfo, new HashSet<>(), + List methods = createEndpoints(classInfo, classInfo, new HashSet<>(), new HashSet<>(), clazz.getPathParameters(), clazz.getPath(), considerApplication); clazz.getMethods().addAll(methods); @@ -351,7 +351,7 @@ protected abstract METHOD createResourceMethod(MethodInfo info, ClassInfo actual Map methodContext); protected List createEndpoints(ClassInfo currentClassInfo, - ClassInfo actualEndpointInfo, Set seenMethods, + ClassInfo actualEndpointInfo, Set seenMethods, Set existingClassNameBindings, Set pathParameters, String resourceClassPath, boolean considerApplication) { if (considerApplication && applicationScanningResult != null && !applicationScanningResult.keepClass(actualEndpointInfo.name().toString())) { @@ -373,6 +373,9 @@ protected List createEndpoints(ClassInfo currentClassInfo, classConsumes, pathParameters, classStreamElementType); Set classNameBindings = NameBindingUtil.nameBindingNames(index, currentClassInfo); + if (classNameBindings.isEmpty()) { + classNameBindings = existingClassNameBindings; + } for (DotName httpMethod : httpAnnotationToMethod.keySet()) { List methods = currentClassInfo.methods(); @@ -438,7 +441,7 @@ protected List createEndpoints(ClassInfo currentClassInfo, if (superClassName != null && !superClassName.equals(OBJECT)) { ClassInfo superClass = index.getClassByName(superClassName); if (superClass != null) { - ret.addAll(createEndpoints(superClass, actualEndpointInfo, seenMethods, + ret.addAll(createEndpoints(superClass, actualEndpointInfo, seenMethods, classNameBindings, pathParameters, resourceClassPath, considerApplication)); } } @@ -446,7 +449,7 @@ protected List createEndpoints(ClassInfo currentClassInfo, for (DotName i : interfaces) { ClassInfo superClass = index.getClassByName(i); if (superClass != null) { - ret.addAll(createEndpoints(superClass, actualEndpointInfo, seenMethods, + ret.addAll(createEndpoints(superClass, actualEndpointInfo, seenMethods, classNameBindings, pathParameters, resourceClassPath, considerApplication)); } } diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/NameBindingWithInterfaceTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/NameBindingWithInterfaceTest.java new file mode 100644 index 0000000000000..10661e08b36d2 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/NameBindingWithInterfaceTest.java @@ -0,0 +1,104 @@ +package org.jboss.resteasy.reactive.server.vertx.test.simple; + +import static io.restassured.RestAssured.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.io.IOException; +import java.lang.annotation.Retention; + +import javax.ws.rs.GET; +import javax.ws.rs.NameBinding; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.ext.Provider; + +import org.hamcrest.Matchers; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +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.smallrye.mutiny.Uni; + +public class NameBindingWithInterfaceTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(BlockingHelloResource.class, ReactiveHelloResource.class, BlockingHelloApi.class, + ReactiveHelloApi.class, AddTestHeaderContainerRequestFilter.class)); + + @Test + public void blockingHello() { + get("/blocking-hello") + .then() + .statusCode(200) + .body(Matchers.equalTo("hello")) + .header("test", "some-value"); + } + + @Test + public void reactiveHello() { + get("/reactive-hello") + .then() + .statusCode(200) + .body(Matchers.equalTo("hello")) + .header("test", "some-value"); + } + + @SomeFilter + public static class BlockingHelloResource implements BlockingHelloApi { + + @Override + public String sayHello() { + return "hello"; + } + } + + @SomeFilter + public static class ReactiveHelloResource implements ReactiveHelloApi { + + @Override + public Uni sayHello() { + return Uni.createFrom().item("hello"); + } + } + + @Path("blocking-hello") + public interface BlockingHelloApi { + + @GET + @Produces(MediaType.TEXT_PLAIN) + String sayHello(); + } + + @Path("reactive-hello") + public interface ReactiveHelloApi { + + @GET + @Produces(MediaType.TEXT_PLAIN) + Uni sayHello(); + } + + @NameBinding + @Retention(RUNTIME) + @interface SomeFilter { + } + + @Provider + @SomeFilter + public static class AddTestHeaderContainerRequestFilter implements ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + responseContext.getHeaders().putSingle("test", "some-value"); + + } + } +} From 99dcbba80047ce90ce7a57f6072cf677632d8475 Mon Sep 17 00:00:00 2001 From: Michelle Purcell Date: Tue, 20 Sep 2022 19:21:52 +0100 Subject: [PATCH 18/81] Quarkus style enhancements: Security overview doc Heading fix SME Review fixes Fix SME comments More SME fixes Further updates Fixed minor issues Moved section up Bad spacing (cherry picked from commit cc483e3cdac47f5adaa586c914ec9b7cafdddf96) --- docs/src/main/asciidoc/security.adoc | 342 +++++++++++++++------------ 1 file changed, 192 insertions(+), 150 deletions(-) diff --git a/docs/src/main/asciidoc/security.adoc b/docs/src/main/asciidoc/security.adoc index 2987891b95f72..89ddb68c619e8 100644 --- a/docs/src/main/asciidoc/security.adoc +++ b/docs/src/main/asciidoc/security.adoc @@ -3,161 +3,186 @@ 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 //// -= Security Architecture and Guides += Quarkus Security overview include::./attributes.adoc[] -Quarkus Security provides the architecture, multiple authentication and authorization mechanisms, and other tools for the developers to build a production-quality security for their Quarkus applications. +Quarkus Security is a framework that provides the architecture, multiple authentication and authorization mechanisms, and other tools for you to build secure and production-quality Java applications. -This document provides a brief overview of Quarkus Security and links to the individual guides. +== Getting started with Quarkus Security -== Getting Started +Before you start building security into your Quarkus applications, review the overview information to learn about the Quarkus Security architecture and the different authentication and authorization mechanisms that Quarkus supports. -Please see the xref:security-getting-started.adoc[Getting Started With Security] guide for a quick walkthrough through Quarkus Security where you can learn how to use xref:security-basic-auth-concept.adoc[Basic HTTP Authentication] mechanism and `JPA Identity Provider` to create `SecurityIdentity` and authorize a secure access to the endpoint with `Role Based Access Control`. +To get started with security in Quarkus, we recommend that you first combine the Quarkus built-in xref:security-basic-auth-concept.adoc[Basic HTTP authentication] with the JPA identity provider to enable role-based access control (RBAC). +Complete the steps in the ref:security-getting-started.adoc[Secure a Quarkus application with Basic authentication] tutorial. +After you have successfully secured your Quarkus application with basic HTTP authentication, you can increase the security further by adding more advanced authentication mechanisms, for example, OpenID Connect (OIDC) authentication. -== Architecture +== Security architecture -`HttpAuthenticationMechanism` is the main entry into Quarkus HTTP Security. +The `HttpAuthenticationMechanism` interface is the main entry mechanism for securing HTTP applications in Quarkus. -Quarkus Security Manager uses `HttpAuthenticationMechanism` to extract the authentication credentials from the HTTP request and delegates to `IdentityProvider` to -complete the conversion of these credentials to `SecurityIdentity`. +Quarkus Security uses `HttpAuthenticationMechanism` to extract the authentication credentials from the HTTP request and delegates them to `IdentityProvider` to convert the credentials to `SecurityIdentity`. +For example, the credentials can come from the `Authorization` header, client HTTPS certificates, or cookies. -For example, the credentials may be coming with the HTTP `Authorization` header, client HTTPS certificates or cookies. - -`IdentityProvider` verifies the authentication credentials and maps them to `SecurityIdentity` which contains the username, roles, the original authentication credentials, and other attributes. +`IdentityProvider` verifies the authentication credentials and maps them to `SecurityIdentity`, which has the username, roles, original authentication credentials, and other attributes. For every authenticated resource, you can inject a `SecurityIdentity` instance to get the authenticated identity information. -In some other contexts you may have other parallel representations of the same information (or parts of it) such as `SecurityContext` -for JAX-RS or `JsonWebToken` for JWT. +In other contexts, it is possible to have other parallel representations of the same information or parts of it, for example, `SecurityContext` +for JAX-RS or `JsonWebToken` for JSON Web Tokens (JWT). == Authentication mechanisms -Quarkus supports several sources to load authentication information from. - -=== Basic and Form Authentication Mechanisms - -Basic and Form HTTP-based authentication mechanisms are the core authentication mechanisms supported in Quarkus. -Please see xref:security-basic-auth-concept.adoc[Basic HTTP Authentication] and xref:security-built-in-authentication.adoc#form-auth[Form HTTP Authentication] for more information. +Quarkus supports multiple authentication mechanisms -=== WebAuthn Authentication Mechanism +=== Basic and Form HTTP authentication -https://webauthn.guide/[WebAuthn] is an authentication mechanism designed to replace passwords. In short, every -time you write a service for registering new users, or logging them in, instead of asking for a password, you use WebAuthn, which will replace the password. +xref:security-basic-auth-concept.adoc[Basic HTTP Authentication] and xref:security-built-in-authentication.adoc#form-auth[Form HTTP authentication] are the core authentication mechanisms supported in Quarkus. -Please see xref:security-webauthn.adoc[our dedicated WebAuthn documentation] for more information. +=== WebAuthn authentication -=== Mutual TLS Authentication +https://webauthn.guide/[WebAuthn] is an authentication mechanism that replaces passwords. +When you write a service for registering new users, or logging them in, instead of asking for a password, you can use WebAuthn, which replaces the password. +For more information, see xref:security-webauthn.adoc[Secure a Quarkus application by using the WebAuthn authentication mechanism]. -Quarkus provides Mutual TLS authentication so that you can authenticate users based on their X.509 certificates. +=== Mutual TLS (mTLS) authentication -Please see xref:security-built-in-authentication.adoc#mutual-tls[Mutual TLS Authentication] for more information. +Quarkus provides mutual TLS (mTLS) authentication so that you can authenticate users based on their X.509 certificates. +For more information, see xref:security-built-in-authentication.adoc#mutual-tls[mutual TLS authentication]. -=== OpenID Connect +=== OpenID Connect authentication -OpenID Connect (OIDC) is an identity layer that works on top of the OAuth 2.0 protocol. OIDC enables client applications to verify the identity of a user based on authentication that is performed by the OIDC provider and retrieves basic information about that user. +OpenID Connect (OIDC) is an identity layer that works on top of the OAuth 2.0 protocol. OIDC enables client applications to verify the identity of a user based on the authentication performed by the OIDC provider and to retrieve basic information about that user. The Quarkus `quarkus-oidc` extension provides a reactive, interoperable, multitenant-enabled OIDC adapter that supports Bearer Token and Authorization Code Flow authentication mechanisms. - The Bearer Token mechanism extracts the token from the HTTP Authorization header. -The Authorization Code Flow mechanism redirects the user to an OIDC provider to authenticate the identity of this user and, after the user is redirected back to Quarkus, the mechanism completes the authentication process by exchanging the provided code grant for ID, access, and refresh tokens. +The Authorization Code Flow mechanism redirects the user to an OIDC provider to authenticate the identity of the user. +After the user is redirected back to Quarkus, the mechanism completes the authentication process by exchanging the provided code that was granted for the ID, access, and refresh tokens. -You can verify ID and access JSON Web Token (JWT) tokens by using the refreshable JSON Web Key (JWK) set. However, both JWT and opaque (binary) tokens can be introspected remotely. +You can verify ID and access JWT tokens by using the refreshable JSON Web Key (JWK) set or you can introspect them remotely. +However, opaque (binary) tokens can only be introspected remotely. [NOTE] ==== -Using the Quarkus OIDC extension, both Bearer Token and Authorization Code Flow mechanisms use <> to represent JWT tokens as Microprofile JWT `org.eclipse.microprofile.jwt.JsonWebToken`. +Using the Quarkus OIDC extension, both Bearer Token and Authorization Code Flow mechanisms use <> to represent JWT tokens as MicroProfile JWT `org.eclipse.microprofile.jwt.JsonWebToken`. ==== -For information about the Bearer Token authentication mechanism, see xref:security-openid-connect.adoc[Using OpenID Connect to Protect Service Applications]. - -For information about the Authorization Code Flow authentication mechanism, see xref:security-openid-connect-web-authentication.adoc[Using OpenID Connect to Protect Web Application]. - -For information about multiple tenants that can support Bearer Token or Authorization Code Flow mechanisms, see xref:security-openid-connect-multitenancy.adoc[Using OpenID Connect Multi-Tenancy]. +==== Additional Quarkus resources for OIDC authentication -For information about using Keycloak to Centralize Authorization, see the xref:security-keycloak-authorization.adoc[Using Keycloak to Centralize Authorization] guide. +For more information about OIDC authentication and authorization methods you can use to secure your Quarkus applications, see the following detailed resources: -For information about configuring Keycloak programmatically, see the xref:security-keycloak-admin-client.adoc[Keycloak Admin Client] guide. +[options="header"] +|==== +|OIDC topic |Quarkus information resource +|Bearer Token authentication mechanism|xref:security-openid-connect.adoc[Using OpenID Connect (OIDC) to protect service applications using Bearer Token authorization] +|Authorization Code Flow authentication mechanism|xref:security-openid-connect-web-authentication.adoc[OpenID Connect (OIDC) authorization code flow mechanism] +|Multiple tenants that can support Bearer Token or Authorization Code Flow mechanisms|xref:security-openid-connect-multitenancy.adoc[Using OpenID Connect (OIDC) multi-tenancy] +|Using Keycloak to centralize authorization|xref:security-keycloak-authorization.adoc[Using OpenID Connect (OIDC) and Keycloak to centralize authorization] +|Configuring Keycloak programmatically|xref:security-keycloak-admin-client.adoc[Using the Keycloak admin client] +|==== [NOTE] ==== -* If you need to enable the Quarkus OIDC extension at runtime, set `quarkus.oidc.tenant-enabled=false` at build time then re-enable it at runtime by using a system property. -For more information about managing the individual tenant configurations in multitenant OIDC deployments, see xref:security-openid-connect-multitenancy.adoc#disable-tenant[Disabling Tenant Configurations]. +* If you need to enable the Quarkus OIDC extension at runtime, set `quarkus.oidc.tenant-enabled=false` at build time and then re-enable it at runtime by using a system property. +For more information about managing the individual tenant configurations in multitenant OIDC deployments, see the _Disabling tenant configurations_ section in the xref:security-openid-connect-multitenancy.adoc#disable-tenant[Using OpenID Connect (OIDC) multi-tenancy] guide. ==== -=== OpenID Connect Client and Filters +=== OpenID Connect client and filters -`quarkus-oidc-client` extension provides `OidcClient` for acquiring and refreshing access tokens from OpenID Connect and OAuth2 providers which support `client-credentials`, `password` and `refresh_token` token grants. +The `quarkus-oidc-client` extension provides `OidcClient` for acquiring and refreshing access tokens from OpenID Connect and OAuth2 providers that support the following token grants: +* `client-credentials` +* `password` +* `refresh_token` -`quarkus-oidc-client-filter` extension depends on the `quarkus-oidc-client` extension and provides JAX-RS `OidcClientRequestFilter` which sets the access token acquired by `OidcClient` as an HTTP `Authorization` header's `Bearer` scheme value. This filter can be registered with MP RestClient implementations injected into the current Quarkus endpoint, but it is not related to the authentication requirements of this service endpoint. For example, it can be a public endpoint, or it can be protected with MTLS - the important point is that this Quarkus endpoint does not have to be protected itself with the Quarkus OpenID Connect adapter. +The `quarkus-oidc-client-filter` extension requires the `quarkus-oidc-client` extension and provides JAX-RS `OidcClientRequestFilter`, which sets the access token acquired by `OidcClient` as the `Bearer` scheme value of the HTTP `Authorization` header. +This filter can be registered with MP RestClient implementations injected into the current Quarkus endpoint, but it is not related to the authentication requirements of this service endpoint. +For example, it can be a public endpoint, or it can be protected with mTLS. -`quarkus-oidc-token-propagation` extension depends on the `quarkus-oidc` extension and provides JAX-RS `TokenCredentialRequestFilter` which sets the OpenID Connect Bearer or Authorization Code Flow access token as an HTTP `Authorization` header's `Bearer` scheme value. This filter can be registered with MP RestClient implementations injected into the current Quarkus endpoint and the Quarkus endpoint must be protected itself with the Quarkus OpenID Connect adapter. This filter can be used to propagate the access token to the downstream services. - -See the xref:security-openid-connect-client.adoc[OpenID Connect and Token Propagation Quickstart] and xref:security-openid-connect-client-reference.adoc[OpenID Connect and OAuth2 Client Reference] guides for more information. +[IMPORTANT] +==== +In this scenario, you do not need to protect your Quarkus endpoint by using the Quarkus OpenID Connect adapter. +==== -[[smallrye-jwt]] -=== SmallRye JWT +The `quarkus-oidc-token-propagation` extension requires the `quarkus-oidc` extension and provides JAX-RS `TokenCredentialRequestFilter`, which sets the OpenID Connect Bearer or Authorization Code Flow access token as the `Bearer` scheme value of the HTTP `Authorization` header. +This filter can be registered with MP RestClient implementations injected into the current Quarkus endpoint, which in turn must be protected by using the Quarkus OpenID Connect adapter. +This filter can be used to propagate the access token to the downstream services. -`quarkus-smallrye-jwt` provides Microprofile JWT 1.1.1 implementation and many more options to verify signed and encrypted `JWT` tokens and represent them as `org.eclipse.microprofile.jwt.JsonWebToken`. +For more information, see the xref:security-openid-connect-client.adoc[OpenID Connect client and token propagation quickstart] and xref:security-openid-connect-client-reference.adoc[OpenID Connect (OIDC) and OAuth2 client and filters reference] guides. -It provides an alternative to `quarkus-oidc` Bearer Token Authentication Mechanism. It can currently verify only `JWT` tokens using the PEM keys or refreshable `JWK` key set. +[[smallrye-jwt]] +=== SmallRye JWT authentication -Additionally, it provides `JWT Generation API` for creating `signed`, `inner-signed` and/or `encrypted` `JWT` tokens with ease. +The `quarkus-smallrye-jwt` extension provides a MicroProfile JSON Web Token (JWT) 1.2.1 implementation and multiple options to verify signed and encrypted `JWT` tokens and represents them as `org.eclipse.microprofile.jwt.JsonWebToken`. -See the xref:security-jwt.adoc[Using SmallRye JWT] guide for more information. +`quarkus-smallrye-jwt` is an alternative to the `quarkus-oidc` Bearer Token authentication mechanism, and verifies only `JWT` tokens by using either PEM keys or the refreshable `JWK` key set. +`quarkus-smallrye-jwt` also provides the JWT generation API, which you can use to easily create `signed`, `inner-signed`, and `encrypted` `JWT` tokens. -=== OAuth2 +For more information, see xref:security-jwt.adoc[Using SmallRye JWT role-based access control]. -`quarkus-elytron-security-oauth2` provides an alternative to `quarkus-oidc` Bearer Token Authentication Mechanism. It is based on `Elytron` and is primarily meant for introspecting the opaque tokens remotely. +=== OAuth2 authentication -See the xref:security-oauth2.adoc[Using OAuth2] guide for more information. +`quarkus-elytron-security-oauth2` provides an alternative to the `quarkus-oidc` Bearer Token authentication mechanism. `quarkus-elytron-security-oauth2` is based on `Elytron` and is primarily intended for introspecting opaque tokens remotely. +For more information, see xref:security-oauth2.adoc[Using OAuth2]. [[oidc-jwt-oauth2-comparison]] -=== Choosing between OpenID Connect, SmallRye JWT and OAuth2 extensions +=== Choosing between OpenID Connect, SmallRye JWT, and OAuth2 authentication mechanisms -`quarkus-oidc` extension requires an OpenID Connect provider such as Keycloak which can be used to verify the Bearer tokens or authenticate the end users with the Authorization Code flow. In both cases `quarkus-oidc` requires a connection to this OpenID Connect provider. +Use the following information to help you to decide which authentication mechanism to use to secure your Quarkus applications: -`quarkus-oidc` is the only option when the user authentication by using Authorization Code flow or supporting multiple tenants is required. It can also request a UserInfo using both Authorization Code Flow and Bearer access tokens. +* `quarkus-oidc` requires an OpenID Connect provider such as Keycloak, which can be used to verify the Bearer tokens or authenticate the end users with the Authorization Code flow. +In both cases, `quarkus-oidc` requires a connection to the specified OpenID Connect provider. -When the Bearer tokens have to be verified then `quarkus-oidc`, `quarkus-smallrye-jwt` and `quarkus-elytron-security-oauth2` can be used. +* If the user authentication requires Authorization Code flow or you need to support multiple tenants, use `quarkus-oidc`. +`quarkus-oidc` can also request user information by using both Authorization Code Flow and Bearer access tokens. -If you have Bearer tokens in a JWT format then all these 3 extensions can be used. Both `quarkus-oidc` and `quarkus-smallrye-jwt` support refreshing the JsonWebKey (JWK) set when the OpenID Connect provider rotates the keys, therefore `quarkus-oidc` or `quarkus-smallrye-jwt` should be used for verifying JWT tokens if the remote token introspection has to be avoided or not supported by the providers. +* If your Bearer tokens must be verified, use `quarkus-oidc`, `quarkus-smallrye-jwt`, or `quarkus-elytron-security-oauth2`. -`quarkus-smallrye-jwt` does not support the remote introspection of the opaque tokens or even JWT tokens - it always relies on the locally available keys - possibly fetched from the OpenID Connect provider. So if you need to introspect the JWT tokens remotely then both `quarkus-oidc` and `quarkus-elytron-security-oauth2` will work. Both extensions also support the verification of the opaque/binary tokens by using the remote introspection. +* If your Bearer tokens are in a JWT format, you can use either of the three extensions. Both `quarkus-oidc` and `quarkus-smallrye-jwt` support refreshing the JsonWebKey (JWK) set when the OpenID Connect provider rotates the keys. +Therefore, if remote token introspection must be avoided or is unsupported by the providers, use `quarkus-oidc` or `quarkus-smallrye-jwt` for verifying JWT tokens. -`quarkus-oidc` and `quarkus-smallrye-jwt` can have both JWT and opaque tokens injected into the endpoint code - the injected JWT tokens may offer a richer information about the user. All extensions can have the tokens injected as `Principal`. +* If you need to introspect the JWT tokens remotely, you can use either `quarkus-oidc` or `quarkus-elytron-security-oauth2` because they support the verification of the opaque or binary tokens by using remote introspection. +`quarkus-smallrye-jwt` does not support the remote introspection of both opaque or JWT tokens but instead relies on the locally available keys that are usually retrieved from the OpenID Connect provider. -`quarkus-smallrye-jwt` supports more key formats than `quarkus-oidc`. The latter will only use the JWK-formatted keys which are part of a JWK set. The former - can also work with PEM keys. +* `quarkus-oidc` and `quarkus-smallrye-jwt` support the injecting of JWT and opaque tokens into the endpoint code. +Injected JWT tokens provide more information about the user. All extensions can have the tokens injected as `Principal`. -`quarkus-smallrye-jwt` can handle locally not only signed but also inner-signed-and-encrypted or only encrypted tokens. In fact `quarkus-oidc` and `quarkus-elytron-security-oauth2` can verify such tokens too but only by treating them as opaque tokens and verifying them through the remote introspection. +* `quarkus-smallrye-jwt` supports more key formats than `quarkus-oidc`. `quarkus-oidc` uses only the JWK-formatted keys that are part of a JWK set, whereas `quarkus-smallrye-jwt` supports PEM keys. -`quarkus-elytron-security-oauth2` is the best choice if you need a lightweight library for the remote introspection of either opaque or JWT tokens. +* `quarkus-smallrye-jwt` handles locally signed, inner-signed-and-encrypted, and encrypted tokens. +While `quarkus-oidc` and `quarkus-elytron-security-oauth2` can also verify such tokens but treats them as opaque tokens and verifies them through remote introspection. -Note that a choice of using the opaque versus JWT token format is often driven by the architectural considerations. Opaque tokens are usually much shorter than JWT tokens, but they require maintaining most of the token associated state in the provider database - the opaque tokens are effectively the database pointers. JWT tokens are significantly longer than the opaque tokens - but the providers are effectively delegating storing most of the token associated state to the client by storing it as the token claims and either signing and/or encrypting them. +* If you need a lightweight library for the remote introspection of opaque or JWT tokens, use `quarkus-elytron-security-oauth2`. -Below is a summary of the options. +[NOTE] +==== +Your decision to choose whether to use opaque or JWT token format will be driven by architectural considerations. +Opaque tokens tend to be much shorter than JWT tokens but need most of the token-associated state to be maintained in the provider database. +Opaque tokens are effectively database pointers. +JWT tokens are significantly longer than the opaque tokens but the providers are effectively delegating storing most of the token-associated state to the client by storing it as the token claims and either signing or encrypting them. +==== + +The following table provides a summary of the options for each authentication mechanism: |=== | | quarkus-oidc| quarkus-smallrye-jwt | quarkus-elytron-security-oauth2 -|Bearer JWT verification is required -|Local Verification or Introspection -|Local Verification +|Requires Bearer JWT verification +|Local verification or introspection +|Local verification |Introspection -|Bearer Opaque Token verification is required +|Requires Bearer opaque token verification |Introspection |No |Introspection -|Refreshing JsonWebKey set for verifying JWT tokens +|Refreshing `JsonWebKey` set for verifying JWT tokens |Yes |Yes |No -|Represent token as Principal +|Represent token as `Principal`` |Yes |Yes |Yes -|Inject JWT as MP JWT JsonWebToken -|Yes +|Inject JWT as MP JSON Web Token (JWT) |Yes |No |Authorization Code Flow @@ -168,60 +193,79 @@ Below is a summary of the options. |Yes |No |No -|UserInfo support +|User info support |Yes |No |No -|Pem Key format support +|PEM key format support |No |Yes |No |SecretKey support |No -|In JsonWebKey format +|In JSON Web Key (JWK) format |No -|InnerSigned/Encrypted or Encrypted tokens +|Inner-signed and encrypted or encrypted tokens |Introspection -|Local Verification +|Local verification |Introspection -|Custom Token Verification +|Custom token verification |No -|With Injected JWTParser +|With injected JWT parser |No -|Accept JWT as cookie +|Accept JWT as a cookie |No |Yes |No |=== [[identity-providers]] -== Identity Providers +== Identity providers + -`IdentityProvider` converts the authentication credentials provided by `HttpAuthenticationMechanism` to `SecurityIdentity`. +The JPA `IdentityProvider` creates a `SecurityIdentity` instance, which is used during user authentication to verify and authorize access requests making your Quarkus application secure. -Some extensions such as `OIDC`, `OAuth2`, `SmallRye JWT` have the inlined `IdentityProvider` implementations which are specific to the supported authentication flow. -For example, `quarkus-oidc` uses its own `IdentityProvider` to convert a token to `SecurityIdentity`. -If you use `Basic` or `Form` HTTP-based authentication then you have to add an `IdentityProvider` which can convert a username and password to `SecurityIdentity`. +`IdentityProvider` converts the authentication credentials provided by `HttpAuthenticationMechanism` to a `SecurityIdentity` instance. -* For more information about `Basic` or `Form` HTTP-based authentication, see: -** xref:security-getting-started.adoc[JPA IdentityProvider] -** xref:security-jdbc.adoc[JDBC IdentityProvider] -** xref:security-ldap.adoc[LDAP IdentityProvider] +Some extensions, for example, `OIDC`, `OAuth2`, and `SmallRye JWT` have inline `IdentityProvider` implementations specific to the supported authentication flow. +For example, `quarkus-oidc` uses its own `IdentityProvider` to convert a token to a `SecurityIdentity` instance. -* For a a Basic Authentication configuration walk-through using JPA, see: -** xref:security-getting-started.adoc[Getting Started With Security] guide. +If you use `Basic` or `Form` HTTP-based authentication then you must add an `IdentityProvider` instance that can convert a username and password to a `SecurityIdentity` instance. -* For testing, use the xref:security-testing.adoc#configuring-user-information[User Properties IdentityProvider] section with the `IdentityProvider` with already set usernames, passwords, and roles in `application.properties`. +* For more information about `Basic` or `Form` HTTP-based authentication, see the following resources: +** xref:security-getting-started.adoc[Secure a Quarkus application with Basic authentication] +** xref:security-jdbc.adoc[Using security with JDBC] +** xref:security-ldap.adoc[Using security with an LDAP realm] -== Combining Authentication Mechanisms +== Authorization -One can combine multiple authentication mechanisms if they get the authentication credentials from the different sources. -For example, combining built-in `Basic` and `quarkus-oidc` `Bearer` authentication mechanisms is allowed, but combining `quarkus-oidc` `Bearer` and `smallrye-jwt` authentication mechanisms is not allowed because both will attempt to verify the token extracted from the HTTP `Authorization Bearer` scheme. +Quarkus also supports role-based access control (RBAC). +For more information about RBAC and other authorization options in Quarkus, see xref:security-authorization.adoc[Security authorization]. -=== Path Specific Authentication Mechanism +== Quarkus Security customization + +Quarkus Security is highly customizable. You can customize the following core security components of Quarkus: +* `HttpAuthenticationMechanism` +* `IdentityProvider` +* `SecurityidentityAugmentor` + +For more information about customizing Quarkus Security including reactive security, and how to register a security provider, see xref:security-customization.adoc[Security customization]. + +== Combining authentication mechanisms + +If the user credentials are provided by different sources, you can combine authentication mechanisms. +For example, you can combine built-in `Basic` and `quarkus-oidc` `Bearer` authentication mechanisms. + +[IMPORTANT] +==== +You cannot combine the `quarkus-oidc` `Bearer` and `smallrye-jwt` authentication mechanisms because both mechanisms attempt to verify the token extracted from the HTTP `Authorization Bearer` scheme. +==== + +=== Path-specific authentication mechanisms + +The following configuration example demonstrates how you can enforce a single selectable authentication mechanism for a given request path: -You can enforce that only a single authentication mechanism is selected for a given request path, for example: [source,properties] ---- quarkus.http.auth.permission.basic-or-bearer.paths=/service @@ -236,64 +280,56 @@ quarkus.http.auth.permission.bearer.policy=authenticated quarkus.http.auth.permission.bearer.auth-mechanism=bearer ---- -The value of the `auth-mechanism` property must match the authentication scheme supported by HttpAuthenticationMechanism such as `basic` or `bearer` or `form`, etc. - -== Proactive Authentication - -By default, Quarkus does what we call proactive authentication. This means that if an incoming request has a -credential then that request will always be authenticated (even if the target page does not require authentication). - -See xref:security-built-in-authentication.adoc#proactive-authentication[Proactive Authentication] for more information. - -== Authorization - -See xref:security-authorization.adoc[Security Authorization] for more information about Role Based Access Control and other authorization options. - -== Customization and other useful tips +Ensure that the value of the `auth-mechanism` property matches the authentication scheme supported by `HttpAuthenticationMechanism`, for example, `basic`, `bearer`, or `form`. -Quarkus Security is highly customizable. One can register custom ``HttpAuthenticationMechanism``s, ``IdentityProvider``s and ``SecurityidentityAugmentor``s. +== Proactive authentication -See xref:security-customization.adoc[Security Customization] for more information about customizing Quarkus Security and other useful tips about the reactive security, registering the security providers, etc. +By default, Quarkus does proactive authentication, which means that all incoming requests with credentials are authenticated regardless of whether the target page requires authentication. +For more information, see xref:security-built-in-authentication.adoc#proactive-authentication[Proactive authentication]. -== Secure connections with SSL +== Secure connections with SSL/TLS -See the xref:http-reference.adoc#ssl[Supporting secure connections with SSL] guide for more information. + For more information about how Quarkus supports secure connections with SSL/TLS, see the xref:http-reference.adoc#ssl[HTTP reference] information. -== Cross-Origin Resource Sharing +== Cross-origin resource sharing -If you plan to make your Quarkus application accessible to another application running on a different domain, you will need to configure CORS (Cross-Origin Resource Sharing). Please read the xref:http-reference.adoc#cors-filter[HTTP CORS documentation] for more information. +To make your Quarkus application accessible to another application running on a different domain, you need to configure cross-origin resource sharing (CORS). +For more information about the CORS filter that is provided by Quarkus, see the xref:http-reference.adoc#cors-filter[HTTP reference] information. -== Cross-Site Request Forgery Prevention +== Cross-site Request Forgery (CSRF) prevention -Quarkus Security provides a RESTEasy Reactive filter which can help protect against a https://owasp.org/www-community/attacks/csrf[Cross-Site Request Forgery] attack. Please read the xref:csrf-prevention.adoc[Cross-Site Request Forgery Prevention] guide for more information. +Quarkus Security provides a RESTEasy Reactive filter that can protect your applications against a https://owasp.org/www-community/attacks/csrf[Cross-Site Request Forgery] attack. +For more information, see xref:security-csrf-prevention.adoc[Cross-Site Request Forgery Prevention]. == SameSite cookies -Please see xref:http-reference.adoc#same-site-cookie[SameSite cookies] for information about adding a https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite[SameSite] cookie property to any of the cookies set by a Quarkus endpoint. +You can add a https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite[SameSite] cookie property to any of the cookies set by a Quarkus endpoint. +For more information, see xref:http-reference.adoc#same-site-cookie[SameSite cookies]. -== Testing +== Secret engines +Secrets engines are components that store, generate, or encrypt data. -See xref:security-testing.adoc[Security Testing] for more information about testing Quarkus Security. - -== Secret Engines - -=== Vault -Quarkus provides a very comprehensive HashiCorp Vault support, please see the link:{vault-guide}[Quarkus and HashiCorp Vault] documentation for more information. +Quarkus provides comprehensive HashiCorp Vault support. +For more information, see the link:{vault-guide}[Quarkus and HashiCorp Vault] documentation. == Secure serialization -When using Security along with RESTEasy Reactive and Jackson, Quarkus can limit the fields that are included in JSON serialization based on the configured security. See the xref:resteasy-reactive.adoc#secure-serialization[RESTEasy Reactive documentation] for details. +If your Quarkus Security architecture includes RESTEasy Reactive and Jackson, Quarkus can limit the fields that are included in JSON serialization based on the configured security. +For more information, see xref:resteasy-reactive.adoc#secure-serialization[Writing REST services with RESTEasy Reactive]. == National Vulnerability Database -Most of Quarkus tags have been registered in link:https://nvd.nist.gov[National Vulnerability Database] (NVD) using a Common Platform Enumeration (CPE) name format. -All registered Quarkus CPE names can be found using link:https://nvd.nist.gov/products/cpe/search/results?namingFormat=2.3&keyword=quarkus[this search query]. -If a Quarkus tag represented by the given CPE name entry is affected by some CVE then you'll be able to follow a provided link to that CVE. +Most of the Quarkus tags are registered in the US link:https://nvd.nist.gov[National Vulnerability Database] (NVD) in Common Platform Enumeration (CPE) name format. +To view the registered Quarkus CPE names, use link:https://nvd.nist.gov/products/cpe/search/results?namingFormat=2.3&keyword=quarkus[this search query]. + +If the NVE database flags a CVE against a Quarkus tag, a link that provides more details about the CVE is added to the given CPE name entry. -We will be asking the NVD CPE team to update the list as well as link Quarkus CPE name entries with the related CVEs on a regular basis. -If you work with the link:https://jeremylong.github.io/DependencyCheck/dependency-check-maven/[OWASP Dependency Check Plugin] which is using NVD feeds to detect the vulnerabilities at the application build time and see a false positive reported then please re-open link:https://github.com/quarkusio/quarkus/issues/2611[this issue] and provide the details. +The NVD CPE team updates the list regularly, but if you encounter a false positive, report the details by creating an issue in the link:https://github.com/quarkusio/quarkus/issues/2611[quarkusio] repository. -You can add `OWASP Dependency Check Plugin` to your project's `pom.xml` like this: +You can detect the vulnerabilities at the application build time with an NVD feed by using the Maven link:https://jeremylong.github.io/DependencyCheck/dependency-check-maven/[OWASP Dependency check plugin]. + + +To add the OWASP Dependency check plugin to your Quarkus Maven project, add the following XML configuration to the `pom.xml` file: [source,xml] ---- @@ -304,9 +340,12 @@ You can add `OWASP Dependency Check Plugin` to your project's `pom.xml` like thi ---- -where `owasp-dependency-check-plugin.version` should be set to `7.1.1` or later. +[IMPORTANT] +==== +Set the `owasp-dependency-check-plugin.version` value to `7.1.1` or later. +==== -You can configure the plugin like this: +Next, configure the plugin as follows: [source,xml] ---- @@ -324,18 +363,14 @@ You can configure the plugin like this: ---- -You can change `failBuildOnCVSS` value to detect less severe issues as well. - -A suppression list may vary depending on whether you'd like to keep checking the false positives to avoid missing something or not. -For example, it can look like this: - +To detect less severe issues, adjust the value of `failBuildOnCVSS` to suppress the false positives, as demonstrated in the following code sample: [source,xml] ---- @@ -389,11 +424,18 @@ For example, it can look like this: Suppress the false positive CPE for graal-sdk to GraalVM (the JVM distribution) ]]> - ^org\.graalvm\.sdk:graal-sdk:.*$ - cpe:/a:oracle:graalvm + ^org\.graalvm\.sdk:g like this ---- -Such a suppression list has to be carefully prepared and revisited from time to time. You should consider making individual suppressions time limited by adding an `until` attribute, for example: `...`. It will let you doublecheck that only the same known false positives are reported when the suppression period expires, and after reviewing the report you can set a new expiry date. +Ensure that you review and update the suppression list regularly to ensure that the results are up to date. +You can optionally apply a time limit to individual suppressions by adding an expiry attribute, as outlined in the following example: + +`...` +You can adjust the expiry date if you need to. + +== Quarkus Security testing +When testing Quarkus security, ensure that your `IdentityProvider` is already set with usernames, passwords, and roles in `application.properties`. +For more information about testing Quarkus Security, see xref:security-testing.adoc#configuring-user-information[Configuring user information]. \ No newline at end of file From ab4d5f10065db20cf37e602626852f1fe3e84eac Mon Sep 17 00:00:00 2001 From: Rakhmad Azhari Date: Fri, 23 Sep 2022 11:32:19 +0700 Subject: [PATCH 19/81] Fix code example on search method. Character is interface without properties, so to access `name` and `surname` we use `getName()` and `getSurname()` methods. (cherry picked from commit df6284d843d6234e5594583124bd0f6a953a20c0) --- docs/src/main/asciidoc/smallrye-graphql.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/smallrye-graphql.adoc b/docs/src/main/asciidoc/smallrye-graphql.adoc index c6f79f30585ba..832deb1dfbae2 100644 --- a/docs/src/main/asciidoc/smallrye-graphql.adoc +++ b/docs/src/main/asciidoc/smallrye-graphql.adoc @@ -742,8 +742,8 @@ Update `GalaxyService` to provide search: .collect(Collectors.toList()); results.addAll(matchingFilms); List matchingCharacters = getAllCharacters().stream() - .filter(character -> character.name.contains(query) - || character.surname.contains(query)) + .filter(character -> character.getName().contains(query) + || character.getSurname().contains(query)) .collect(Collectors.toList()); results.addAll(matchingCharacters); return results; From 71f28612cc0a9a716f901db98a8dd66a46eff6aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Sep 2022 21:39:25 +0000 Subject: [PATCH 20/81] Bump mariadb-java-client from 3.0.7 to 3.0.8 Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.7 to 3.0.8. - [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases) - [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md) - [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.7...3.0.8) --- updated-dependencies: - dependency-name: org.mariadb.jdbc:mariadb-java-client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 1f78222aef29c9aa1352dc5caff51306396d679c) --- 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 6191a1abcfe7f..4f9e5ae2302d9 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -118,7 +118,7 @@ 2.3.2 2.1.214 42.5.0 - 3.0.7 + 3.0.8 8.0.30 11.2.0.jre11 1.6.7 From dcdecaaa338b83d8a8d52af301dfab0b598ebc2c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 22 Sep 2022 18:33:08 +0100 Subject: [PATCH 21/81] Add an option to clear service test results in DevUI for Keycloak (cherry picked from commit 98a38ddc722b8aabc421017c7a60dc15ce6c6e5e) --- .../main/resources/dev-templates/provider.html | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/extensions/oidc/deployment/src/main/resources/dev-templates/provider.html b/extensions/oidc/deployment/src/main/resources/dev-templates/provider.html index acafa6886780e..853e969933789 100644 --- a/extensions/oidc/deployment/src/main/resources/dev-templates/provider.html +++ b/extensions/oidc/deployment/src/main/resources/dev-templates/provider.html @@ -405,6 +405,10 @@ window.open("http://localhost:" + port + servicePath); } +function clearResults() { + $('#results').text(''); +} + {/script} {#body} @@ -562,9 +566,12 @@
Decoded

-
+
+ + +
@@ -620,6 +627,9 @@
Decoded
+ + + {#else if info:oidcGrantType is 'client_credentials'} @@ -658,6 +668,9 @@
Decoded
+ + + {/if} From 33ee0660846b6c52e8e4d5b81d0e02f42cf7b859 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 23 Sep 2022 16:08:19 +0100 Subject: [PATCH 22/81] Increase token lifespans in the default realm created by DevServices for Keycloak (cherry picked from commit 27e6bc90383b6d731ab92816084e5202a7c35c95) --- .../devservices/keycloak/KeycloakDevServicesProcessor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java index 5a11fad2a485c..ec87b2893adb6 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java @@ -644,6 +644,8 @@ private RealmRepresentation createRealmRep() { realm.setEnabled(true); realm.setUsers(new ArrayList<>()); realm.setClients(new ArrayList<>()); + realm.setAccessTokenLifespan(600); + realm.setSsoSessionMaxLifespan(600); RolesRepresentation roles = new RolesRepresentation(); List realmRoles = new ArrayList<>(); From 6e1d849f821badbf3df10fe8fdb7e5cfe6101a43 Mon Sep 17 00:00:00 2001 From: cui fliter Date: Sun, 25 Sep 2022 20:49:41 +0800 Subject: [PATCH 23/81] all: fix some typos Signed-off-by: cui fliter (cherry picked from commit 3cbafc34aef6042c30e26985f412b5bb2e109abc) --- adr/0001-community-discussions.adoc | 2 +- .../main/asciidoc/security-openid-connect-client-reference.adoc | 2 +- .../java/io/quarkus/arc/impl/AroundInvokeInvocationContext.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adr/0001-community-discussions.adoc b/adr/0001-community-discussions.adoc index fbbfe4462c5e7..5708ac9269b74 100644 --- a/adr/0001-community-discussions.adoc +++ b/adr/0001-community-discussions.adoc @@ -41,7 +41,7 @@ User looking for a job involving Quarkus - where do he go look ? Not proposed since it is just another waterhose of info with no good ability to ignore/consume as needed/wanted. === enable discussions on github.com/quarkusio/quarkus-insights -Considered as quarkus insights is an existing "community" and let us have separate permissons for moderators/triage but some suggested it might be hard to find. +Considered as quarkus insights is an existing "community" and let us have separate permissions for moderators/triage but some suggested it might be hard to find. === enable discussions on github.com/quarkusio/community Downside is that labels etc. would need to be kept in sync and that searches in github.com/quarkusio would not show up community/discussions. 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 9dcd5aebf7359..eb80ac9ee6a6b 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc @@ -535,7 +535,7 @@ or with the secret retrieved from a xref:credentials-provider.adoc[CredentialsPr quarkus.oidc-client.auth-server-url=http://localhost:8180/auth/realms/quarkus/ quarkus.oidc-client.client-id=quarkus-app -# This is a key which will be used to retrieve a secret from the map of credentails returned from CredentialsProvider +# This is a key which will be used to retrieve a secret from the map of credentials returned from CredentialsProvider quarkus.oidc-client.credentials.client-secret.provider.key=mysecret-key # Set it only if more than one CredentialsProvider can be registered quarkus.oidc-client.credentials.client-secret.provider.name=oidc-credentials-provider diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AroundInvokeInvocationContext.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AroundInvokeInvocationContext.java index 0ebb9780c6f83..c964fc94642af 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AroundInvokeInvocationContext.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AroundInvokeInvocationContext.java @@ -18,7 +18,7 @@ * asynchronously, possibly on a different thread. *

* Note that context data and method parameters are mutable and are not guarded/synchronized. We expect them to be modified - * before or after dispatch. If modified before and after dispatch an unpredicatble behavior may occur. + * before or after dispatch. If modified before and after dispatch an unpredictable behavior may occur. */ class AroundInvokeInvocationContext extends AbstractInvocationContext { From 1f2ac33d3a24c5d0786539b7dcdeb84c915f78e8 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Mon, 19 Sep 2022 15:58:44 +0200 Subject: [PATCH 24/81] Catch exception happening in the gRPC interceptors and close the call immediately. Fix https://github.com/quarkusio/quarkus/issues/28053. (cherry picked from commit 747f6eeb2787ebd484f16e42f96dba1505deae62) --- .../FailingInInterceptorTest.java | 70 ++++++++++++++ .../interceptors/FailingInterceptorTest.java | 60 ++++++++++++ .../GrpcDuplicatedContextGrpcInterceptor.java | 92 +++++++++++++------ 3 files changed, 194 insertions(+), 28 deletions(-) create mode 100644 extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/FailingInInterceptorTest.java create mode 100644 extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/FailingInterceptorTest.java diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/FailingInInterceptorTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/FailingInInterceptorTest.java new file mode 100644 index 0000000000000..7f2278a6f6493 --- /dev/null +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/FailingInInterceptorTest.java @@ -0,0 +1,70 @@ +package io.quarkus.grpc.server.interceptors; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.Duration; + +import javax.enterprise.context.ApplicationScoped; + +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.grpc.ForwardingServerCall; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; +import io.grpc.examples.helloworld.*; +import io.quarkus.grpc.GlobalInterceptor; +import io.quarkus.grpc.GrpcClient; +import io.quarkus.grpc.server.services.HelloService; +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.mutiny.Uni; + +public class FailingInInterceptorTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class) + .addPackage(GreeterGrpc.class.getPackage()) + .addClasses(MyFailingInterceptor.class, GreeterBean.class, HelloRequest.class, HelloService.class)); + + @GrpcClient + Greeter greeter; + + @Test + void test() { + Uni result = greeter.sayHello(HelloRequest.newBuilder().setName("ServiceA").build()); + assertThatThrownBy(() -> result.await().atMost(Duration.ofSeconds(4))) + .isInstanceOf(StatusRuntimeException.class) + .hasMessageContaining("UNKNOWN"); + } + + @ApplicationScoped + @GlobalInterceptor + public static class MyFailingInterceptor implements ServerInterceptor { + + @Override + public ServerCall.Listener interceptCall(ServerCall call, Metadata headers, + ServerCallHandler next) { + return next + .startCall(new ForwardingServerCall.SimpleForwardingServerCall(call) { + + @Override + public void sendMessage(RespT message) { + throw new IllegalArgumentException("BOOM"); + } + + @Override + public void close(Status status, Metadata trailers) { + super.close(status, trailers); + } + }, headers); + } + } + +} diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/FailingInterceptorTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/FailingInterceptorTest.java new file mode 100644 index 0000000000000..5fb5b090cc92c --- /dev/null +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/FailingInterceptorTest.java @@ -0,0 +1,60 @@ +package io.quarkus.grpc.server.interceptors; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.Duration; + +import javax.enterprise.context.ApplicationScoped; + +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.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import io.grpc.StatusRuntimeException; +import io.grpc.examples.helloworld.Greeter; +import io.grpc.examples.helloworld.GreeterBean; +import io.grpc.examples.helloworld.GreeterGrpc; +import io.grpc.examples.helloworld.HelloReply; +import io.grpc.examples.helloworld.HelloRequest; +import io.quarkus.grpc.GlobalInterceptor; +import io.quarkus.grpc.GrpcClient; +import io.quarkus.grpc.server.services.HelloService; +import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.mutiny.Uni; + +public class FailingInterceptorTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class) + .addPackage(GreeterGrpc.class.getPackage()) + .addClasses(MyFailingInterceptor.class, GreeterBean.class, HelloRequest.class, HelloService.class)); + + @GrpcClient + Greeter greeter; + + @Test + void test() { + Uni result = greeter.sayHello(HelloRequest.newBuilder().setName("ServiceA").build()); + assertThatThrownBy(() -> result.await().atMost(Duration.ofSeconds(4))) + .isInstanceOf(StatusRuntimeException.class) + .hasMessageContaining("UNKNOWN"); + } + + @ApplicationScoped + @GlobalInterceptor + public static class MyFailingInterceptor implements ServerInterceptor { + + @Override + public ServerCall.Listener interceptCall(ServerCall call, Metadata headers, + ServerCallHandler next) { + throw new IllegalArgumentException("BOOM!"); + } + } + +} diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/context/GrpcDuplicatedContextGrpcInterceptor.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/context/GrpcDuplicatedContextGrpcInterceptor.java index 21d7b0ee42249..d4ca59da2e55c 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/context/GrpcDuplicatedContextGrpcInterceptor.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/context/GrpcDuplicatedContextGrpcInterceptor.java @@ -2,6 +2,8 @@ import static io.quarkus.vertx.core.runtime.context.VertxContextSafetyToggle.setContextSafe; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; import java.util.function.Supplier; import javax.enterprise.context.ApplicationScoped; @@ -13,9 +15,11 @@ import io.grpc.ServerCall; import io.grpc.ServerCallHandler; import io.grpc.ServerInterceptor; +import io.grpc.Status; import io.quarkus.grpc.GlobalInterceptor; import io.smallrye.common.vertx.VertxContext; import io.vertx.core.Context; +import io.vertx.core.Handler; import io.vertx.core.Vertx; @ApplicationScoped @@ -44,7 +48,7 @@ public ServerCall.Listener interceptCall(ServerCall(() -> next.startCall(call, headers), local); + return new ListenedOnDuplicatedContext<>(call, () -> next.startCall(call, headers), local); } else { log.warn("Unable to run on a duplicated context - interceptor not called on the Vert.x event loop"); return next.startCall(call, headers); @@ -56,67 +60,99 @@ public int getPriority() { return Integer.MAX_VALUE; } - static class ListenedOnDuplicatedContext extends ServerCall.Listener { + static class ListenedOnDuplicatedContext extends ServerCall.Listener { private final Context context; private final Supplier> supplier; + private final ServerCall call; private ServerCall.Listener delegate; - public ListenedOnDuplicatedContext(Supplier> supplier, Context context) { + private final AtomicBoolean closed = new AtomicBoolean(); + + public ListenedOnDuplicatedContext(ServerCall call, Supplier> supplier, + Context context) { this.context = context; this.supplier = supplier; + this.call = call; } private synchronized ServerCall.Listener getDelegate() { if (delegate == null) { - delegate = supplier.get(); + try { + delegate = supplier.get(); + } catch (Throwable t) { + // If the interceptor supplier throws an exception, catch it, and close the call. + log.warnf("Unable to retrieve gRPC Server call listener", t); + close(t); + return null; + } } return delegate; } - @Override - public void onMessage(ReqT message) { + private void close(Throwable t) { + if (closed.compareAndSet(false, true)) { + call.close(Status.fromThrowable(t), new Metadata()); + } + } + + private void invoke(Consumer> invocation) { if (Vertx.currentContext() == context) { - getDelegate().onMessage(message); + ServerCall.Listener listener = getDelegate(); + if (listener == null) { + return; + } + try { + invocation.accept(listener); + } catch (Throwable t) { + close(t); + } } else { - context.runOnContext(x -> getDelegate().onMessage(message)); + context.runOnContext(new Handler() { + @Override + public void handle(Void x) { + ServerCall.Listener listener = ListenedOnDuplicatedContext.this.getDelegate(); + if (listener == null) { + return; + } + try { + invocation.accept(listener); + } catch (Throwable t) { + close(t); + } + } + }); } } + @Override + public void onMessage(ReqT message) { + invoke(new Consumer>() { + @Override + public void accept(ServerCall.Listener listener) { + listener.onMessage(message); + } + }); + } + @Override public void onReady() { - if (Vertx.currentContext() == context) { - getDelegate().onReady(); - } else { - context.runOnContext(x -> getDelegate().onReady()); - } + invoke(ServerCall.Listener::onReady); } @Override public void onHalfClose() { - if (Vertx.currentContext() == context) { - getDelegate().onHalfClose(); - } else { - context.runOnContext(x -> getDelegate().onHalfClose()); - } + invoke(ServerCall.Listener::onHalfClose); } @Override public void onCancel() { - if (Vertx.currentContext() == context) { - getDelegate().onCancel(); - } else { - context.runOnContext(x -> getDelegate().onCancel()); - } + invoke(ServerCall.Listener::onCancel); } @Override public void onComplete() { - if (Vertx.currentContext() == context) { - getDelegate().onComplete(); - } else { - context.runOnContext(x -> getDelegate().onComplete()); - } + invoke(ServerCall.Listener::onComplete); } } } From 965142345862f7744d81bd2778bc4f40c2c1ea4f Mon Sep 17 00:00:00 2001 From: Manyanda Chitimbo Date: Mon, 26 Sep 2022 09:06:18 +0200 Subject: [PATCH 25/81] docs(redis): fix broken links Update docs/src/main/asciidoc/redis.adoc Co-authored-by: Guillaume Smet (cherry picked from commit 8015231d7e6a4e37f1a6b25057ed7466e1f3f203) --- docs/src/main/asciidoc/redis-reference.adoc | 2 +- docs/src/main/asciidoc/redis.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/redis-reference.adoc b/docs/src/main/asciidoc/redis-reference.adoc index 0d5f969364a0f..5976003d40733 100644 --- a/docs/src/main/asciidoc/redis-reference.adoc +++ b/docs/src/main/asciidoc/redis-reference.adoc @@ -713,7 +713,7 @@ public static class MyExampleCustomizer implements RedisOptionsCustomizer { === Dev Services -See link:redis-dev-services.adoc[Redis Dev Service]. +See xref:redis-dev-services.adoc[Redis Dev Service]. [[redis-configuration-reference]] == Configuration Reference diff --git a/docs/src/main/asciidoc/redis.adoc b/docs/src/main/asciidoc/redis.adoc index cedbf72431104..57a2e59c3372f 100644 --- a/docs/src/main/asciidoc/redis.adoc +++ b/docs/src/main/asciidoc/redis.adoc @@ -526,4 +526,4 @@ Once the build is finished, you can run the executable with: == Going further -To learn more about the Quarkus Redis extension, check link:redis-reference.adoc[the Redis extension reference guide]. +To learn more about the Quarkus Redis extension, check xref:redis-reference.adoc[the Redis extension reference guide]. From 816ea24b5a8b7f7b14067adc616f806f1b423b78 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 23 Sep 2022 09:39:17 +0300 Subject: [PATCH 26/81] Take quarkus.package.output-directory into account when launching native tests Fixes: #28131 (cherry picked from commit a8c0aee2a1e76c06490568ebe4b8d6ba7dad7161) --- .../common/DefaultNativeImageLauncher.java | 48 +++++++++++-------- .../test/common/NativeImageLauncher.java | 2 + .../test/junit/NativeTestExtension.java | 1 + .../launcher/NativeImageLauncherProvider.java | 10 +++- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/DefaultNativeImageLauncher.java b/test-framework/common/src/main/java/io/quarkus/test/common/DefaultNativeImageLauncher.java index 33345980da115..2680369cdbe12 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/DefaultNativeImageLauncher.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/DefaultNativeImageLauncher.java @@ -34,6 +34,7 @@ public class DefaultNativeImageLauncher implements NativeImageLauncher { private String testProfile; private List argLine; private String nativeImagePath; + private String configuredOutputDirectory; private Class testClass; private Process quarkusProcess; @@ -48,6 +49,7 @@ public void init(NativeImageInitContext initContext) { this.waitTimeSeconds = initContext.waitTime().getSeconds(); this.testProfile = initContext.testProfile(); this.nativeImagePath = initContext.nativeImagePath(); + this.configuredOutputDirectory = initContext.getConfiguredOutputDirectory(); this.argLine = initContext.argLine(); this.testClass = initContext.testClass(); } @@ -173,7 +175,7 @@ private void waitForStartedSupplier(Supplier startedSupplier, Process q } } - private static String guessPath(Class testClass) { + private String guessPath(Class testClass) { //ok, lets make a guess //this is a horrible hack, but it is intended to make this work in IDE's @@ -203,41 +205,47 @@ private static String guessPath(Class testClass) { "Unable to automatically find native image, please set the native.image.path to the native executable you wish to test"); } - private static String guessPath(final URL url) { + private String guessPath(final URL url) { if (url == null) { return null; } + String file = null; if (url.getProtocol().equals("file") && url.getPath().endsWith("test-classes/")) { //we have the maven test classes dir File testClasses = new File(url.getPath()); - for (File file : testClasses.getParentFile().listFiles()) { - if (isNativeExecutable(file)) { - logGuessedPath(file.getAbsolutePath()); - return file.getAbsolutePath(); - } - } + file = guessPathFromDir(testClasses.getParentFile()); } else if (url.getProtocol().equals("file") && url.getPath().endsWith("test/")) { //we have the gradle test classes dir, build/classes/java/test File testClasses = new File(url.getPath()); - for (File file : testClasses.getParentFile().getParentFile().getParentFile().listFiles()) { - if (isNativeExecutable(file)) { - logGuessedPath(file.getAbsolutePath()); - return file.getAbsolutePath(); - } - } + file = guessPathFromDir(testClasses.getParentFile().getParentFile().getParentFile()); } else if (url.getProtocol().equals("file") && url.getPath().contains("/target/surefire/")) { //this will make mvn failsafe:integration-test work String path = url.getPath(); int index = path.lastIndexOf("/target/"); File targetDir = new File(path.substring(0, index) + "/target/"); - for (File file : targetDir.listFiles()) { - if (isNativeExecutable(file)) { - logGuessedPath(file.getAbsolutePath()); - return file.getAbsolutePath(); - } - } + file = guessPathFromDir(targetDir); } + return file; + } + + private String guessPathFromDir(File dir) { + if (dir == null) { + return null; + } + if (configuredOutputDirectory != null) { + dir = dir.toPath().resolve(configuredOutputDirectory).toFile(); + } + File[] files = dir.listFiles(); + if (files == null) { + return null; + } + for (File file : files) { + if (isNativeExecutable(file)) { + logGuessedPath(file.getAbsolutePath()); + return file.getAbsolutePath(); + } + } return null; } diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/NativeImageLauncher.java b/test-framework/common/src/main/java/io/quarkus/test/common/NativeImageLauncher.java index 5121ceebd9545..3ade1d9704460 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/NativeImageLauncher.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/NativeImageLauncher.java @@ -6,6 +6,8 @@ interface NativeImageInitContext extends InitContext { String nativeImagePath(); + String getConfiguredOutputDirectory(); + Class testClass(); } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java index c3a10dae9c048..218d5eed831f5 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java @@ -217,6 +217,7 @@ public void close() { } }, System.getProperty("native.image.path"), + config.getOptionalValue("quarkus.package.output-directory", String.class).orElse(null), requiredTestClass)); return launcher; } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/launcher/NativeImageLauncherProvider.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/launcher/NativeImageLauncherProvider.java index 5a829c7f3c07a..ec9d9f56039e8 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/launcher/NativeImageLauncherProvider.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/launcher/NativeImageLauncherProvider.java @@ -45,6 +45,7 @@ public NativeImageLauncher create(CreateContext context) { ConfigUtil.argLineValue(config), context.devServicesLaunchResult(), System.getProperty("native.image.path"), + config.getOptionalValue("quarkus.package.output-directory", String.class).orElse(null), context.testClass())); return launcher; } else { @@ -57,12 +58,14 @@ public static class DefaultNativeImageInitContext extends DefaultInitContextBase private final String nativeImagePath; private final Class testClass; + private final String configuredOutputDirectory; public DefaultNativeImageInitContext(int httpPort, int httpsPort, Duration waitTime, String testProfile, List argLine, ArtifactLauncher.InitContext.DevServicesLaunchResult devServicesLaunchResult, - String nativeImagePath, Class testClass) { + String nativeImagePath, String configuredOutputDirectory, Class testClass) { super(httpPort, httpsPort, waitTime, testProfile, argLine, devServicesLaunchResult); this.nativeImagePath = nativeImagePath; + this.configuredOutputDirectory = configuredOutputDirectory; this.testClass = testClass; } @@ -71,6 +74,11 @@ public String nativeImagePath() { return nativeImagePath; } + @Override + public String getConfiguredOutputDirectory() { + return configuredOutputDirectory; + } + @Override public Class testClass() { return testClass; From 4c70759366fc1aa5716886f5724a1c7d6e7d9633 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Fri, 23 Sep 2022 15:57:36 +0100 Subject: [PATCH 27/81] Use KeyMap source only for wildcard defaults (cherry picked from commit 1c0f8e7f6340f4c614c31089f777688631106cf7) --- .../runtime/configuration/ConfigUtils.java | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java index a0e3d24df1d41..d971720ba9488 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java @@ -5,6 +5,8 @@ import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_PROFILE; import static io.smallrye.config.SmallRyeConfig.SMALLRYE_CONFIG_PROFILE_PARENT; import static io.smallrye.config.SmallRyeConfigBuilder.META_INF_MICROPROFILE_CONFIG_PROPERTIES; +import static java.lang.Integer.MAX_VALUE; +import static java.lang.Integer.MIN_VALUE; import java.io.IOException; import java.net.URL; @@ -13,7 +15,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -39,13 +40,13 @@ import io.smallrye.config.EnvConfigSource; import io.smallrye.config.FallbackConfigSourceInterceptor; import io.smallrye.config.KeyMap; -import io.smallrye.config.KeyMapBackedConfigSource; import io.smallrye.config.Priorities; import io.smallrye.config.ProfileConfigSourceInterceptor; import io.smallrye.config.RelocateConfigSourceInterceptor; import io.smallrye.config.SmallRyeConfig; import io.smallrye.config.SmallRyeConfigBuilder; import io.smallrye.config.SysPropConfigSource; +import io.smallrye.config.common.MapBackedConfigSource; import io.smallrye.config.common.utils.ConfigSourceUtil; /** @@ -115,10 +116,8 @@ public static SmallRyeConfigBuilder configBuilder(final boolean runTime, final b builder.addDiscoveredValidator(); builder.withDefaultValue(UUID_KEY, UUID.randomUUID().toString()); builder.withSources(new DotEnvConfigSourceProvider()); - builder.withSources( - new DefaultsConfigSource(loadBuildTimeRunTimeValues(), "BuildTime RunTime Fixed", Integer.MAX_VALUE)); - builder.withSources( - new DefaultsConfigSource(loadRunTimeDefaultValues(), "RunTime Defaults", Integer.MIN_VALUE + 100)); + builder.withSources(new DefaultsConfigSource(loadBuildTimeRunTimeValues(), "BuildTime RunTime Fixed", MAX_VALUE)); + builder.withSources(new DefaultsConfigSource(loadRunTimeDefaultValues(), "RunTime Defaults", MIN_VALUE + 100)); } else { List sources = new ArrayList<>(); sources.addAll(classPathSources(META_INF_MICROPROFILE_CONFIG_PROPERTIES, classLoader)); @@ -381,43 +380,35 @@ public Set getPropertyNames() { } } - static class DefaultsConfigSource extends KeyMapBackedConfigSource { + static class DefaultsConfigSource extends MapBackedConfigSource { private final KeyMap wildcards; - private final Set propertyNames; public DefaultsConfigSource(final Map properties, final String name, final int ordinal) { - super(name, ordinal, propertiesToKeyMap(properties)); + // Defaults may contain wildcards, but we don't want to expose them in getPropertyNames, so we need to filter them + super(name, filterWildcards(properties), ordinal); this.wildcards = new KeyMap<>(); - this.propertyNames = new HashSet<>(); for (Map.Entry entry : properties.entrySet()) { if (entry.getKey().contains("*")) { this.wildcards.findOrAdd(entry.getKey()).putRootValue(entry.getValue()); - } else { - this.propertyNames.add(entry.getKey()); } } } - @Override - public Set getPropertyNames() { - return propertyNames; - } - @Override public String getValue(final String propertyName) { String value = super.getValue(propertyName); return value == null ? wildcards.findRootValue(propertyName) : value; } - private static KeyMap propertiesToKeyMap(final Map properties) { - KeyMap keyMap = new KeyMap<>(); + private static Map filterWildcards(final Map properties) { + Map filtered = new HashMap<>(); for (Map.Entry entry : properties.entrySet()) { if (entry.getKey().contains("*")) { continue; } - keyMap.findOrAdd(entry.getKey()).putRootValue(entry.getValue()); + filtered.put(entry.getKey(), entry.getValue()); } - return keyMap; + return filtered; } } From bce18dc1d269c45f42451dd12aad42bd88b54efd Mon Sep 17 00:00:00 2001 From: Harald Albers Date: Mon, 26 Sep 2022 13:33:15 +0200 Subject: [PATCH 28/81] Fix broken link to IntelliJ remote debugging documentation Signed-off-by: Harald Albers (cherry picked from commit c1baaa1caf69a40b4f52b0769023a8d292ec6497) --- docs/src/main/asciidoc/deploying-to-kubernetes.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/deploying-to-kubernetes.adoc b/docs/src/main/asciidoc/deploying-to-kubernetes.adoc index 4128b0c53197d..c92d25fe88144 100644 --- a/docs/src/main/asciidoc/deploying-to-kubernetes.adoc +++ b/docs/src/main/asciidoc/deploying-to-kubernetes.adoc @@ -1052,7 +1052,7 @@ kubectl port-forward svc/ 5005:5005 Using this command, you'll forward the traffic from the "localhost:5005" to the kubernetes service running the java agent using the port "5005" which is the one that the java agent uses by default for remote debugging. You can also configure another java agent port using the property `quarkus.kubernetes.remote-debug.address-port`. -Finally, all you need to do is to configure your favorite IDE to attach the java agent process that is forwarded to `localhost:5005` and start to debug your application. For example, in IntelliJ IDEA, you can follow https://www.jetbrains.com/help/idea/tutorial-remote-debug.html:[this tutorial] to debug remote applications. +Finally, all you need to do is to configure your favorite IDE to attach the java agent process that is forwarded to `localhost:5005` and start to debug your application. For example, in IntelliJ IDEA, you can follow https://www.jetbrains.com/help/idea/tutorial-remote-debug.html[this tutorial] to debug remote applications. == Using existing resources From 9fc70293ee18af137876e4e58da5e71b1e582e9e Mon Sep 17 00:00:00 2001 From: Ozan Gunalp Date: Mon, 26 Sep 2022 12:53:31 +0100 Subject: [PATCH 29/81] Bump Reactive Messaging version to 3.20.0 (cherry picked from commit 793cfca8dc6e599b8b3a8b5fe03e7f4de3216ae0) --- 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 4f9e5ae2302d9..91efb672bf0e4 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -52,7 +52,7 @@ 1.0.13 2.7.0 2.26.0 - 3.19.1 + 3.20.0 1.1.2 1.2.1 1.3.5 From c1b28e6c5515ec11bc3b5f80d7491a6b24c5f5a3 Mon Sep 17 00:00:00 2001 From: Rostislav Svoboda Date: Mon, 26 Sep 2022 12:34:12 +0200 Subject: [PATCH 30/81] Use single quotes to fix rendering in the all-config guide (cherry picked from commit 3b8021d5649a237b3c6132504276dd7dffafea1f) --- .../envers/HibernateEnversBuildTimeConfigPersistenceUnit.java | 2 +- .../orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java | 2 +- ...ibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfigPersistenceUnit.java b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfigPersistenceUnit.java index c71c07716308e..5c439cb39cc41 100644 --- a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfigPersistenceUnit.java +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/HibernateEnversBuildTimeConfigPersistenceUnit.java @@ -24,7 +24,7 @@ public class HibernateEnversBuildTimeConfigPersistenceUnit { * * @asciidoclet */ - @ConfigItem(defaultValueDocumentation = "`true` if Hibernate ORM is enabled; `false` otherwise") + @ConfigItem(defaultValueDocumentation = "'true' if Hibernate ORM is enabled; 'false' otherwise") public Optional active = Optional.empty(); } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java index cda963fac09ac..7ba8bb8141912 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java @@ -26,7 +26,7 @@ public class HibernateOrmRuntimeConfigPersistenceUnit { * * @asciidoclet */ - @ConfigItem(defaultValueDocumentation = "`true` if Hibernate ORM is enabled; `false` otherwise") + @ConfigItem(defaultValueDocumentation = "'true' if Hibernate ORM is enabled; 'false' otherwise") public Optional active = Optional.empty(); /** diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java index 6979394ca30cc..d91637c6cc975 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRuntimeConfigPersistenceUnit.java @@ -34,7 +34,7 @@ public class HibernateSearchElasticsearchRuntimeConfigPersistenceUnit { * * @asciidoclet */ - @ConfigItem(defaultValueDocumentation = "`true` if Hibernate Search is enabled; `false` otherwise") + @ConfigItem(defaultValueDocumentation = "'true' if Hibernate Search is enabled; 'false' otherwise") public Optional active; /** From e8d788e78554e2ce8b45631522477dd364af8dc4 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 26 Sep 2022 13:36:46 +0200 Subject: [PATCH 31/81] Dev Services - Mark configured images as compatible with default ones Fixes #27862 (cherry picked from commit cf81292249ab52f3cd36a81fb0789135c1d81949) --- .../common/deployment/DevServicesElasticsearchProcessor.java | 4 ++-- .../kafka/client/deployment/DevServicesKafkaProcessor.java | 2 +- .../devservice/DevServicesApicurioRegistryProcessor.java | 3 ++- .../amqp/deployment/AmqpDevServicesProcessor.java | 2 +- .../rabbitmq/deployment/RabbitMQDevServicesProcessor.java | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java index 44803e7d84ca2..fae32662211ac 100644 --- a/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java +++ b/extensions/elasticsearch-rest-client-common/deployment/src/main/java/io/quarkus/elasticsearch/restclient/common/deployment/DevServicesElasticsearchProcessor.java @@ -176,7 +176,7 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch( throw new BuildException("Dev services for Elasticsearch didn't support Opensearch", Collections.emptyList()); } - // Hibernate search Elasticsearch have a version configuration property, we need to check that it is coherent + // Hibernate Search Elasticsearch have a version configuration property, we need to check that it is coherent // with the image we are about to launch if (buildItemConfig.version != null) { String containerTag = config.imageName.substring(config.imageName.indexOf(':') + 1); @@ -197,7 +197,7 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch( // Starting the server final Supplier defaultElasticsearchSupplier = () -> { ElasticsearchContainer container = new ElasticsearchContainer( - DockerImageName.parse(config.imageName)); + DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("elasticsearch/elasticsearch")); ConfigureUtil.configureSharedNetwork(container, "elasticsearch"); if (config.serviceName != null) { container.withLabel(DEV_SERVICE_LABEL, config.serviceName); diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java index 319ff7d521d45..0bbe4d8112266 100644 --- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java +++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java @@ -246,7 +246,7 @@ private RunningDevService startKafka(DockerStatusBuildItem dockerStatusBuildItem KAFKA_BOOTSTRAP_SERVERS, container.getBootstrapServers()); } else { RedPandaKafkaContainer container = new RedPandaKafkaContainer( - DockerImageName.parse(config.imageName), + DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("vectorized/redpanda"), config.fixedExposedPort, launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT ? config.serviceName : null, useSharedNetwork, config.redpanda); diff --git a/extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java b/extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java index 79e659a5562e5..7698a26f35537 100644 --- a/extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java +++ b/extensions/schema-registry/devservice/deployment/src/main/java/io/quarkus/apicurio/registry/devservice/DevServicesApicurioRegistryProcessor.java @@ -173,7 +173,8 @@ private RunningDevService startApicurioRegistry(DockerStatusBuildItem dockerStat getRegistryUrlConfigs("http://" + address.getUrl()))) .orElseGet(() -> { ApicurioRegistryContainer container = new ApicurioRegistryContainer( - DockerImageName.parse(config.imageName), config.fixedExposedPort, + DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("apicurio/apicurio-registry-mem"), + config.fixedExposedPort, launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT ? config.serviceName : null, useSharedNetwork); timeout.ifPresent(container::withStartupTimeout); diff --git a/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java b/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java index d6c6fa0146adb..99fdf9a906bf9 100644 --- a/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java +++ b/extensions/smallrye-reactive-messaging-amqp/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/amqp/deployment/AmqpDevServicesProcessor.java @@ -176,7 +176,7 @@ private RunningDevService startAmqpBroker(DockerStatusBuildItem dockerStatusBuil final Supplier defaultAmqpBrokerSupplier = () -> { // Starting the broker ArtemisContainer container = new ArtemisContainer( - DockerImageName.parse(config.imageName), + DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("artemiscloud/activemq-artemis-broker"), config.extra, config.fixedExposedPort, launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT ? config.serviceName : null); diff --git a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java index 85763f737ae33..39d102ff57582 100644 --- a/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java +++ b/extensions/smallrye-reactive-messaging-rabbitmq/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/rabbitmq/deployment/RabbitMQDevServicesProcessor.java @@ -176,7 +176,7 @@ private RunningDevService startRabbitMQBroker(DockerStatusBuildItem dockerStatus } ConfiguredRabbitMQContainer container = new ConfiguredRabbitMQContainer( - DockerImageName.parse(config.imageName), + DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("rabbitmq"), config.fixedExposedPort, config.fixedExposedHttpPort, launchMode.getLaunchMode() == LaunchMode.DEVELOPMENT ? config.serviceName : null); From 24e570221b0f3ed5116689b8d2c2c9a07a39e40c Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Tue, 27 Sep 2022 09:16:17 +1000 Subject: [PATCH 32/81] Use notifyAll instead of notify HTTP/2 could have multiple threads waiting on the connection, so we need to wake up all of them. In future we could move to a park/unpark based approach but it would need some performance testing. Fixes #28116 (cherry picked from commit eb5083b8745a850b6eb73be8a05480e10c70e1e1) --- .../java/io/quarkus/vertx/http/runtime/VertxInputStream.java | 4 ++-- .../resteasy/reactive/server/vertx/VertxInputStream.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java index 9e56af5b332b7..10717a6f0c869 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java @@ -190,7 +190,7 @@ public void handle(Void event) { synchronized (connection) { eof = true; if (waiting) { - connection.notify(); + connection.notifyAll(); } } } @@ -212,7 +212,7 @@ public void handle(Throwable event) { } } if (waiting) { - connection.notify(); + connection.notifyAll(); } } } diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxInputStream.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxInputStream.java index c30afb92de7ba..9c56278499d75 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxInputStream.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxInputStream.java @@ -190,7 +190,7 @@ public void handle(Void event) { synchronized (connection) { eof = true; if (waiting) { - connection.notify(); + connection.notifyAll(); } } } @@ -212,7 +212,7 @@ public void handle(Throwable event) { } } if (waiting) { - connection.notify(); + connection.notifyAll(); } } } From 6fc03b0190cc6cedc2463935bf3f07b43044529f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Tue, 27 Sep 2022 10:26:36 +0200 Subject: [PATCH 33/81] Do not index resources removed by extensions Excluded resources removed by extension from indexing as according to io.quarkus.maven.ExtensionDescriptorMojo#removedResources should be equivalent of quarkus.class-loading.removed-resources, however later classes are excluded from indexing while former are not. (cherry picked from commit b1779038387f0a822c215dd9909ad3e571346526) --- .../quarkus/deployment/index/ApplicationArchiveBuildStep.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java index 4dcaca5be140b..55ac76fb2f44e 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java @@ -102,6 +102,9 @@ ApplicationArchivesBuildItem build( removedResources.put(new GACT(entry.getKey().split(":")), entry.getValue()); } + // Add resources removed from the classpath by extensions + removedResources.putAll(curateOutcomeBuildItem.getApplicationModel().getRemovedResources()); + List applicationArchives = scanForOtherIndexes(buildCloseables, appMarkers, root, additionalApplicationArchiveBuildItem, indexDependencyBuildItems, indexCache, curateOutcomeBuildItem, removedResources); From 193d40d396c1e3a5294e6e7faea7a6320e2f385f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Tue, 27 Sep 2022 10:32:27 +0200 Subject: [PATCH 34/81] Do not index excluded `ResteasyJackson2Provider` to prevent warnings fixes: #27907 (cherry picked from commit 34b21a7b56c6c849569f0e0eb759bb5b80b8bd65) --- extensions/keycloak-admin-client-reactive/runtime/pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/keycloak-admin-client-reactive/runtime/pom.xml b/extensions/keycloak-admin-client-reactive/runtime/pom.xml index 81a08005fa838..dc194217932f8 100644 --- a/extensions/keycloak-admin-client-reactive/runtime/pom.xml +++ b/extensions/keycloak-admin-client-reactive/runtime/pom.xml @@ -73,6 +73,14 @@ io.quarkus quarkus-extension-maven-plugin + + + + org.keycloak:keycloak-admin-client + org/keycloak/admin/client/JacksonProvider.class + + + maven-compiler-plugin From 0eb921f2c116b88de60bb1c4124372fe63ad2ce3 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 27 Sep 2022 15:56:02 +0300 Subject: [PATCH 35/81] Take application provided JSON providers when warning about missing JSON feature Fixes: #28195 (cherry picked from commit 2e21ad05d8fc7803e2f6841ed6834fded3c89a5e) --- .../QuarkusServerEndpointIndexer.java | 33 +++++++++++++++++-- .../common/processor/EndpointIndexer.java | 2 +- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java index 16b6355c47190..76fdcbbb826d4 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java @@ -15,6 +15,9 @@ import org.jboss.logging.Logger; import org.jboss.resteasy.reactive.common.ResteasyReactiveConfig; import org.jboss.resteasy.reactive.common.processor.DefaultProducesHandler; +import org.jboss.resteasy.reactive.common.processor.scanning.ResteasyReactiveScanner; +import org.jboss.resteasy.reactive.common.processor.scanning.ScannedSerializer; +import org.jboss.resteasy.reactive.common.processor.scanning.SerializerScanningResult; import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationStore; import org.jboss.resteasy.reactive.server.model.ServerResourceMethod; import org.jboss.resteasy.reactive.server.processor.ServerEndpointIndexer; @@ -43,6 +46,7 @@ public class QuarkusServerEndpointIndexer private final ResteasyReactiveRecorder resteasyReactiveRecorder; private final Predicate applicationClassPredicate; + private SerializerScanningResult serializerScanningResult; QuarkusServerEndpointIndexer(Builder builder) { super(builder); @@ -170,10 +174,33 @@ private void warnAboutMissingJsonProviderIfNeeded(ServerResourceMethod method, M return; } if (hasJson(method) || (hasNoTypesDefined(method) && isDefaultJson())) { - LOGGER.warnf("Quarkus detected the use of JSON in JAX-RS method '" + info.declaringClass().name() + "#" - + info.name() - + "' but no JSON extension has been added. Consider adding 'quarkus-resteasy-reactive-jackson' or 'quarkus-resteasy-reactive-jsonb'."); + if (serializerScanningResult == null) { + serializerScanningResult = ResteasyReactiveScanner.scanForSerializers(index, applicationScanningResult); + } + boolean appProvidedJsonReaderExists = appProvidedJsonProviderExists(serializerScanningResult.getReaders()); + boolean appProvidedJsonWriterExists = appProvidedJsonProviderExists(serializerScanningResult.getWriters()); + if (!appProvidedJsonReaderExists || !appProvidedJsonWriterExists) { + LOGGER.warnf("Quarkus detected the use of JSON in JAX-RS method '" + info.declaringClass().name() + "#" + + info.name() + + "' but no JSON extension has been added. Consider adding 'quarkus-resteasy-reactive-jackson' or 'quarkus-resteasy-reactive-jsonb'."); + } + } + } + + private boolean appProvidedJsonProviderExists(List providers) { + boolean appProvidedJsonReaderExists = false; + for (ScannedSerializer provider : providers) { + for (String mt : provider.getMediaTypeStrings()) { + if (isJson(mt)) { + appProvidedJsonReaderExists = true; + break; + } + } + if (appProvidedJsonReaderExists) { + break; + } } + return appProvidedJsonReaderExists; } private boolean isDefaultJson() { diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java index 072551ecfebc7..4ee14ffe8f5b1 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java @@ -226,7 +226,7 @@ public abstract class EndpointIndexer> factoryCreator; private final Consumer resourceMethodCallback; private final AnnotationStore annotationStore; - private final ApplicationScanningResult applicationScanningResult; + protected final ApplicationScanningResult applicationScanningResult; private final Set contextTypes; private final MultipartReturnTypeIndexerExtension multipartReturnTypeIndexerExtension; private final MultipartParameterIndexerExtension multipartParameterIndexerExtension; From e1199cd0c71a97521ceb30c73eded1a2e01fa1ca Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 27 Sep 2022 22:44:09 +0300 Subject: [PATCH 36/81] Fix support for rootless docker context With the `'` it returns `'''unix:///run/user/1000/docker.sock'` which fails the startsWith("unix://") check. Fix up #26892 (cherry picked from commit 71a050140ef151dd05561784254ed164db237a0b) --- .../pkg/steps/NativeImageBuildLocalContainerRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java index 1be0521d44027..58c97c1eb237c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java @@ -87,7 +87,7 @@ private static String fetchDockerEndpoint() { OutputFilter outputFilter = new OutputFilter(); if (!ExecUtil.execWithTimeout(new File("."), outputFilter, Duration.ofMillis(3000), "docker", "context", "ls", "--format", - "'{{- if .Current -}} {{- .DockerEndpoint -}} {{- end -}}'")) { + "{{- if .Current -}} {{- .DockerEndpoint -}} {{- end -}}")) { LOGGER.debug("Docker context lookup didn't succeed in time"); return null; } From 255053900ccd025c92e5bd749c0ba81f41e537e6 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Mon, 26 Sep 2022 22:03:13 +0100 Subject: [PATCH 37/81] Preparing Hibernate ORM extension for lack of automatic features (cherry picked from commit eae2c5460a2fcbc42534ff6d9cfddc4d7cc98e07) --- .../orm/deployment/GraalVMFeatures.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java new file mode 100644 index 0000000000000..faee479dab034 --- /dev/null +++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/GraalVMFeatures.java @@ -0,0 +1,24 @@ +package io.quarkus.hibernate.orm.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; +import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem; + +/** + * Activates the native-image features included in the module + * org.hibernate:hibernate-graalvm. + */ +@BuildSteps +public class GraalVMFeatures { + + @BuildStep + NativeImageFeatureBuildItem staticNativeImageFeature() { + return new NativeImageFeatureBuildItem("org.hibernate.graalvm.internal.GraalVMStaticFeature"); + } + + @BuildStep + NativeImageFeatureBuildItem queryParsingSupportFeature() { + return new NativeImageFeatureBuildItem("org.hibernate.graalvm.internal.QueryParsingSupport"); + } + +} From 3c7d75eb8ce9fbb62edfb39e5964875e01612f58 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 27 Sep 2022 13:44:19 +0100 Subject: [PATCH 38/81] Upgrade to Hibernate ORM 5.6.12.Final (cherry picked from commit 3b8da68e0ee2d48d57a67e519832cb88a7520407) --- 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 91efb672bf0e4..a9f070520e9a4 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -88,7 +88,7 @@ 3.12.0 1.15 1.5.1 - 5.6.11.Final + 5.6.12.Final 1.12.9 1.1.7.Final 6.2.5.Final From 7dc55bed959f937e94efedf2f1befe3284c502ba Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Fri, 23 Sep 2022 10:45:16 +0200 Subject: [PATCH 39/81] In remote dev delete files after the app has been closed and before it is restarted (cherry picked from commit 93f5b5b1f4a7ad4085d54069eab8efbba025cecc) --- .../deployment/dev/RuntimeUpdatesProcessor.java | 12 ++++++++++-- .../io/quarkus/bootstrap/runner/DevModeMediator.java | 11 +++++++++++ 2 files changed, 21 insertions(+), 2 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 e1c0873b86691..d9326e67bb9b9 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 @@ -53,6 +53,7 @@ import org.jboss.jandex.Indexer; import org.jboss.logging.Logger; +import io.quarkus.bootstrap.runner.DevModeMediator; import io.quarkus.bootstrap.runner.Timing; import io.quarkus.changeagent.ClassChangeAgent; import io.quarkus.deployment.dev.DevModeContext.ModuleInfo; @@ -602,6 +603,7 @@ public Set syncState(Map fileHashes) { ret.add(i.getKey()); } } + List removedFiles = List.of(); for (Map.Entry remaining : ourHashes.entrySet()) { String file = remaining.getKey(); if (file.endsWith("META-INF/MANIFEST.MF") || file.contains("META-INF/maven") @@ -609,8 +611,14 @@ public Set syncState(Map fileHashes) { //we have some filters, for files that we don't want to delete continue; } - log.info("Deleting removed file " + file); - Files.deleteIfExists(applicationRoot.resolve(file)); + log.info("Scheduled for removal " + file); + if (removedFiles.isEmpty()) { + removedFiles = new ArrayList<>(); + } + removedFiles.add(applicationRoot.resolve(file)); + } + if (!removedFiles.isEmpty()) { + DevModeMediator.removedFiles.addLast(removedFiles); } return ret; } catch (IOException e) { diff --git a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java index 0cc428bf191f1..87d382db3c60a 100644 --- a/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java +++ b/independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/DevModeMediator.java @@ -9,9 +9,11 @@ import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Deque; import java.util.List; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.LinkedBlockingDeque; import org.jboss.logging.Logger; @@ -24,6 +26,8 @@ public class DevModeMediator { protected static final Logger LOGGER = Logger.getLogger(DevModeMediator.class); + public static final Deque> removedFiles = new LinkedBlockingDeque<>(); + static void doDevMode(Path appRoot) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Path deploymentClassPath = appRoot.resolve(QuarkusEntryPoint.LIB_DEPLOYMENT_DEPLOYMENT_CLASS_PATH_DAT); @@ -88,6 +92,13 @@ public void run() { closeable.close(); } closeable = null; + final List pathsToDelete = removedFiles.pollFirst(); + if (pathsToDelete != null) { + for (Path p : pathsToDelete) { + LOGGER.info("Deleting " + p); + Files.deleteIfExists(p); + } + } try { closeable = doStart(appRoot, deploymentClassPath); } catch (Exception e) { From 310ae348bfef5e4c81efca32d811f575df832805 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 21:11:55 +0000 Subject: [PATCH 40/81] Bump junit-jupiter from 5.9.0 to 5.9.1 Bumps [junit-jupiter](https://github.com/junit-team/junit5) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/junit-team/junit5/releases) - [Commits](https://github.com/junit-team/junit5/compare/r5.9.0...r5.9.1) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 107f501e607a7cc7afa91b427df726e70074b1f7) --- independent-projects/bootstrap/pom.xml | 2 +- independent-projects/extension-maven-plugin/pom.xml | 2 +- independent-projects/tools/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index 6b4ccaec51479..e34ae94641a12 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -42,7 +42,7 @@ 3.23.1 0.9.5 3.5.0.Final - 5.9.0 + 5.9.1 3.8.6 0.3.5 3.6.0 diff --git a/independent-projects/extension-maven-plugin/pom.xml b/independent-projects/extension-maven-plugin/pom.xml index 850223560ca13..7ac9c49a56f05 100644 --- a/independent-projects/extension-maven-plugin/pom.xml +++ b/independent-projects/extension-maven-plugin/pom.xml @@ -36,7 +36,7 @@ 3.0.0-M7 1.6.8 2.13.4 - 5.9.0 + 5.9.1 diff --git a/independent-projects/tools/pom.xml b/independent-projects/tools/pom.xml index 239581cf0360a..ec81155d7ba80 100644 --- a/independent-projects/tools/pom.xml +++ b/independent-projects/tools/pom.xml @@ -51,7 +51,7 @@ 3.23.1 2.13.4 2.0.2 - 5.9.0 + 5.9.1 1.21 3.5.0.Final 3.8.6 From 59bb0d8f4fd05a9ffd4a7690787ae3707790d6bd Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Fri, 23 Sep 2022 18:09:07 +0100 Subject: [PATCH 41/81] Support doc annotations in @ConfigMapping (cherry picked from commit 0495d0b51a384076d27e292deadb7fb3973e366f) --- .../java/io/quarkus/runtime/annotations/ConfigDocMapKey.java | 3 ++- .../java/io/quarkus/runtime/annotations/ConfigDocSection.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocMapKey.java b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocMapKey.java index a6d1096c26c85..74e6b54fd62a6 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocMapKey.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocMapKey.java @@ -1,6 +1,7 @@ package io.quarkus.runtime.annotations; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -13,7 +14,7 @@ */ @Documented @Retention(SOURCE) -@Target({ FIELD, PARAMETER }) +@Target({ FIELD, PARAMETER, METHOD }) public @interface ConfigDocMapKey { String value(); } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocSection.java b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocSection.java index 86915c752b300..372cc7782d621 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocSection.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocSection.java @@ -1,6 +1,7 @@ package io.quarkus.runtime.annotations; import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -14,6 +15,6 @@ */ @Documented @Retention(SOURCE) -@Target({ FIELD, PARAMETER }) +@Target({ FIELD, PARAMETER, METHOD }) public @interface ConfigDocSection { } From d566b23c81c938e38b7d4f2fc6d9bab91e79e3fb Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Mon, 26 Sep 2022 13:43:08 +0200 Subject: [PATCH 42/81] Fix https://github.com/quarkusio/quarkus/issues/26830 The problem comes from the default gRPC context storage using a thread-local. This commit overrides the storage implementation (using the recommended method) to use the duplicated context and fallback to a thread-local. (cherry picked from commit b82b23553a319e5d37d299242418842a4fac05f4) --- .../io/quarkus/grpc/auth/GrpcAuthTest.java | 8 +-- .../GrpcContextPropagationTest.java | 39 +++++++++++ .../interceptors/MyFirstInterceptor.java | 27 +++++--- .../interceptors/MyInterceptedGreeting.java | 18 +++++ .../grpc/override/ContextStorageOverride.java | 67 +++++++++++++++++++ .../GrpcDuplicatedContextGrpcInterceptor.java | 2 +- 6 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/GrpcContextPropagationTest.java create mode 100644 extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/MyInterceptedGreeting.java create mode 100644 extensions/grpc/runtime/src/main/java/io/grpc/override/ContextStorageOverride.java diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/auth/GrpcAuthTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/auth/GrpcAuthTest.java index c995eb501b7c0..b73d71e2c7604 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/auth/GrpcAuthTest.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/auth/GrpcAuthTest.java @@ -68,7 +68,7 @@ void shouldSecureUniEndpoint() { client.unaryCall(Security.Container.newBuilder().setText("woo-hoo").build()) .subscribe().with(e -> resultCount.incrementAndGet()); - await().atMost(5, TimeUnit.SECONDS) + await().atMost(10, TimeUnit.SECONDS) .until(() -> resultCount.get() == 1); } @@ -82,7 +82,7 @@ void shouldSecureMultiEndpoint() { .supplier(() -> (Security.Container.newBuilder().setText("woo-hoo").build())).atMost(4)) .subscribe().with(e -> results.add(e.getIsOnEventLoop())); - await().atMost(5, TimeUnit.SECONDS) + await().atMost(10, TimeUnit.SECONDS) .until(() -> results.size() == 5); assertThat(results.stream().filter(e -> !e)).isEmpty(); @@ -101,7 +101,7 @@ void shouldFailWithInvalidCredentials() { .onFailure().invoke(error::set) .subscribe().with(e -> resultCount.incrementAndGet()); - await().atMost(5, TimeUnit.SECONDS) + await().atMost(10, TimeUnit.SECONDS) .until(() -> error.get() != null); } @@ -118,7 +118,7 @@ void shouldFailWithInvalidInsufficientRole() { .onFailure().invoke(error::set) .subscribe().with(e -> resultCount.incrementAndGet()); - await().atMost(5, TimeUnit.SECONDS) + await().atMost(10, TimeUnit.SECONDS) .until(() -> error.get() != null); } diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/GrpcContextPropagationTest.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/GrpcContextPropagationTest.java new file mode 100644 index 0000000000000..83278b2073d38 --- /dev/null +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/GrpcContextPropagationTest.java @@ -0,0 +1,39 @@ +package io.quarkus.grpc.server.interceptors; + +import static org.assertj.core.api.Assertions.assertThat; + +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.grpc.examples.helloworld.Greeter; +import io.grpc.examples.helloworld.GreeterGrpc; +import io.grpc.examples.helloworld.HelloReply; +import io.grpc.examples.helloworld.HelloRequest; +import io.quarkus.grpc.GrpcClient; +import io.quarkus.test.QuarkusUnitTest; + +/** + * Test reproducing #26830. + */ +public class GrpcContextPropagationTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest().setArchiveProducer( + () -> ShrinkWrap.create(JavaArchive.class) + .addPackage(GreeterGrpc.class.getPackage()) + .addClasses(MyFirstInterceptor.class, MyInterceptedGreeting.class)); + + @GrpcClient + Greeter greeter; + + @Test + void test() { + HelloReply foo = greeter.sayHello(HelloRequest.newBuilder().setName("foo").build()).await().indefinitely(); + assertThat(foo.getMessage()).isEqualTo("hello k1 - 1"); + foo = greeter.sayHello(HelloRequest.newBuilder().setName("foo").build()).await().indefinitely(); + assertThat(foo.getMessage()).isEqualTo("hello k1 - 2"); + } + +} diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/MyFirstInterceptor.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/MyFirstInterceptor.java index ea796231910ca..bee9dba9d37bc 100644 --- a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/MyFirstInterceptor.java +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/MyFirstInterceptor.java @@ -1,8 +1,12 @@ package io.quarkus.grpc.server.interceptors; +import java.util.concurrent.atomic.AtomicInteger; + import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.spi.Prioritized; +import io.grpc.Context; +import io.grpc.Contexts; import io.grpc.ForwardingServerCall; import io.grpc.Metadata; import io.grpc.ServerCall; @@ -15,19 +19,26 @@ @GlobalInterceptor public class MyFirstInterceptor implements ServerInterceptor, Prioritized { + public static Context.Key KEY_1 = Context.key("X-TEST_1"); + public static Context.Key KEY_2 = Context.keyWithDefault("X-TEST_2", -1); private volatile long callTime; + private AtomicInteger counter = new AtomicInteger(); + @Override public ServerCall.Listener interceptCall(ServerCall serverCall, Metadata metadata, ServerCallHandler serverCallHandler) { - return serverCallHandler - .startCall(new ForwardingServerCall.SimpleForwardingServerCall(serverCall) { - @Override - public void close(Status status, Metadata trailers) { - callTime = System.nanoTime(); - super.close(status, trailers); - } - }, metadata); + + Context ctx = Context.current().withValue(KEY_1, "k1").withValue(KEY_2, counter.incrementAndGet()); + return Contexts.interceptCall(ctx, new ForwardingServerCall.SimpleForwardingServerCall<>(serverCall) { + + @Override + public void close(Status status, Metadata trailers) { + callTime = System.nanoTime(); + super.close(status, trailers); + } + }, metadata, serverCallHandler); + } public long getLastCall() { diff --git a/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/MyInterceptedGreeting.java b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/MyInterceptedGreeting.java new file mode 100644 index 0000000000000..1929da8abd640 --- /dev/null +++ b/extensions/grpc/deployment/src/test/java/io/quarkus/grpc/server/interceptors/MyInterceptedGreeting.java @@ -0,0 +1,18 @@ +package io.quarkus.grpc.server.interceptors; + +import io.grpc.examples.helloworld.Greeter; +import io.grpc.examples.helloworld.HelloReply; +import io.grpc.examples.helloworld.HelloRequest; +import io.quarkus.grpc.GrpcService; +import io.smallrye.common.annotation.Blocking; +import io.smallrye.mutiny.Uni; + +@GrpcService +public class MyInterceptedGreeting implements Greeter { + @Override + @Blocking + public Uni sayHello(HelloRequest request) { + return Uni.createFrom().item(() -> HelloReply.newBuilder() + .setMessage("hello " + MyFirstInterceptor.KEY_1.get() + " - " + MyFirstInterceptor.KEY_2.get()).build()); + } +} diff --git a/extensions/grpc/runtime/src/main/java/io/grpc/override/ContextStorageOverride.java b/extensions/grpc/runtime/src/main/java/io/grpc/override/ContextStorageOverride.java new file mode 100644 index 0000000000000..84de72b532fdd --- /dev/null +++ b/extensions/grpc/runtime/src/main/java/io/grpc/override/ContextStorageOverride.java @@ -0,0 +1,67 @@ +package io.grpc.override; + +import io.grpc.Context; +import io.smallrye.common.vertx.VertxContext; +import io.vertx.core.Vertx; + +/** + * Override gRPC context storage to rely on duplicated context when available. + */ +public class ContextStorageOverride extends Context.Storage { + + private static final ThreadLocal fallback = new ThreadLocal<>(); + + private static final String GRPC_CONTEXT = "GRPC_CONTEXT"; + + @Override + public Context doAttach(Context toAttach) { + Context current = current(); + io.vertx.core.Context dc = Vertx.currentContext(); + if (dc != null && VertxContext.isDuplicatedContext(dc)) { + dc.putLocal(GRPC_CONTEXT, toAttach); + } else { + fallback.set(toAttach); + } + return current; + } + + @Override + public void detach(Context context, Context toRestore) { + io.vertx.core.Context dc = Vertx.currentContext(); + if (toRestore != Context.ROOT) { + if (dc != null && VertxContext.isDuplicatedContext(dc)) { + dc.putLocal(GRPC_CONTEXT, toRestore); + } else { + fallback.set(toRestore); + } + } else { + if (dc != null && VertxContext.isDuplicatedContext(dc)) { + // Do nothing duplicated context are not shared. + } else { + fallback.set(null); + } + } + } + + @Override + public Context current() { + if (VertxContext.isOnDuplicatedContext()) { + Context current = Vertx.currentContext().getLocal(GRPC_CONTEXT); + if (current == null) { + return Context.ROOT; + } + return current; + } else { + Context current = fallback.get(); + if (current == null) { + return Context.ROOT; + } + return current; + } + } + + @Override + public void attach(Context toAttach) { + // do nothing, should not be called. + } +} diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/context/GrpcDuplicatedContextGrpcInterceptor.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/context/GrpcDuplicatedContextGrpcInterceptor.java index d4ca59da2e55c..b876ab00e3001 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/context/GrpcDuplicatedContextGrpcInterceptor.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/supports/context/GrpcDuplicatedContextGrpcInterceptor.java @@ -82,7 +82,7 @@ private synchronized ServerCall.Listener getDelegate() { delegate = supplier.get(); } catch (Throwable t) { // If the interceptor supplier throws an exception, catch it, and close the call. - log.warnf("Unable to retrieve gRPC Server call listener", t); + log.warn("Unable to retrieve gRPC Server call listener", t); close(t); return null; } From 273b04ad593159b9f1b793d30b87c6c673b846f9 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Wed, 28 Sep 2022 13:13:23 +0200 Subject: [PATCH 43/81] Filter out a messaging on Mac indicating that Netty falls back to the system DNS resolver (cherry picked from commit 59a2869dfd715b12752e908dadae6ac383b2ef45) --- .../quarkus/netty/deployment/NettyProcessor.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java b/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java index 608572fc702ee..5d5f7626e2b36 100644 --- a/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java +++ b/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java @@ -13,6 +13,7 @@ import org.jboss.logmanager.Level; import io.netty.channel.EventLoopGroup; +import io.netty.resolver.dns.DnsServerAddressStreamProviders; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.logging.InternalLoggerFactory; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; @@ -253,11 +254,23 @@ RuntimeInitializedClassBuildItem runtimeInitBcryptUtil() { //if debug logging is enabled netty logs lots of exceptions //see https://github.com/quarkusio/quarkus/issues/5213 @BuildStep - LogCleanupFilterBuildItem cleanup() { + LogCleanupFilterBuildItem cleanupUnsafeLog() { return new LogCleanupFilterBuildItem(PlatformDependent.class.getName() + "0", Level.TRACE, "direct buffer constructor", "jdk.internal.misc.Unsafe", "sun.misc.Unsafe"); } + /** + * On mac, if you do not have the `MacOSDnsServerAddressStreamProvider` class, Netty prints a warning saying it + * falls back to the default system DNS provider. This is not a problem and generates tons of questions. + * + * @return the log cleanup item removing the message + */ + @BuildStep + LogCleanupFilterBuildItem cleanupMacDNSInLog() { + return new LogCleanupFilterBuildItem(DnsServerAddressStreamProviders.class.getName(), Level.WARN, + "Can not find io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider in the classpath"); + } + private String calculateMaxOrder(OptionalInt userConfig, List minMaxOrderBuildItems, boolean shouldWarn) { int result = DEFAULT_NETTY_ALLOCATOR_MAX_ORDER; From d5b0f1a90fe952d9859c4d3e7f22894a66060404 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 28 Sep 2022 17:43:44 +0200 Subject: [PATCH 44/81] Proofreading for security-csrf-prevention.adoc (cherry picked from commit 2a23578ab7f8b03313c06042ad6a7fd834559923) --- .../asciidoc/security-csrf-prevention.adoc | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/docs/src/main/asciidoc/security-csrf-prevention.adoc b/docs/src/main/asciidoc/security-csrf-prevention.adoc index 376c5934a5469..51cf8106dea3a 100644 --- a/docs/src/main/asciidoc/security-csrf-prevention.adoc +++ b/docs/src/main/asciidoc/security-csrf-prevention.adoc @@ -7,9 +7,9 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc include::./attributes.adoc[] -https://owasp.org/www-community/attacks/csrf[Cross-Site Request Forgery(CSRF)] is an attack that forces an end user to execute unwanted actions on a web application in which they are currently authenticated. +https://owasp.org/www-community/attacks/csrf[Cross-Site Request Forgery (CSRF)] is an attack that forces an end user to execute unwanted actions on a web application in which they are currently authenticated. -Quarkus Security provides a CSRF prevention feature which consists of a xref:resteasy-reactive.adoc[Resteasy Reactive] server filter which creates and verifies CSRF tokens and an HTML form parameter provider which supports the xref:qute-reference.adoc#injecting-beans-directly-in-templates[injection of CSRF tokens in Qute templates]. +Quarkus Security provides a CSRF prevention feature which consists of a xref:resteasy-reactive.adoc[RESTEasy Reactive] server filter which creates and verifies CSRF tokens and an HTML form parameter provider which supports the xref:qute-reference.adoc#injecting-beans-directly-in-templates[injection of CSRF tokens in Qute templates]. == Creating the Project @@ -45,7 +45,7 @@ This will add the following to your build file: implementation("io.quarkus:quarkus-csrf-reactive") ---- -Next lets add a Qute template producing an HTML form: +Next, let's add a `csrfToken.html` Qute template producing an HTML form in the `src/main/resources/templates` folder: [source,html] ---- @@ -67,11 +67,8 @@ Next lets add a Qute template producing an HTML form: ---- - <1> This expression is used to inject a CSRF token into a hidden form field. This token will be verified by the CSRF filter against a CSRF cookie. -You can name the file containing this template as `csrfToken.html` and put it in a `src/main/resources/templates` folder. - Now let's create a resource class which returns an HTML form and handles form POST requests: [source,java] @@ -112,14 +109,13 @@ public class UserNameResource { } } ---- - <1> Inject the `csrfToken.html` as a `Template`. -<2> Return HTML form with a hidden form field containing a CSRF token created by the CSRF filter. -<3> Handle the form POST request, this method can only be invoked only if the CSRF filter has successfully verified the token. +<2> Return the HTML form with a hidden form field containing a CSRF token created by the CSRF filter. +<3> Handle the form POST request, this method can only be invoked if the CSRF filter has successfully verified the token. The form POST request will fail with HTTP status `400` if the filter finds the hidden CSRF form field is missing, the CSRF cookie is missing, or if the CSRF form field and CSRF cookie values do not match. -At this stage no additional configuration is needed - by default the CSRF form field and cookie name will be set to `csrf_token`, and the filter will verify the token. But lets change these names: +At this stage no additional configuration is needed - by default the CSRF form field and cookie name will be set to `csrf_token`, and the filter will verify the token. But let's change these names: [source,properties] ---- @@ -127,7 +123,7 @@ quarkus.csrf-reactive.form-field-name=csrftoken quarkus.csrf-reactive.cookie-name=csrftoken ---- -Note that the CSRF filter has to read the input stream in order to verify the token and then re-create the stream for the application code to read it as well. The filter performs this work on an event loop thread so for small form payloads such as the one shown in the example above it will have negligible peformance side-effects. However if you deal with large form payloads then it is recommended to compare the CSRF form field and cookie values in the application code: +Note that the CSRF filter has to read the input stream in order to verify the token and then re-create the stream for the application code to read it as well. The filter performs this work on an event loop thread so for small form payloads, such as the one shown in the example above, it will have negligible peformance side-effects. However if you deal with large form payloads then it is recommended to compare the CSRF form field and cookie values in the application code: [source,java] ---- @@ -172,7 +168,6 @@ public class UserNameResource { } } ---- - <1> Compare the CSRF form field and cookie values and fail with HTTP status `400` if they don't match. Also disable the token verification in the filter: @@ -182,7 +177,6 @@ Also disable the token verification in the filter: quarkus.csrf-reactive.verify-token=false ---- - [[csrf-reactive-configuration-reference]] == Configuration Reference From 8a6d36c060f46aea91847039f3baf03dca26566e Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 29 Sep 2022 09:42:34 +0200 Subject: [PATCH 45/81] Qute getting started guide - remove outdated section about async APIs - resolves #28230 (cherry picked from commit 6c612ac8c54cc85fd428dfafb35a6b69b121d882) --- docs/src/main/asciidoc/qute.adoc | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/docs/src/main/asciidoc/qute.adoc b/docs/src/main/asciidoc/qute.adoc index 0fcfce0d6d71e..b3d32086fbf5b 100644 --- a/docs/src/main/asciidoc/qute.adoc +++ b/docs/src/main/asciidoc/qute.adoc @@ -498,29 +498,6 @@ public class ReportGenerator { <2> Use the `@Scheduled` annotation to instruct Quarkus to execute this method on the half hour. For more information see the xref:scheduler.adoc[Scheduler] guide. <3> The `TemplateInstance.render()` method triggers rendering. Note that this method blocks the current thread. -== Reactive and Asynchronous APIs - -Templates can be rendered as a `CompletionStage` (completed with the rendered output asynchronously) or as `Publisher` containing the rendered chunks: - -[source, java] ----- -CompletionStage async = template.data("name", "neo").renderAsync(); -Publisher publisher = template.data("name", "neo").publisher(); ----- - -In the case of a `Publisher`, the template is rendered chunk by chunk following the requests from the subscriber. -The rendering is not started until a subscriber requests it. -The returned `Publisher` is an instance of `io.smallrye.mutiny.Multi`. - -It is possible to create an instance of `io.smallrye.mutiny.Uni` as follows: - -[source, java] ----- -Uni uni = Uni.createFrom().completionStage(() -> template.data("name", "neo").renderAsync()); ----- - -In this case, the rendering only starts once the subscriber requests it. - == Qute Reference Guide To learn more about Qute, please refer to the xref:qute-reference.adoc[Qute reference guide]. From 4d5ec2390de282a52c44ab38cec094a6a08ae07f Mon Sep 17 00:00:00 2001 From: Vincent van Dam Date: Wed, 28 Sep 2022 18:59:24 +0200 Subject: [PATCH 46/81] Fix unmodifiableList handling of parameterized tests (cherry picked from commit d890527e856bb29884fddd8b829ff60236e88670) --- .../java/io/quarkus/it/extension/ParamsTest.java | 15 +++++++++++++++ .../test/junit/internal/CustomListConverter.java | 1 + 2 files changed, 16 insertions(+) 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 42c1949b690c2..11da8f2bbc9ea 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 @@ -120,6 +120,15 @@ static List testDataListArguments() { return Arrays.asList(Arguments.of(new TestData(), "foo"), Arguments.of(new TestData(), "foo")); } + @ParameterizedTest + @MethodSource("testDataUnmodifiableListArguments") + public void methodUnmodifiableListArguments(Object list) { + } + + static Stream testDataUnmodifiableListArguments() { + return Stream.of(Arguments.of(Collections.unmodifiableList(List.of(new TestObject3())))); + } + @ParameterizedTest @MethodSource("testStreamOfMapEntryArguments") public void methodList(Map.Entry ignore) { @@ -165,4 +174,10 @@ Map getMap() { } } + + static class TestObject3 { + + static final String value = "one"; + + } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomListConverter.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomListConverter.java index b55a0c77607a4..3ec9b108d9b13 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomListConverter.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomListConverter.java @@ -25,6 +25,7 @@ public class CustomListConverter extends CollectionConverter { List.of().getClass().getName(), List.of(Integer.MAX_VALUE).getClass().getName(), Arrays.asList(Integer.MAX_VALUE).getClass().getName(), + Collections.unmodifiableList(List.of()).getClass().getName(), Collections.emptyList().getClass().getName()); public CustomListConverter(Mapper mapper) { From da432a81739d31aa219dda9d3597ffc4153e210e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 21:40:24 +0000 Subject: [PATCH 47/81] Bump snakeyaml from 1.32 to 1.33 Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.32 to 1.33. - [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-1.33..snakeyaml-1.32) --- updated-dependencies: - dependency-name: org.yaml:snakeyaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] (cherry picked from commit 6cb1b9a1f00f00822acb1d323079fd0043177f23) --- 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 a9f070520e9a4..2cd50e0d08580 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -159,7 +159,7 @@ 9.3.1 1.0.11 4.16.1 - 1.32 + 1.33 6.0.0 4.7.1 1.5.2 From 5366e9d6c909de8aa69a5350ebbb49b22fc26585 Mon Sep 17 00:00:00 2001 From: yesunch9 Date: Wed, 28 Sep 2022 21:06:31 +0200 Subject: [PATCH 48/81] Fix NPE when the subprotocal of websocket is null (cherry picked from commit 47350dba4e85ab1deb0e7d5ad5e01c9973fa5963) --- .../SmallRyeGraphQLOverWebSocketHandler.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLOverWebSocketHandler.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLOverWebSocketHandler.java index 2bb65a37f5d50..2cae3cd455bb6 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLOverWebSocketHandler.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLOverWebSocketHandler.java @@ -34,7 +34,12 @@ protected void doHandle(final RoutingContext ctx) { if (event.succeeded()) { ServerWebSocket serverWebSocket = event.result(); String subprotocol = serverWebSocket.subProtocol(); - GraphQLWebsocketHandler handler = null; + if (subprotocol == null) { + log.warn("Websocket subprotocol is null"); + serverWebSocket.close(); + return; + } + GraphQLWebsocketHandler handler; switch (subprotocol) { case "graphql-transport-ws": handler = new GraphQLTransportWSSubprotocolHandler( @@ -49,14 +54,14 @@ protected void doHandle(final RoutingContext ctx) { serverWebSocket.close(); return; } - log.debug("Starting websocket with subprotocol = " + subprotocol); + log.debugf("Starting websocket with subprotocol = %s", subprotocol); GraphQLWebsocketHandler finalHandler = handler; serverWebSocket.closeHandler(v -> finalHandler.onClose()); serverWebSocket.endHandler(v -> finalHandler.onEnd()); - serverWebSocket.exceptionHandler(t -> finalHandler.onThrowable(t)); - serverWebSocket.textMessageHandler(m -> finalHandler.onMessage(m)); + serverWebSocket.exceptionHandler(finalHandler::onThrowable); + serverWebSocket.textMessageHandler(finalHandler::onMessage); } else { - log.warn("WebSocket failed", event.cause()); + log.warn("Websocket failed", event.cause()); } }); } else { From 8c7436a5e6a528b694d0968c9113600bc59bc38c Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Wed, 28 Sep 2022 15:53:49 +0200 Subject: [PATCH 49/81] Fix the original JAR check when the finalName in the Quarkus plugin is different from the one in the POM's build section (cherry picked from commit 34faea6d40af502f25e232be41cd783230fe01ac) --- .../quarkus/deployment/QuarkusAugmentor.java | 14 ++++++-- .../deployment/jbang/JBangAugmentorImpl.java | 3 ++ .../builditem/BuildSystemTargetBuildItem.java | 20 +++++++++++ .../pkg/builditem/OutputTargetBuildItem.java | 33 +++++++++++++++++++ .../pkg/steps/JarResultBuildStep.java | 7 ++-- .../runner/bootstrap/AugmentActionImpl.java | 3 ++ .../maven/QuarkusBootstrapProvider.java | 21 ++++++------ .../bootstrap/app/QuarkusBootstrap.java | 13 ++++++++ .../java/io/quarkus/maven/it/PackageIT.java | 13 ++++++-- .../uberjar-maven-plugin-config/pom.xml | 3 ++ 10 files changed, 112 insertions(+), 18 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java index ca3519a4be75a..dba8f2b5eeb88 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java @@ -57,6 +57,7 @@ public class QuarkusAugmentor { private final Path targetDir; private final ApplicationModel effectiveModel; private final String baseName; + private final String originalBaseName; private final boolean rebuild; private final boolean auxiliaryApplication; private final Optional auxiliaryDevModeType; @@ -75,6 +76,7 @@ public class QuarkusAugmentor { this.targetDir = builder.targetDir; this.effectiveModel = builder.effectiveModel; this.baseName = builder.baseName; + this.originalBaseName = builder.originalBaseName; this.deploymentClassLoader = builder.deploymentClassLoader; this.rebuild = builder.rebuild; this.devModeType = builder.devModeType; @@ -149,7 +151,7 @@ public BuildResult run() throws Exception { .produce(new LaunchModeBuildItem(launchMode, devModeType == null ? Optional.empty() : Optional.of(devModeType), auxiliaryApplication, auxiliaryDevModeType, test)) - .produce(new BuildSystemTargetBuildItem(targetDir, baseName, rebuild, + .produce(new BuildSystemTargetBuildItem(targetDir, baseName, originalBaseName, rebuild, buildSystemProperties == null ? new Properties() : buildSystemProperties)) .produce(new AppModelProviderBuildItem(effectiveModel)); for (PathCollection i : additionalApplicationArchives) { @@ -194,6 +196,8 @@ public static Builder builder() { public static final class Builder { + private static final String QUARKUS_APPLICATION = "quarkus-application"; + public DevModeType auxiliaryDevModeType; boolean rebuild; List additionalApplicationArchives = new ArrayList<>(); @@ -208,7 +212,8 @@ public static final class Builder { Properties buildSystemProperties; ApplicationModel effectiveModel; - String baseName = "quarkus-application"; + String baseName = QUARKUS_APPLICATION; + String originalBaseName = QUARKUS_APPLICATION; ClassLoader deploymentClassLoader; DevModeType devModeType; boolean test; @@ -302,6 +307,11 @@ public Builder setBaseName(String baseName) { return this; } + public Builder setOriginalBaseName(String originalBaseName) { + this.originalBaseName = originalBaseName; + return this; + } + public Properties getBuildSystemProperties() { return buildSystemProperties; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java b/core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java index 01eeaca5051ee..946699643f592 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/jbang/JBangAugmentorImpl.java @@ -54,6 +54,9 @@ public void accept(CuratedApplication curatedApplication, Map re if (quarkusBootstrap.getBaseName() != null) { builder.setBaseName(quarkusBootstrap.getBaseName()); } + if (quarkusBootstrap.getOriginalBaseName() != null) { + builder.setOriginalBaseName(quarkusBootstrap.getOriginalBaseName()); + } boolean auxiliaryApplication = curatedApplication.getQuarkusBootstrap().isAuxiliaryApplication(); builder.setAuxiliaryApplication(auxiliaryApplication); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/BuildSystemTargetBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/BuildSystemTargetBuildItem.java index 3fa8da77c6a2b..ccec015fef4c1 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/BuildSystemTargetBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/BuildSystemTargetBuildItem.java @@ -12,12 +12,28 @@ public final class BuildSystemTargetBuildItem extends SimpleBuildItem { private final Path outputDirectory; private final String baseName; + private final String originalBaseName; private final boolean rebuild; private final Properties buildSystemProps; + /** + * @deprecated in favor of {@link #BuildSystemTargetBuildItem(Path, String, String, boolean, Properties)} + * + * @param outputDirectory build output directory + * @param baseName base runner name + * @param rebuild indicates whether the application is being re-built + * @param buildSystemProps build system properties + */ + @Deprecated(forRemoval = true) public BuildSystemTargetBuildItem(Path outputDirectory, String baseName, boolean rebuild, Properties buildSystemProps) { + this(outputDirectory, baseName, baseName, rebuild, buildSystemProps); + } + + public BuildSystemTargetBuildItem(Path outputDirectory, String baseName, String originalBaseName, boolean rebuild, + Properties buildSystemProps) { this.outputDirectory = outputDirectory; this.baseName = baseName; + this.originalBaseName = originalBaseName; this.rebuild = rebuild; this.buildSystemProps = buildSystemProps; } @@ -30,6 +46,10 @@ public String getBaseName() { return baseName; } + public String getOriginalBaseName() { + return originalBaseName; + } + public boolean isRebuild() { return rebuild; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/OutputTargetBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/OutputTargetBuildItem.java index 24479c85b677c..6eb601ace4e1d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/OutputTargetBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/OutputTargetBuildItem.java @@ -17,14 +17,32 @@ public final class OutputTargetBuildItem extends SimpleBuildItem { private final Path outputDirectory; private final String baseName; + private final String originalBaseName; private final boolean rebuild; private final Properties buildSystemProperties; private final Optional> includedOptionalDependencies; + /** + * @deprecated in favor of {@link #OutputTargetBuildItem(Path, String, String, boolean, Properties, Optional)} + * + * @param outputDirectory build output directory + * @param baseName base runner name + * @param rebuild indicates whether the application is being re-built + * @param buildSystemProperties build system properties + * @param includedOptionalDependencies included optional dependencies + */ + @Deprecated(forRemoval = true) public OutputTargetBuildItem(Path outputDirectory, String baseName, boolean rebuild, Properties buildSystemProperties, Optional> includedOptionalDependencies) { + this(outputDirectory, baseName, baseName, rebuild, buildSystemProperties, includedOptionalDependencies); + } + + public OutputTargetBuildItem(Path outputDirectory, String baseName, String originalBaseName, boolean rebuild, + Properties buildSystemProperties, + Optional> includedOptionalDependencies) { this.outputDirectory = outputDirectory; this.baseName = baseName; + this.originalBaseName = originalBaseName; this.rebuild = rebuild; this.buildSystemProperties = buildSystemProperties; this.includedOptionalDependencies = includedOptionalDependencies; @@ -34,10 +52,25 @@ public Path getOutputDirectory() { return outputDirectory; } + /** + * Base name for the Quarkus application runner file. + * + * @return base name for the Quarkus application runner file + */ public String getBaseName() { return baseName; } + /** + * The base name (not including the extension) of the original JAR generated by the build system. + * This name could be different from the value of {@ #getBaseName()}, which will be used for the Quarkus runner file. + * + * @return name of the original JAR generated by the build system + */ + public String getOriginalBaseName() { + return originalBaseName; + } + public boolean isRebuild() { return rebuild; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java index 3e092d838d8fa..dd28e38cf88ac 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java @@ -158,12 +158,13 @@ OutputTargetBuildItem outputTarget(BuildSystemTargetBuildItem bst, PackageConfig Optional> includedOptionalDependencies; if (packageConfig.filterOptionalDependencies) { includedOptionalDependencies = Optional.of(packageConfig.includedOptionalDependencies - .map(set -> set.stream().map(s -> (ArtifactKey) GACT.fromString(s)).collect(Collectors.toSet())) + .map(set -> set.stream().map(s -> ArtifactKey.fromString(s)).collect(Collectors.toSet())) .orElse(Collections.emptySet())); } else { includedOptionalDependencies = Optional.empty(); } - return new OutputTargetBuildItem(path, name, bst.isRebuild(), bst.getBuildSystemProps(), includedOptionalDependencies); + return new OutputTargetBuildItem(path, name, bst.getOriginalBaseName(), bst.isRebuild(), bst.getBuildSystemProps(), + includedOptionalDependencies); } @BuildStep(onlyIf = JarRequired.class) @@ -283,7 +284,7 @@ private JarBuildItem buildUberJar(CurateOutcomeBuildItem curateOutcomeBuildItem, //for uberjars we move the original jar, so there is only a single jar in the output directory final Path standardJar = outputTargetBuildItem.getOutputDirectory() - .resolve(outputTargetBuildItem.getBaseName() + ".jar"); + .resolve(outputTargetBuildItem.getOriginalBaseName() + ".jar"); final Path originalJar = Files.exists(standardJar) ? standardJar : null; diff --git a/core/deployment/src/main/java/io/quarkus/runner/bootstrap/AugmentActionImpl.java b/core/deployment/src/main/java/io/quarkus/runner/bootstrap/AugmentActionImpl.java index 6d9cca4a075da..a2007cf6732db 100644 --- a/core/deployment/src/main/java/io/quarkus/runner/bootstrap/AugmentActionImpl.java +++ b/core/deployment/src/main/java/io/quarkus/runner/bootstrap/AugmentActionImpl.java @@ -291,6 +291,9 @@ private BuildResult runAugment(boolean firstRun, Set changedResources, if (quarkusBootstrap.getBaseName() != null) { builder.setBaseName(quarkusBootstrap.getBaseName()); } + if (quarkusBootstrap.getOriginalBaseName() != null) { + builder.setOriginalBaseName(quarkusBootstrap.getOriginalBaseName()); + } boolean auxiliaryApplication = curatedApplication.getQuarkusBootstrap().isAuxiliaryApplication(); builder.setAuxiliaryApplication(auxiliaryApplication); diff --git a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java index d3529126378ca..7815865536442 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/QuarkusBootstrapProvider.java @@ -37,8 +37,6 @@ import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.Dependency; -import io.quarkus.maven.dependency.GACT; -import io.quarkus.maven.dependency.GACTV; import io.quarkus.maven.dependency.ResolvedArtifactDependency; import io.quarkus.runtime.LaunchMode; import io.smallrye.common.expression.Expression; @@ -59,7 +57,7 @@ public class QuarkusBootstrapProvider implements Closeable { .concurrencyLevel(4).softValues().initialCapacity(10).build(); static ArtifactKey getProjectId(MavenProject project) { - return new GACT(project.getGroupId(), project.getArtifactId()); + return ArtifactKey.ga(project.getGroupId(), project.getArtifactId()); } public RepositorySystem repositorySystem() { @@ -209,12 +207,13 @@ private CuratedApplication doBootstrap(QuarkusBootstrapMojo mojo, LaunchMode mod final List localProjects = mojo.mavenProject().getCollectedProjects(); final Set localProjectKeys = new HashSet<>(localProjects.size()); for (MavenProject p : localProjects) { - localProjectKeys.add(new GACT(p.getGroupId(), p.getArtifactId())); + localProjectKeys.add(ArtifactKey.ga(p.getGroupId(), p.getArtifactId())); } reloadableModules = new HashSet<>(localProjects.size() + 1); for (Artifact a : mojo.mavenProject().getArtifacts()) { - if (localProjectKeys.contains(new GACT(a.getGroupId(), a.getArtifactId()))) { - reloadableModules.add(new GACT(a.getGroupId(), a.getArtifactId(), a.getClassifier(), a.getType())); + if (localProjectKeys.contains(ArtifactKey.ga(a.getGroupId(), a.getArtifactId()))) { + reloadableModules + .add(ArtifactKey.of(a.getGroupId(), a.getArtifactId(), a.getClassifier(), a.getType())); } } reloadableModules.add(appArtifact.getKey()); @@ -228,7 +227,6 @@ private CuratedApplication doBootstrap(QuarkusBootstrapMojo mojo, LaunchMode mod } catch (AppModelResolverException e) { throw new MojoExecutionException("Failed to bootstrap application in " + mode + " mode", e); } - QuarkusBootstrap.Builder builder = QuarkusBootstrap.builder() .setAppArtifact(appModel.getAppArtifact()) .setExistingModel(appModel) @@ -237,6 +235,7 @@ private CuratedApplication doBootstrap(QuarkusBootstrapMojo mojo, LaunchMode mod .setBuildSystemProperties(effectiveProperties) .setProjectRoot(mojo.baseDir().toPath()) .setBaseName(mojo.finalName()) + .setOriginalBaseName(mojo.mavenProject().getBuild().getFinalName()) .setTargetDirectory(mojo.buildDir().toPath()) .setForcedDependencies(forcedDependencies); @@ -277,12 +276,12 @@ protected CuratedApplication bootstrapApplication(QuarkusBootstrapMojo mojo, Lau return prodApp == null ? prodApp = doBootstrap(mojo, mode) : prodApp; } - protected GACTV managingProject(QuarkusBootstrapMojo mojo) { + protected ArtifactCoords managingProject(QuarkusBootstrapMojo mojo) { if (mojo.appArtifactCoords() == null) { return null; } final Artifact artifact = mojo.mavenProject().getArtifact(); - return new GACTV(artifact.getGroupId(), artifact.getArtifactId(), + return ArtifactCoords.of(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), artifact.getArtifactHandler().getExtension(), artifact.getVersion()); } @@ -321,7 +320,7 @@ private ArtifactCoords appArtifact(QuarkusBootstrapMojo mojo) } final String groupId = coordsArr[0]; final String artifactId = coordsArr[1]; - String classifier = ""; + String classifier = ArtifactCoords.DEFAULT_CLASSIFIER; String type = ArtifactCoords.TYPE_JAR; String version = null; if (coordsArr.length == 3) { @@ -349,7 +348,7 @@ private ArtifactCoords appArtifact(QuarkusBootstrapMojo mojo) } } - return new GACTV(groupId, artifactId, classifier, type, version); + return ArtifactCoords.of(groupId, artifactId, classifier, type, version); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/QuarkusBootstrap.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/QuarkusBootstrap.java index 001b08b33e944..3757ed11b36e3 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/QuarkusBootstrap.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/QuarkusBootstrap.java @@ -65,6 +65,7 @@ public class QuarkusBootstrap implements Serializable { private final Properties buildSystemProperties; private final String baseName; + private final String originalBaseName; private final Path targetDirectory; private final Mode mode; @@ -102,6 +103,7 @@ private QuarkusBootstrap(Builder builder) { this.test = builder.test; this.localProjectDiscovery = builder.localProjectDiscovery; this.baseName = builder.baseName; + this.originalBaseName = builder.originalJarName; this.baseClassLoader = builder.baseClassLoader; this.targetDirectory = builder.targetDirectory; this.appModelResolver = builder.appModelResolver; @@ -235,6 +237,10 @@ public String getBaseName() { return baseName; } + public String getOriginalBaseName() { + return originalBaseName; + } + public ClassLoader getBaseClassLoader() { return baseClassLoader; } @@ -262,6 +268,7 @@ public List getClassLoaderEventListeners() { public Builder clonedBuilder() { Builder builder = new Builder() .setBaseName(baseName) + .setOriginalBaseName(originalBaseName) .setProjectRoot(projectRoot) .setBaseClassLoader(baseClassLoader) .setBuildSystemProperties(buildSystemProperties) @@ -304,6 +311,7 @@ public static class Builder { boolean rebuild; PathCollection applicationRoot; String baseName; + String originalJarName; Path projectRoot; ClassLoader baseClassLoader = ClassLoader.getSystemClassLoader(); final List additionalApplicationArchives = new ArrayList<>(); @@ -417,6 +425,11 @@ public Builder setBaseName(String baseName) { return this; } + public Builder setOriginalBaseName(String originalJarName) { + this.originalJarName = originalJarName; + return this; + } + public Builder setBaseClassLoader(ClassLoader baseClassLoader) { this.baseClassLoader = baseClassLoader; return this; diff --git a/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java b/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java index 0f9806b0f26b3..c3565bb6b3e35 100644 --- a/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java +++ b/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java @@ -56,10 +56,19 @@ public void testUberJarMavenPluginConfiguration() throws MavenInvocationException, IOException, InterruptedException { testDir = initProject("projects/uberjar-maven-plugin-config"); running = new RunningInvoker(testDir, false); - final MavenProcessInvocationResult result = running.execute(Collections.singletonList("package"), - Collections.emptyMap()); + final MavenProcessInvocationResult result = running.execute(List.of("install", "-DskipTests"), Map.of()); assertThat(result.getProcess().waitFor()).isEqualTo(0); + final Path localRunner = getTargetDir().toPath().resolve("acme-quarkus-runner.jar"); + assertThat(localRunner).exists(); + + // make sure the runner jar was attached, there was a bug of the runner jar not being attached when the + // finalName option in the Quarkus plugin didn't match the finalName ocnfigured in the POM's build section + final Path installedRunner = running.getLocalRepositoryDirectory().toPath().resolve("org").resolve("acme") + .resolve("acme") + .resolve("1.0-SNAPSHOT").resolve("acme-1.0-SNAPSHOT-runner.jar"); + assertThat(installedRunner).exists(); + verifyUberJar(); } 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 3885f5e319342..4bb9c2d219eea 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 @@ -61,6 +61,9 @@ + + acme-quarkus + From 807329d03ca65bc4e26f0a2d8e197673e83b7660 Mon Sep 17 00:00:00 2001 From: Adler Fleurant <2609856+AdlerFleurant@users.noreply.github.com> Date: Sun, 25 Sep 2022 11:25:23 -0400 Subject: [PATCH 50/81] Ignore non-existing class and resource paths during JVM test Some resource and class locations may not exist when resolving paths. We now ensure that these non-existing path are not added to the application root. Signed-off-by: Adler Fleurant <2609856+AdlerFleurant@users.noreply.github.com> (cherry picked from commit 33f1e6c8d1ba87b8bf849d89fd5164c9441f4d0c) --- .../java/io/quarkus/maven/it/PackageIT.java | 8 ++ .../deployment/pom.xml | 44 +++++++++ .../integration-tests/pom.xml | 93 +++++++++++++++++++ .../test/java/org/example/ExampleTest.java | 12 +++ .../extension-test-with-no-main/pom.xml | 77 +++++++++++++++ .../runtime/pom.xml | 50 ++++++++++ .../AbstractJvmQuarkusTestExtension.java | 36 +++---- 7 files changed, 299 insertions(+), 21 deletions(-) create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/deployment/pom.xml create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/integration-tests/pom.xml create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/integration-tests/src/test/java/org/example/ExampleTest.java create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/pom.xml create mode 100644 integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/runtime/pom.xml diff --git a/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java b/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java index c3565bb6b3e35..86e576f03b12a 100644 --- a/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java +++ b/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java @@ -51,6 +51,14 @@ public void testExtensionRemovedResources() throws Exception { assertThat(result.getProcess().waitFor()).isEqualTo(0); } + @Test + public void testExtensionTestWithNoMain() throws Exception { + testDir = initProject("projects/extension-test-with-no-main"); + running = new RunningInvoker(testDir, false); + final MavenProcessInvocationResult result = running.execute(List.of("verify"), Map.of()); + assertThat(result.getProcess().waitFor()).isEqualTo(0); + } + @Test public void testUberJarMavenPluginConfiguration() throws MavenInvocationException, IOException, InterruptedException { diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/deployment/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/deployment/pom.xml new file mode 100644 index 0000000000000..9a2863adf1ba0 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/deployment/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + org.example + extension-test-with-no-main + 1.0.0-SNAPSHOT + + runtime-deployment + Extension test with no main - Deployment + + + io.quarkus + quarkus-arc-deployment + + + org.example + runtime + \${project.version} + + + io.quarkus + quarkus-junit5-internal + test + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + \${quarkus.version} + + + + + + + diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/integration-tests/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/integration-tests/pom.xml new file mode 100644 index 0000000000000..8e400633eec0b --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/integration-tests/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + org.example + extension-test-with-no-main + 1.0.0-SNAPSHOT + + extension-test-with-no-main-integration-tests + Extension test with no main - Integration Tests + + true + + + + io.quarkus + quarkus-resteasy + + + org.example + runtime + \${project.version} + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + \${project.build.directory}/\${project.build.finalName}-runner + org.jboss.logmanager.LogManager + \${maven.home} + + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + \${native.surefire.skip} + + + + + + false + native + + + + diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/integration-tests/src/test/java/org/example/ExampleTest.java b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/integration-tests/src/test/java/org/example/ExampleTest.java new file mode 100644 index 0000000000000..89b9cc85fd5a2 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/integration-tests/src/test/java/org/example/ExampleTest.java @@ -0,0 +1,12 @@ +package org.example; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +@QuarkusTest +public class ExampleTest { + + @Test + public void test() {} + +} \ No newline at end of file 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 new file mode 100644 index 0000000000000..e92c9ddb51621 --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + org.example + extension-test-with-no-main + 1.0.0-SNAPSHOT + pom + Extension test with no main + + deployment + runtime + integration-tests + + + 3.8.1 + ${surefire-plugin.version} + 11 + UTF-8 + UTF-8 + @project.version@ + 3.0.0-M7 + + + + + io.quarkus + quarkus-bom + \${quarkus.version} + pom + import + + + + + + + + io.quarkus + quarkus-maven-plugin + \${quarkus.version} + + + maven-surefire-plugin + \${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + \${maven.home} + \${settings.localRepository} + + + + + maven-failsafe-plugin + \${failsafe-plugin.version} + + + org.jboss.logmanager.LogManager + \${maven.home} + \${settings.localRepository} + + + + + maven-compiler-plugin + \${compiler-plugin.version} + + + -parameters + + + + + + + diff --git a/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/runtime/pom.xml b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/runtime/pom.xml new file mode 100644 index 0000000000000..352593d4e387a --- /dev/null +++ b/integration-tests/maven/src/test/resources-filtered/projects/extension-test-with-no-main/runtime/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + org.example + extension-test-with-no-main + 1.0.0-SNAPSHOT + + runtime + Extension test with no main - Runtime + + + io.quarkus + quarkus-arc + + + + + + io.quarkus + quarkus-extension-maven-plugin + \${quarkus.version} + + + compile + + extension-descriptor + + + \${project.groupId}:\${project.artifactId}-deployment:\${project.version} + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + \${quarkus.version} + + + + + + + diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java index 9021f60b113b7..629dd7c3df92f 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java @@ -12,6 +12,7 @@ import java.util.Deque; import java.util.HashMap; import java.util.Map; +import java.util.function.Consumer; import java.util.stream.Collectors; import javax.enterprise.inject.Alternative; @@ -59,20 +60,21 @@ protected PrepareResult createAugmentor(ExtensionContext context, Class addToBuilderIfConditionMet = path -> { + if (path != null && Files.exists(path) && !rootBuilder.contains(path)) { + rootBuilder.add(path); + } + }; + if (!appClassLocation.equals(testClassLocation)) { - rootBuilder.add(testClassLocation); + addToBuilderIfConditionMet.accept(testClassLocation); // if test classes is a dir, we should also check whether test resources dir exists as a separate dir (gradle) // TODO: this whole app/test path resolution logic is pretty dumb, it needs be re-worked using proper workspace discovery final Path testResourcesLocation = PathTestHelper.getResourcesForClassesDirOrNull(testClassLocation, "test"); - if (testResourcesLocation != null) { - rootBuilder.add(testResourcesLocation); - } + addToBuilderIfConditionMet.accept(testResourcesLocation); } originalCl = Thread.currentThread().getContextClassLoader(); - Map sysPropRestore = new HashMap<>(); - sysPropRestore.put(ProfileManager.QUARKUS_TEST_PROFILE_PROP, - System.getProperty(ProfileManager.QUARKUS_TEST_PROFILE_PROP)); // clear the test.url system property as the value leaks into the run when using different profiles System.clearProperty("test.url"); @@ -99,17 +101,15 @@ protected PrepareResult createAugmentor(ExtensionContext context, Class Date: Thu, 29 Sep 2022 16:04:40 +0200 Subject: [PATCH 51/81] Updates to Infinispan 13.0.11.Final (cherry picked from commit 60edacd7efb027fb37771d7f5faad0df82729698) --- 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 2cd50e0d08580..64eb322c17993 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -130,7 +130,7 @@ 5.9.1 1.5.0 6.14.2 - 13.0.10.Final + 13.0.11.Final 4.4.3.Final 2.9.3 4.1.79.Final From 4f399ba697c4707f2b0cf89c1b1c18764db86fcb Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Thu, 29 Sep 2022 13:03:25 +0200 Subject: [PATCH 52/81] Run fireOnDataFetchError on batch errors (cherry picked from commit 0ac49b1f6f8247e6ec0f8661f77833a009e7602a) --- .../spi/datafetcher/QuarkusDefaultDataFetcher.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/QuarkusDefaultDataFetcher.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/QuarkusDefaultDataFetcher.java index a6d3e00dbb110..923f053356b54 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/QuarkusDefaultDataFetcher.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/QuarkusDefaultDataFetcher.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CompletionStage; +import java.util.function.Consumer; import org.eclipse.microprofile.graphql.GraphQLException; @@ -108,10 +109,19 @@ private CompletionStage> invokeBatchBlocking(DataFetchingEnvironment dfe return (List) operationInvoker.invokePrivileged(arguments); }); + // this gets called on a batch error, so that error callbacks can run with the proper context too + Consumer onErrorConsumer = threadContext.contextualConsumer((Throwable exception) -> { + eventEmitter.fireOnDataFetchError(dfe.getExecutionId().toString(), exception); + }); + // Here call blocking with context BlockingHelper.runBlocking(vc, contextualCallable, result); - return result.future().toCompletionStage(); - + return result.future().toCompletionStage() + .whenComplete((resultList, error) -> { + if (error != null) { + onErrorConsumer.accept(error); + } + }); } private boolean runBlocking(DataFetchingEnvironment dfe) { From 2fab40a69d9f0d0675352cd017954f09ac617105 Mon Sep 17 00:00:00 2001 From: Bernardo Coferre Date: Thu, 29 Sep 2022 11:18:34 -0300 Subject: [PATCH 53/81] OIDC: Fix wait strategy not respecting timeout (cherry picked from commit ecb64020de845d26b017c48967a6f74ca6e63b7f) --- .../devservices/keycloak/KeycloakDevServicesProcessor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java index ec87b2893adb6..aba26ac8346d3 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java @@ -410,6 +410,8 @@ public QuarkusOidcContainer(DockerImageName dockerImageName, OptionalInt fixedEx this.fixedExposedPort = fixedExposedPort; this.startCommand = startCommand; this.showLogs = showLogs; + + super.setWaitStrategy(Wait.forLogMessage(".*Keycloak.*started.*", 1)); } @Override @@ -482,7 +484,6 @@ protected void configure() { } LOG.infof("Using %s powered Keycloak distribution", keycloakX ? "Quarkus" : "WildFly"); - super.setWaitStrategy(Wait.forLogMessage(".*Keycloak.*started.*", 1)); } private Integer findRandomPort() { From ad58ed7ab49dbb096a8cb1956c942ed197478a66 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Thu, 29 Sep 2022 15:46:06 +0200 Subject: [PATCH 54/81] Fix https://github.com/quarkusio/quarkus/issues/28292 Registers the StorkClientRequestFilter when we detect a URI starting with stork:// or storks://. (cherry picked from commit 1563b88de6d6d08e83e9df2fb27ccd384c57ade6) --- .../reactive/stork/StorkIntegrationTest.java | 17 +++++++++ .../reactive/client/impl/WebTargetImpl.java | 37 ++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/StorkIntegrationTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/StorkIntegrationTest.java index f57a1d437d771..32c92031137db 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/StorkIntegrationTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/stork/StorkIntegrationTest.java @@ -4,6 +4,10 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.net.URI; +import java.net.URISyntaxException; + +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.core.UriBuilder; import org.eclipse.microprofile.rest.client.RestClientBuilder; import org.eclipse.microprofile.rest.client.inject.RestClient; @@ -34,6 +38,19 @@ void shouldDetermineUrlViaStork() { assertThat(greeting).isEqualTo("hello, black and white bird"); } + @Test + void shouldDetermineUrlViaStorkWhenUsingTarget() throws URISyntaxException { + String greeting = ClientBuilder.newClient().target("stork://hello-service/hello").request().get(String.class); + assertThat(greeting).isEqualTo("Hello"); + + greeting = ClientBuilder.newClient().target(new URI("stork://hello-service/hello")).request().get(String.class); + assertThat(greeting).isEqualTo("Hello"); + + greeting = ClientBuilder.newClient().target(UriBuilder.fromUri("stork://hello-service/hello")).request() + .get(String.class); + assertThat(greeting).isEqualTo("Hello"); + } + @Test void shouldDetermineUrlViaStorkCDI() { String greeting = client.echo("big bird"); diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java index 56fc7d2f4d0fc..1b4b0355ae6e1 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java @@ -320,10 +320,45 @@ private void abortIfClosed() { protected InvocationBuilderImpl createQuarkusRestInvocationBuilder(HttpClient client, UriBuilder uri, ConfigurationImpl configuration) { - return new InvocationBuilderImpl(uri.build(), restClient, client, this, configuration, + URI actualUri = uri.build(); + registerStorkFilterIfNeeded(configuration, actualUri); + return new InvocationBuilderImpl(actualUri, restClient, client, this, configuration, handlerChain.setPreClientSendHandler(preClientSendHandler), requestContext); } + /** + * If the URI starts with stork:// or storks://, then register the StorkClientRequestFilter automatically. + * + * @param configuration the configuration + * @param actualUri the uri + */ + private static void registerStorkFilterIfNeeded(ConfigurationImpl configuration, URI actualUri) { + if (actualUri.getScheme() != null && actualUri.getScheme().startsWith("stork") + && !isStorkAlreadyRegistered(configuration)) { + configuration.register(StorkClientRequestFilter.class); + } + } + + /** + * Checks if the Stork request filter is already registered. + * We cannot use configuration.isRegistered, as the user registration uses a subclass, and so fail the equality + * expectation. + *

+ * This method prevents having the stork filter registered twice: once because the uri starts with stork:// and, + * once from the user. + * + * @param configuration the configuration + * @return {@code true} if stork is already registered. + */ + private static boolean isStorkAlreadyRegistered(ConfigurationImpl configuration) { + for (Class clazz : configuration.getClasses()) { + if (clazz.getName().startsWith(StorkClientRequestFilter.class.getName())) { + return true; + } + } + return false; + } + @Override public WebTargetImpl property(String name, Object value) { abortIfClosed(); From afa42b8a2780b830269ace27f8b993cdef53bd89 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 29 Sep 2022 17:42:41 +0100 Subject: [PATCH 55/81] Prefix OIDC DevUI service path with forward slash (cherry picked from commit 06566c0f2c7897de1f3ab0e9c7314c8871a2c8da) --- .../KeycloakDevServicesProcessor.java | 4 +-- .../resources/dev-templates/provider.html | 36 +++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java index aba26ac8346d3..4fe2d13be6e9e 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java @@ -547,7 +547,7 @@ private FileTime getRealmFileLastModifiedDate(Optional realm) { private void createDefaultRealm(String keycloakUrl, Map users, String oidcClientId, String oidcClientSecret) { - RealmRepresentation realm = createRealmRep(); + RealmRepresentation realm = createDefaultRealmRep(); realm.getClients().add(createClient(oidcClientId, oidcClientSecret)); for (Map.Entry entry : users.entrySet()) { @@ -638,7 +638,7 @@ private List getUserRoles(String user) { : roles; } - private RealmRepresentation createRealmRep() { + private RealmRepresentation createDefaultRealmRep() { RealmRepresentation realm = new RealmRepresentation(); realm.setRealm(getDefaultRealmName()); diff --git a/extensions/oidc/deployment/src/main/resources/dev-templates/provider.html b/extensions/oidc/deployment/src/main/resources/dev-templates/provider.html index 853e969933789..b73635a79de3f 100644 --- a/extensions/oidc/deployment/src/main/resources/dev-templates/provider.html +++ b/extensions/oidc/deployment/src/main/resources/dev-templates/provider.html @@ -117,7 +117,7 @@ } function testServiceWithAccessToken(){ - var servicePath = $('#servicePath').val(); + var servicePath = getServicePath(); $.post("testServiceWithToken", { serviceUrl: "http://localhost:" + port + servicePath, @@ -129,7 +129,7 @@ } function testServiceWithIdToken(){ - var servicePath = $('#servicePath').val(); + var servicePath = getServicePath(); $.post("testServiceWithToken", { serviceUrl: "http://localhost:" + port + servicePath, @@ -253,9 +253,23 @@ return token; } } + + function printLoginError(){ + var search = window.location.search; + var errorDescription = search.match(/error_description=([^&]+)/)[1]; + $('#errorDescription').append(""); + $('#errorDescription').append("" + "Login error: " + decodeURI(errorDescription).replaceAll("+", " ") + ""); + } + {/if} {/if} +{#if info:oidcApplicationType is 'web-app'} +function signInToService(servicePath) { + window.open("http://localhost:" + port + servicePath); +} +{/if} + {#if info:oidcGrantType is 'password'} function testServiceWithPassword(userName, password, servicePath){ @@ -394,15 +408,9 @@ $('#results').append("
"); } -function printLoginError(){ - var search = window.location.search; - var errorDescription = search.match(/error_description=([^&]+)/)[1]; - $('#errorDescription').append(""); - $('#errorDescription').append("" + "Login error: " + decodeURI(errorDescription).replaceAll("+", " ") + ""); -} - -function signInToService(servicePath) { - window.open("http://localhost:" + port + servicePath); +function getServicePath() { + var servicePath = $('#servicePath').val(); + return servicePath.startsWith("/") ? servicePath : ("/" + servicePath); } function clearResults() { @@ -607,7 +615,7 @@

Decoded
- +
{#if info:swaggerIsAvailable??} @@ -648,7 +656,7 @@
Decoded
- +
{#if info:swaggerIsAvailable??} @@ -687,7 +695,7 @@
Decoded
- +
From 7511480521b6d163d24442556fb7451dc4306376 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 30 Sep 2022 10:21:26 +0200 Subject: [PATCH 56/81] Add missing debug config to avoid warnings Fixes #27778 (cherry picked from commit 15424f4e8fb0142571d5793f7453131f56a38453) --- .../java/io/quarkus/deployment/DebugConfig.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/DebugConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/DebugConfig.java index e54d05c735570..bdacdd8419c53 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/DebugConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/DebugConfig.java @@ -25,4 +25,18 @@ public class DebugConfig { */ @ConfigItem Optional generatedClassesDir; + + /** + * If set to a directory, all transformed classes (e.g. Panache entities) will be written into that directory + */ + @ConfigItem + Optional transformedClassesDir; + + /** + * If set to a directory, ZIG files for generated code will be written into that directory. + *

+ * A ZIG file is a textual representation of the generated code that is referenced in the stacktraces. + */ + @ConfigItem + Optional generatedSourcesDir; } From 2cc682c0d00a7bde9cc47bb15f2f30ea3e875955 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Wed, 28 Sep 2022 09:34:17 +0100 Subject: [PATCH 57/81] Bump Hibernate Reactive from 1.1.7.Final to 1.1.8.Final (cherry picked from commit 53dad3b4e7cca9ed961707579e965b52a0702f20) --- 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 64eb322c17993..3c4d887a73f22 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -90,7 +90,7 @@ 1.5.1 5.6.12.Final 1.12.9 - 1.1.7.Final + 1.1.8.Final 6.2.5.Final 6.1.7.Final 5.13.1.Alpha1 From 742eb5cc7622a1837bfad1fc5cad258b17519aaa Mon Sep 17 00:00:00 2001 From: Adler Fleurant <2609856+AdlerFleurant@users.noreply.github.com> Date: Tue, 20 Sep 2022 10:33:54 -0400 Subject: [PATCH 58/81] Distrust maven from system properties Check if maven settings file exists when getting from system properties. Null check on mavenSettings. Fix nearby typos. Fixes #28090 Signed-off-by: Adler Fleurant <2609856+AdlerFleurant@users.noreply.github.com> (cherry picked from commit 2b0923f614c04aa16ece0c11ae4e70d5f850e00a) --- .../io/quarkus/cli/build/MavenRunner.java | 8 +-- .../test/java/io/quarkus/cli/CliDriver.java | 53 +++++++++---------- .../cli/RegistryClientBuilderTestBase.java | 12 ++--- 3 files changed, 35 insertions(+), 38 deletions(-) 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 d8bcb2b29ab90..2c801a999901a 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 @@ -1,6 +1,7 @@ package io.quarkus.cli.build; import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayDeque; import java.util.ArrayList; @@ -32,6 +33,7 @@ import picocli.CommandLine; public class MavenRunner implements BuildSystemRunner { + public static String MAVEN_SETTINGS = "maven.settings"; static final String[] windowsWrapper = { "mvnw.cmd", "mvnw.bat" }; static final String otherWrapper = "mvnw"; @@ -238,13 +240,13 @@ void setMavenProperties(ArrayDeque args, boolean batchMode) { args.addFirst("-Dstyle.color=always"); } - String mavenSettings = propertiesOptions.properties.remove("maven.settings"); + String mavenSettings = propertiesOptions.properties.remove(MAVEN_SETTINGS); if (mavenSettings != null && !mavenSettings.isEmpty()) { args.add("-s"); args.add(mavenSettings); } else { - mavenSettings = System.getProperty("maven.settings"); - if (mavenSettings != null && !mavenSettings.isEmpty()) { + mavenSettings = System.getProperty(MAVEN_SETTINGS); + if (mavenSettings != null && !mavenSettings.isEmpty() && Files.exists(Path.of(mavenSettings))) { args.add("-s"); args.add(mavenSettings); } diff --git a/devtools/cli/src/test/java/io/quarkus/cli/CliDriver.java b/devtools/cli/src/test/java/io/quarkus/cli/CliDriver.java index ddf060402c37e..2ee05931bef2b 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/CliDriver.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/CliDriver.java @@ -1,5 +1,8 @@ package io.quarkus.cli; +import static io.quarkus.cli.build.MavenRunner.MAVEN_SETTINGS; +import static org.apache.maven.cli.MavenCli.LOCAL_REPO_PROPERTY; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; @@ -12,6 +15,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.function.BinaryOperator; +import java.util.function.UnaryOperator; import org.junit.jupiter.api.Assertions; @@ -20,6 +26,9 @@ public class CliDriver { static final PrintStream stdout = System.out; static final PrintStream stderr = System.err; + private static final BinaryOperator ARG_FORMATTER = (key, value) -> "-D" + key + "=" + value; + private static final UnaryOperator REPO_ARG_FORMATTER = value -> ARG_FORMATTER.apply(LOCAL_REPO_PROPERTY, value); + private static final UnaryOperator SETTINGS_ARG_FORMATTER = value -> ARG_FORMATTER.apply(MAVEN_SETTINGS, value); public static class CliDriverBuilder { @@ -63,8 +72,10 @@ public Result execute() throws Exception { newArgs.subList(index, newArgs.size()).clear(); } - propagateProperty("maven.repo.local", mavenLocalRepo, newArgs); - propagateProperty("maven.settings", mavenSettings, newArgs); + Optional.ofNullable(mavenLocalRepo).or(CliDriver::getMavenLocalRepoProperty).map(REPO_ARG_FORMATTER) + .ifPresent(newArgs::add); + Optional.ofNullable(mavenSettings).or(CliDriver::getMavenSettingsProperty).map(SETTINGS_ARG_FORMATTER) + .ifPresent(newArgs::add); newArgs.add("--cli-test"); newArgs.add("--cli-test-dir"); @@ -81,7 +92,7 @@ public Result execute() throws Exception { PrintStream errPs = new PrintStream(err); System.setErr(errPs); - final Map originalProps = collectOverridenProps(newArgs); + final Map originalProps = collectOverriddenProps(newArgs); Result result = new Result(); QuarkusCli cli = new QuarkusCli(); @@ -109,7 +120,7 @@ protected void resetProperties(Map originalProps) { } } - protected Map collectOverridenProps(List newArgs) { + protected Map collectOverriddenProps(List newArgs) { final Map originalProps = new HashMap<>(); for (String s : newArgs) { if (s.startsWith("-D")) { @@ -121,22 +132,14 @@ protected Map collectOverridenProps(List newArgs) { originalProps.put(propName, origValue); } else if (System.getProperties().contains(propName)) { originalProps.put(propName, "true"); + } else { + originalProps.put(propName, null); } } } } return originalProps; } - - private static void propagateProperty(String propName, String testValue, List args) { - if (testValue == null) { - testValue = System.getProperty(propName); - if (testValue == null) { - return; - } - } - args.add("-D" + propName + "=" + testValue); - } } public static CliDriverBuilder builder() { @@ -144,14 +147,8 @@ public static CliDriverBuilder builder() { } public static void preserveLocalRepoSettings(Collection args) { - String s = convertToProperty("maven.repo.local"); - if (s != null) { - args.add(s); - } - s = convertToProperty("maven.settings"); - if (s != null) { - args.add(s); - } + getMavenLocalRepoProperty().map(REPO_ARG_FORMATTER).ifPresent(args::add); + getMavenSettingsProperty().map(SETTINGS_ARG_FORMATTER).ifPresent(args::add); } public static Result executeArbitraryCommand(Path startingDir, String... args) throws Exception { @@ -439,12 +436,12 @@ public static void validateApplicationProperties(Path projectRoot, List "Properties file should contain " + conf + ". Found:\n" + propertiesFile)); } - private static String convertToProperty(String name) { - String value = System.getProperty(name); - if (value != null) { - return "-D" + name + "=" + value; - } - return null; + private static Optional getMavenLocalRepoProperty() { + return Optional.ofNullable(System.getProperty(LOCAL_REPO_PROPERTY)); + } + + private static Optional getMavenSettingsProperty() { + return Optional.ofNullable(System.getProperty(MAVEN_SETTINGS)).filter(value -> Files.exists(Path.of(value))); } private static void retryDelete(File file) { diff --git a/devtools/cli/src/test/java/io/quarkus/cli/RegistryClientBuilderTestBase.java b/devtools/cli/src/test/java/io/quarkus/cli/RegistryClientBuilderTestBase.java index 00a402bcda5ca..d101dbe001899 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/RegistryClientBuilderTestBase.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/RegistryClientBuilderTestBase.java @@ -1,7 +1,7 @@ package io.quarkus.cli; -import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.nio.file.Files; @@ -59,7 +59,7 @@ static void setup() throws Exception { final BootstrapMavenContext mavenContext = new BootstrapMavenContext( BootstrapMavenContext.config().setWorkspaceDiscovery(false)); - final Settings settings = getBaseMavenSettings(mavenContext.getUserSettings().toPath()); + final Settings settings = getBaseMavenSettings(mavenContext.getUserSettings()); Profile profile = new Profile(); settings.addActiveProfile("qs-test-registry"); @@ -106,11 +106,9 @@ protected static String getCurrentQuarkusVersion() { return v; } - private static Settings getBaseMavenSettings(Path mavenSettings) throws IOException { - if (Files.exists(mavenSettings)) { - try (BufferedReader reader = Files.newBufferedReader(mavenSettings)) { - return new DefaultSettingsReader().read(reader, Map.of()); - } + private static Settings getBaseMavenSettings(File mavenSettings) throws IOException { + if (mavenSettings != null && mavenSettings.exists()) { + return new DefaultSettingsReader().read(mavenSettings, Map.of()); } return new Settings(); } From d0ea8cf6550ad89f4ceebcac8f708ce4c212d8a0 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 30 Sep 2022 14:00:33 +0200 Subject: [PATCH 59/81] Mark SmallRye OpenTracing as deprecated (cherry picked from commit 9b0268d53ca2ec6577f87c21dff0f174b381bc57) --- docs/src/main/asciidoc/opentracing.adoc | 10 ++++++++++ .../src/main/resources/META-INF/quarkus-extension.yaml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/opentracing.adoc b/docs/src/main/asciidoc/opentracing.adoc index 3c5e6d83c84f8..2905ffb25afc2 100644 --- a/docs/src/main/asciidoc/opentracing.adoc +++ b/docs/src/main/asciidoc/opentracing.adoc @@ -4,12 +4,22 @@ and pull requests should be submitted there: https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// = Using OpenTracing +:extension-status: deprecated include::./attributes.adoc[] This guide explains how your Quarkus application can utilize OpenTracing to provide distributed tracing for interactive web applications. +[IMPORTANT] +==== +xref:opentelemetry.adoc[OpenTelemetry] is the recommended approach to tracing and telemetry for Quarkus. + +When Quarkus will upgrade to Eclipse MicroProfile 6, the SmallRye OpenTracing support will be discontinued. +==== + +include::{includes}/extension-status.adoc[] + == Prerequisites :prerequisites-docker: diff --git a/extensions/smallrye-opentracing/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/smallrye-opentracing/runtime/src/main/resources/META-INF/quarkus-extension.yaml index 8837f47db11c9..ef5bb82541cfe 100644 --- a/extensions/smallrye-opentracing/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/smallrye-opentracing/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -11,7 +11,7 @@ metadata: guide: "https://quarkus.io/guides/opentracing" categories: - "observability" - status: "stable" + status: "deprecated" config: - "quarkus.jaeger." - "mp.opentracing." From 488654e03e7a3d3590a05342cee1d427413f5fe8 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 30 Sep 2022 14:00:50 +0200 Subject: [PATCH 60/81] Mark SmallRye Metrics as deprecated (cherry picked from commit 27b728d8246083dab422b4c8a606a4c30d35c41c) --- docs/src/main/asciidoc/smallrye-metrics.adoc | 10 +++++++++- .../src/main/resources/META-INF/quarkus-extension.yaml | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/smallrye-metrics.adoc b/docs/src/main/asciidoc/smallrye-metrics.adoc index c78629b116f46..9324e141fb783 100644 --- a/docs/src/main/asciidoc/smallrye-metrics.adoc +++ b/docs/src/main/asciidoc/smallrye-metrics.adoc @@ -5,6 +5,7 @@ https://github.com/quarkusio/quarkus/tree/main/docs/src/main/asciidoc //// = SmallRye Metrics +:extension-status: deprecated include::./attributes.adoc[] @@ -15,7 +16,14 @@ SmallRye Metrics allows applications to gather metrics and statistics that provi Apart from application-specific metrics described in this guide, you may also use built-in metrics exposed by various Quarkus extensions. These are described in the guide for each particular extension that supports built-in metrics. -IMPORTANT: xref:micrometer.adoc[Micrometer] is the recommended approach to metrics for Quarkus. Use the SmallRye Metrics extension when it is required to retain MicroProfile specification compatibility. +[IMPORTANT] +==== +xref:micrometer.adoc[Micrometer] is the recommended approach to metrics for Quarkus. Use the SmallRye Metrics extension when it is required to retain MicroProfile specification compatibility. + +When Quarkus will upgrade to Eclipse MicroProfile 6, the SmallRye Metrics support will be discontinued. +==== + +include::{includes}/extension-status.adoc[] == Prerequisites diff --git a/extensions/smallrye-metrics/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/smallrye-metrics/runtime/src/main/resources/META-INF/quarkus-extension.yaml index ef89ea41073fa..c0f47046841be 100644 --- a/extensions/smallrye-metrics/runtime/src/main/resources/META-INF/quarkus-extension.yaml +++ b/extensions/smallrye-metrics/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -12,7 +12,7 @@ metadata: guide: "https://quarkus.io/guides/microprofile-metrics" categories: - "observability" - status: "stable" + status: "deprecated" config: - "quarkus.smallrye-metrics." - "mp.metrics." From e626844456ce3eb18c5eca8dd5e514ecc767118e Mon Sep 17 00:00:00 2001 From: kdnakt Date: Sat, 1 Oct 2022 11:05:18 +0900 Subject: [PATCH 61/81] docs: fix sample code in csrf project (cherry picked from commit 931015f565640ef8f0158a07016c721be76c3774) --- docs/src/main/asciidoc/security-csrf-prevention.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/security-csrf-prevention.adoc b/docs/src/main/asciidoc/security-csrf-prevention.adoc index 51cf8106dea3a..e877219bf1b54 100644 --- a/docs/src/main/asciidoc/security-csrf-prevention.adoc +++ b/docs/src/main/asciidoc/security-csrf-prevention.adoc @@ -104,7 +104,7 @@ public class UserNameResource { @Path("/csrfTokenForm") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.TEXT_PLAIN) - public String postCsrfTokenForm(@FormParam("name") String name) { + public String postCsrfTokenForm(@FormParam("name") String userName) { return userName; <3> } } @@ -123,7 +123,7 @@ quarkus.csrf-reactive.form-field-name=csrftoken quarkus.csrf-reactive.cookie-name=csrftoken ---- -Note that the CSRF filter has to read the input stream in order to verify the token and then re-create the stream for the application code to read it as well. The filter performs this work on an event loop thread so for small form payloads, such as the one shown in the example above, it will have negligible peformance side-effects. However if you deal with large form payloads then it is recommended to compare the CSRF form field and cookie values in the application code: +Note that the CSRF filter has to read the input stream in order to verify the token and then re-create the stream for the application code to read it as well. The filter performs this work on an event loop thread so for small form payloads, such as the one shown in the example above, it will have negligible performance side-effects. However if you deal with large form payloads then it is recommended to compare the CSRF form field and cookie values in the application code: [source,java] ---- From 316e2384a132707746a8b17fe2bb065784a45f29 Mon Sep 17 00:00:00 2001 From: Andreas Eberle Date: Sat, 1 Oct 2022 12:15:14 +0200 Subject: [PATCH 62/81] Fix typos in hiberate-orm-panache.adoc (cherry picked from commit 9f45615fe8516f20863822d268e42d6adfc55bbe) --- docs/src/main/asciidoc/hibernate-orm-panache.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/hibernate-orm-panache.adoc b/docs/src/main/asciidoc/hibernate-orm-panache.adoc index 8859677edd6dc..5008778c42402 100644 --- a/docs/src/main/asciidoc/hibernate-orm-panache.adoc +++ b/docs/src/main/asciidoc/hibernate-orm-panache.adoc @@ -815,7 +815,7 @@ public class RaceWeight { } // Only the race and the average weight will be loaded -PanacheQuery query = Person.find("select d.race, AVG(d.weight) from Dog d group by d.race).project(RaceWeight.class); +PanacheQuery query = Person.find("select d.race, AVG(d.weight) from Dog d group by d.race").project(RaceWeight.class); ---- <1> Hibernate ORM will use this constructor. When the query has a select clause, it is possible to have multiple constructors. @@ -827,7 +827,7 @@ For example, this will fail: [source,java] ---- -PanacheQuery query = Person.find("select new MyView(d.race, AVG(d.weight)) from Dog d group by d.race).project(AnotherView.class); +PanacheQuery query = Person.find("select new MyView(d.race, AVG(d.weight)) from Dog d group by d.race").project(AnotherView.class); ---- ==== From 758f6ac5ac411ceafb02ce9e28a909a81d2b6aa4 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Sat, 1 Oct 2022 17:10:27 +0200 Subject: [PATCH 63/81] Remove a call to the Uni.log() operator which is a left-over from a previous PR (cherry picked from commit eca537fec39c0c1f13f7bfbaf428f4cdbe36fdb2) --- .../reactive/client/impl/StorkClientRequestFilter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilter.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilter.java index c81f86cb0fb87..020d0d42f6fcd 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilter.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/StorkClientRequestFilter.java @@ -37,8 +37,7 @@ public void filter(ResteasyReactiveClientRequestContext requestContext) { try { serviceInstance = Stork.getInstance() .getService(serviceName) - .selectInstanceAndRecordStart(measureTime) - .log(); + .selectInstanceAndRecordStart(measureTime); } catch (Throwable e) { log.error("Error selecting service instance for serviceName: " + serviceName, e); requestContext.resume(e); From dadebbcd2ce8dcc1c27d6af39befa5a42ae49c21 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Fri, 30 Sep 2022 16:18:16 +0200 Subject: [PATCH 64/81] Update to Vert.x 4.3.4 * Bump dependency * Update substitutions * Add missing methods (cherry picked from commit 6b795e76aa8656f15379c61230257d904d11619e) --- bom/application/pom.xml | 2 +- .../runtime/graal/VertxSubstitutions.java | 103 +++++++----------- .../reactive/client/impl/ClientImpl.java | 10 ++ .../resteasy-reactive/pom.xml | 2 +- 4 files changed, 50 insertions(+), 67 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 3c4d887a73f22..70099e2b8f2c0 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -109,7 +109,7 @@ 1.0.1.Final 1.20.1.Final 3.4.3.Final - 4.3.3 + 4.3.4 4.5.13 4.4.15 diff --git a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/graal/VertxSubstitutions.java b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/graal/VertxSubstitutions.java index b5174a1f87ff3..0a7313ca0473b 100644 --- a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/graal/VertxSubstitutions.java +++ b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/core/runtime/graal/VertxSubstitutions.java @@ -6,22 +6,21 @@ import java.util.concurrent.ConcurrentMap; import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLException; import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509KeyManager; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import io.netty.handler.ssl.ApplicationProtocolConfig; -import io.netty.handler.ssl.OpenSslServerContext; +import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslProvider; import io.vertx.core.MultiMap; import io.vertx.core.Promise; import io.vertx.core.Vertx; -import io.vertx.core.VertxException; import io.vertx.core.dns.AddressResolverOptions; import io.vertx.core.eventbus.EventBusOptions; import io.vertx.core.eventbus.impl.HandlerHolder; @@ -140,83 +139,57 @@ EventBusOptions options() { } } -@TargetClass(className = "io.vertx.core.net.impl.SSLHelper") -final class Target_io_vertx_core_net_impl_SSLHelper { - - @Alias - private boolean client; +@TargetClass(className = "io.vertx.core.spi.tls.DefaultSslContextFactory") +final class Target_DefaultSslContextFactory { @Alias private Set enabledCipherSuites; - @Alias - private boolean openSsl; - @Alias private List applicationProtocols; @Alias - private KeyManagerFactory getKeyMgrFactory(VertxInternal vertx) throws Exception { - return null; - } + private ClientAuth clientAuth; @Substitute - private SslContext createContext(VertxInternal vertx, boolean useAlpn, X509KeyManager mgr, - TrustManagerFactory trustMgrFactory) { - try { - SslContextBuilder builder; - if (client) { - builder = SslContextBuilder.forClient(); - KeyManagerFactory keyMgrFactory = getKeyMgrFactory(vertx); - if (keyMgrFactory != null) { - builder.keyManager(keyMgrFactory); - } - } else { - if (mgr != null) { - builder = SslContextBuilder.forServer(mgr.getPrivateKey(null), null, mgr.getCertificateChain(null)); - } else { - KeyManagerFactory keyMgrFactory = getKeyMgrFactory(vertx); - if (keyMgrFactory == null) { - throw new VertxException("Key/certificate is mandatory for SSL"); - } - builder = SslContextBuilder.forServer(keyMgrFactory); - } - } - Collection cipherSuites = enabledCipherSuites; - if (openSsl) { - throw new UnsupportedOperationException("OpenSSL not supported in native images"); - } else { - builder.sslProvider(SslProvider.JDK); - if (cipherSuites == null || cipherSuites.isEmpty()) { - cipherSuites = Target_io_vertx_core_net_impl_DefaultJDKCipherSuite.get(); - } - } - if (trustMgrFactory != null) { - builder.trustManager(trustMgrFactory); + private SslContext createContext(boolean useAlpn, boolean client, KeyManagerFactory kmf, TrustManagerFactory tmf) + throws SSLException { + SslContextBuilder builder; + if (client) { + builder = SslContextBuilder.forClient(); + if (kmf != null) { + builder.keyManager(kmf); } - if (cipherSuites != null && cipherSuites.size() > 0) { - builder.ciphers(cipherSuites); - } - if (useAlpn && applicationProtocols != null && applicationProtocols.size() > 0) { - builder.applicationProtocolConfig(new ApplicationProtocolConfig( - ApplicationProtocolConfig.Protocol.ALPN, - ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, - ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, - applicationProtocols)); - } - SslContext ctx = builder.build(); - if (ctx instanceof OpenSslServerContext) { - throw new UnsupportedOperationException("OpenSSL not supported in native images"); - } - return ctx; - } catch (Exception e) { - throw new VertxException(e); + } else { + builder = SslContextBuilder.forServer(kmf); + } + Collection cipherSuites = enabledCipherSuites; + builder.sslProvider(SslProvider.JDK); + if (cipherSuites == null || cipherSuites.isEmpty()) { + cipherSuites = Target_io_vertx_core_spi_tls_DefaultJDKCipherSuite.get(); + } + if (tmf != null) { + builder.trustManager(tmf); + } + if (cipherSuites != null && cipherSuites.size() > 0) { + builder.ciphers(cipherSuites); + } + if (useAlpn && applicationProtocols != null && applicationProtocols.size() > 0) { + builder.applicationProtocolConfig(new ApplicationProtocolConfig( + ApplicationProtocolConfig.Protocol.ALPN, + ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, + ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, + applicationProtocols)); + } + if (clientAuth != null) { + builder.clientAuth(clientAuth); } + return builder.build(); } } -@TargetClass(className = "io.vertx.core.net.impl.DefaultJDKCipherSuite") -final class Target_io_vertx_core_net_impl_DefaultJDKCipherSuite { +@TargetClass(className = "io.vertx.core.spi.tls.DefaultJDKCipherSuite") +final class Target_io_vertx_core_spi_tls_DefaultJDKCipherSuite { @Alias static List get() { return null; diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java index d0cf2b2261fbd..a7ea3759d8a86 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java @@ -463,11 +463,21 @@ public long setPeriodic(long l, Handler handler) { return getDelegate().setPeriodic(l, handler); } + @Override + public long setPeriodic(long initialDelay, long delay, Handler handler) { + return getDelegate().setPeriodic(initialDelay, delay, handler); + } + @Override public TimeoutStream periodicStream(long l) { return getDelegate().periodicStream(l); } + @Override + public TimeoutStream periodicStream(long initialDelay, long delay) { + return getDelegate().periodicStream(initialDelay, delay); + } + @Override public boolean cancelTimer(long l) { return getDelegate().cancelTimer(l); diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 54c84b2bb6f67..b64a423e7572f 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -57,7 +57,7 @@ 1.1.6 1.7.0 1.13.1 - 4.3.2 + 4.3.4 4.5.1 1.0.0.Final 2.0.0.Final From 8c0e800cc8750ffb2305c4aedd7afc228cba6325 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 30 Sep 2022 11:30:51 +0200 Subject: [PATCH 65/81] Avoid Maven imports in core/deployment The Maven classes won't be available in a Gradle environment. Also install forbiddenapis to make sure they don't come back. Relates to #28301 (cherry picked from commit 49b23524c91c30f6fbe95f14e4dc709fbd16d516) --- build-parent/pom.xml | 5 + core/deployment/banned-signatures.txt | 3 + core/deployment/pom.xml | 22 ++++ .../deployment/dev/IsolatedDevModeMain.java | 7 +- .../dev/QuarkusDevModeLauncher.java | 4 +- .../deployment/util/CommandLineUtil.java | 107 ++++++++++++++++++ extensions/hibernate-orm/pom.xml | 1 - 7 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 core/deployment/banned-signatures.txt create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/util/CommandLineUtil.java diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 2a9d7f36089c1..eb50b9cd424df 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -701,6 +701,11 @@ asciidoctor-maven-plugin ${asciidoctor-maven-plugin.version} + + de.thetaphi + forbiddenapis + ${forbiddenapis-maven-plugin.version} + diff --git a/core/deployment/banned-signatures.txt b/core/deployment/banned-signatures.txt new file mode 100644 index 0000000000000..447cdd1634461 --- /dev/null +++ b/core/deployment/banned-signatures.txt @@ -0,0 +1,3 @@ +@defaultMessage Don't use Maven classes. They won't be available when using Gradle. +org.apache.maven.** +org.codehaus.plexus.** \ No newline at end of file diff --git a/core/deployment/pom.xml b/core/deployment/pom.xml index 18a27d767cca7..f87114058b104 100644 --- a/core/deployment/pom.xml +++ b/core/deployment/pom.xml @@ -196,6 +196,28 @@ + + de.thetaphi + forbiddenapis + + + verify-forbidden-apis + + + false + + ./banned-signatures.txt + + false + true + + compile + + check + + + + 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 318a4c8e544e9..2f0a5402a4bc6 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 @@ -22,8 +22,6 @@ import java.util.function.Consumer; import java.util.function.Predicate; -import org.apache.maven.shared.utils.cli.CommandLineException; -import org.apache.maven.shared.utils.cli.CommandLineUtils; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.jboss.logging.Logger; import org.jboss.logmanager.formatters.ColorPatternFormatter; @@ -47,6 +45,7 @@ import io.quarkus.deployment.dev.testing.MessageFormat; import io.quarkus.deployment.dev.testing.TestSupport; import io.quarkus.deployment.steps.ClassTransformingBuildStep; +import io.quarkus.deployment.util.CommandLineUtil; import io.quarkus.dev.appstate.ApplicationStartException; import io.quarkus.dev.console.DevConsoleManager; import io.quarkus.dev.spi.DeploymentFailedStartHandler; @@ -122,8 +121,8 @@ public void accept(Integer integer) { public void accept(String args) { try { context.setArgs( - CommandLineUtils.translateCommandline(args)); - } catch (CommandLineException e) { + CommandLineUtil.translateCommandline(args)); + } catch (Exception e) { log.error("Failed to parse command line", e); return; } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java index eced94cb17d69..7108a534536b0 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/QuarkusDevModeLauncher.java @@ -28,11 +28,11 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import org.apache.maven.shared.utils.cli.CommandLineUtils; import org.jboss.logging.Logger; import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.deployment.dev.DevModeContext.ModuleInfo; +import io.quarkus.deployment.util.CommandLineUtil; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.runtime.logging.JBossVersion; import io.quarkus.runtime.util.JavaVersionUtil; @@ -476,7 +476,7 @@ protected void prepare() throws Exception { args.add("-jar"); args.add(tempFile.getAbsolutePath()); if (applicationArgs != null) { - args.addAll(Arrays.asList(CommandLineUtils.translateCommandline(applicationArgs))); + args.addAll(Arrays.asList(CommandLineUtil.translateCommandline(applicationArgs))); } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/CommandLineUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/CommandLineUtil.java new file mode 100644 index 0000000000000..b9fdc7d7ffe59 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/CommandLineUtil.java @@ -0,0 +1,107 @@ +package io.quarkus.deployment.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * This class contains code coming from org.apache.maven.shared.utils.cli.CommandLineUtils. + *

+ * We don't want to directly use code coming from Maven as this artifact should be Maven-agnostic. + * + * @author Trygve Laugstøl + */ +public final class CommandLineUtil { + + private CommandLineUtil() { + } + + /** + * @param toProcess The command line to translate. + * @return The array of translated parts. + * @throws IllegalStateException in case of unbalanced quotes. + */ + public static String[] translateCommandline(String toProcess) { + if ((toProcess == null) || (toProcess.length() == 0)) { + return new String[0]; + } + + // parse with a simple finite state machine + + final int normal = 0; + final int inQuote = 1; + final int inDoubleQuote = 2; + boolean inEscape = false; + int state = normal; + final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' \\", true); + List tokens = new ArrayList(); + StringBuilder current = new StringBuilder(); + + while (tok.hasMoreTokens()) { + String nextTok = tok.nextToken(); + switch (state) { + case inQuote: + if ("\'".equals(nextTok)) { + if (inEscape) { + current.append(nextTok); + inEscape = false; + } else { + state = normal; + } + } else { + current.append(nextTok); + inEscape = "\\".equals(nextTok); + } + break; + case inDoubleQuote: + if ("\"".equals(nextTok)) { + if (inEscape) { + current.append(nextTok); + inEscape = false; + } else { + state = normal; + } + } else { + current.append(nextTok); + inEscape = "\\".equals(nextTok); + } + break; + default: + if ("\'".equals(nextTok)) { + if (inEscape) { + inEscape = false; + current.append(nextTok); + } else { + state = inQuote; + } + } else if ("\"".equals(nextTok)) { + if (inEscape) { + inEscape = false; + current.append(nextTok); + } else { + state = inDoubleQuote; + } + } else if (" ".equals(nextTok)) { + if (current.length() != 0) { + tokens.add(current.toString()); + current.setLength(0); + } + } else { + current.append(nextTok); + inEscape = "\\".equals(nextTok); + } + break; + } + } + + if (current.length() != 0) { + tokens.add(current.toString()); + } + + if ((state == inQuote) || (state == inDoubleQuote)) { + throw new IllegalStateException("unbalanced quotes in " + toProcess); + } + + return tokens.toArray(new String[tokens.size()]); + } +} diff --git a/extensions/hibernate-orm/pom.xml b/extensions/hibernate-orm/pom.xml index 4efc7831aedb9..eb2a3a90ace5c 100644 --- a/extensions/hibernate-orm/pom.xml +++ b/extensions/hibernate-orm/pom.xml @@ -25,7 +25,6 @@ de.thetaphi forbiddenapis - ${forbiddenapis-maven-plugin.version} verify-forbidden-apis From 7d71023659fb9d085e67232d1c31edb113415cbd Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Sun, 2 Oct 2022 09:46:35 +0300 Subject: [PATCH 66/81] Revert "Prevent possible memory leak from Reactive REST Client multipart upload usage" The change introduced by c08560a2 to address https://github.com/eclipse-vertx/vert.x/issues/4473 is no longer needed because Vert.x 4.3.4 solves this issue via https://github.com/eclipse-vertx/vert.x/commit/9c35a76f1c8002fc25f4e1e3569d0a5168a966e7 (cherry picked from commit 09be06f4c7d25bcbbf3a5d8162a0d2d877f3d1cd) --- .../multipart/QuarkusMultipartFormUpload.java | 12 -- .../impl/multipart/RequestTrackingPipe.java | 172 ------------------ 2 files changed, 184 deletions(-) delete mode 100644 independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/RequestTrackingPipe.java diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java index eaf1af8765fbf..5e8ffaa6d4d1f 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java @@ -20,7 +20,6 @@ import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.impl.headers.HeadersAdaptor; -import io.vertx.core.streams.Pipe; import io.vertx.core.streams.ReadStream; import io.vertx.core.streams.impl.InboundBuffer; @@ -233,15 +232,4 @@ public synchronized QuarkusMultipartFormUpload endHandler(Handler handler) return this; } - /** - * The reason we need a custom Pipe here is that if we don't manually throttle the read stream, - * the fact that the underlying connection is shared can lead to exhaustion of heap memory. - * See this for more details. - */ - @Override - public Pipe pipe() { - pause(); - return new RequestTrackingPipe<>(this, 16); - } - } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/RequestTrackingPipe.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/RequestTrackingPipe.java deleted file mode 100644 index 6b6b3f21d892e..0000000000000 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/RequestTrackingPipe.java +++ /dev/null @@ -1,172 +0,0 @@ -package org.jboss.resteasy.reactive.client.impl.multipart; - -import java.util.concurrent.atomic.AtomicLong; - -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Promise; -import io.vertx.core.VertxException; -import io.vertx.core.streams.Pipe; -import io.vertx.core.streams.ReadStream; -import io.vertx.core.streams.WriteStream; - -/** - * Based on {@link io.vertx.core.streams.impl.PipeImpl}. - * We can't use the aforementioned class as it is not open enough for us to add the read tracking we want. - */ -class RequestTrackingPipe implements Pipe { - - private final long maxInFlightReads; - - private final Promise result; - private final ReadStream src; - private boolean endOnSuccess = true; - private boolean endOnFailure = true; - private WriteStream dst; - - private boolean manuallyPaused = false; - - private final AtomicLong inFlightReads = new AtomicLong(0); - - public RequestTrackingPipe(ReadStream src, long maxInFlightReads) { - this.src = src; - this.maxInFlightReads = maxInFlightReads; - this.result = Promise.promise(); - - // Set handlers now - src.endHandler(result::tryComplete); - src.exceptionHandler(result::tryFail); - } - - @Override - public synchronized Pipe endOnFailure(boolean end) { - endOnFailure = end; - return this; - } - - @Override - public synchronized Pipe endOnSuccess(boolean end) { - endOnSuccess = end; - return this; - } - - @Override - public synchronized Pipe endOnComplete(boolean end) { - endOnSuccess = end; - endOnFailure = end; - return this; - } - - private void handleWriteResult(AsyncResult ack) { - long currentInFlightReads = inFlightReads.decrementAndGet(); - if (currentInFlightReads <= (maxInFlightReads / 2)) { - synchronized (RequestTrackingPipe.this) { - if (manuallyPaused) { - manuallyPaused = false; - src.resume(); - } - } - } - if (ack.failed()) { - result.tryFail(new WriteException(ack.cause())); - } - } - - @Override - public void to(WriteStream ws, Handler> completionHandler) { - if (ws == null) { - throw new NullPointerException(); - } - boolean endOnSuccess; - boolean endOnFailure; - synchronized (RequestTrackingPipe.this) { - if (dst != null) { - throw new IllegalStateException(); - } - dst = ws; - endOnSuccess = this.endOnSuccess; - endOnFailure = this.endOnFailure; - } - Handler drainHandler = v -> src.resume(); - src.handler(item -> { - ws.write(item, this::handleWriteResult); - long currentInFlightReads = inFlightReads.incrementAndGet(); - if (ws.writeQueueFull()) { - src.pause(); - ws.drainHandler(drainHandler); - } else if (currentInFlightReads > maxInFlightReads) { - synchronized (RequestTrackingPipe.this) { - src.pause(); - manuallyPaused = true; - } - } - }); - src.resume(); - result.future().onComplete(ar -> { - try { - src.handler(null); - } catch (Exception ignore) { - } - try { - src.exceptionHandler(null); - } catch (Exception ignore) { - } - try { - src.endHandler(null); - } catch (Exception ignore) { - } - if (ar.succeeded()) { - handleSuccess(completionHandler); - } else { - Throwable err = ar.cause(); - if (err instanceof WriteException) { - src.resume(); - err = err.getCause(); - } - handleFailure(err, completionHandler); - } - }); - } - - private void handleSuccess(Handler> completionHandler) { - if (endOnSuccess) { - dst.end(completionHandler); - } else { - completionHandler.handle(Future.succeededFuture()); - } - } - - private void handleFailure(Throwable cause, Handler> completionHandler) { - Future res = Future.failedFuture(cause); - if (endOnFailure) { - dst.end(ignore -> { - completionHandler.handle(res); - }); - } else { - completionHandler.handle(res); - } - } - - public void close() { - synchronized (this) { - src.exceptionHandler(null); - src.handler(null); - if (dst != null) { - dst.drainHandler(null); - dst.exceptionHandler(null); - } - } - VertxException err = new VertxException("Pipe closed", true); - if (result.tryFail(err)) { - src.resume(); - } - } - - private static class WriteException extends VertxException { - private WriteException(Throwable cause) { - super(cause, true); - } - } - -} From e09760673a2573a8f602a7a7e7a71618909e0629 Mon Sep 17 00:00:00 2001 From: Ozan Gunalp Date: Tue, 20 Sep 2022 20:01:22 +0100 Subject: [PATCH 67/81] Kafka Health check, admin client is created only with necessary config. Fixes #27904 (cherry picked from commit 57f18eb8792673a93c364db14ed21d205e720193) --- .../deployment/DevServicesKafkaProcessor.java | 2 +- .../kafka/client/runtime/KafkaAdminClient.java | 14 +++++++++++++- .../client/runtime/ui/KafkaTopicClient.java | 17 +++-------------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java index 0bbe4d8112266..03b704e56fe5e 100644 --- a/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java +++ b/extensions/kafka-client/deployment/src/main/java/io/quarkus/kafka/client/deployment/DevServicesKafkaProcessor.java @@ -152,7 +152,7 @@ public void createTopicPartitions(String bootstrapServers, KafkaDevServiceCfg co // get current partitions for topics asked to be created Set currentTopics = adminClient.listTopics().names() .get(adminClientTimeout, TimeUnit.MILLISECONDS); - Map partitions = adminClient.describeTopics(currentTopics).all() + Map partitions = adminClient.describeTopics(currentTopics).allTopicNames() .get(adminClientTimeout, TimeUnit.MILLISECONDS); // find new topics to create List newTopics = topicPartitions.entrySet().stream() diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaAdminClient.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaAdminClient.java index c9b75dc1d00c0..d9901b7bb12b5 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaAdminClient.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/KafkaAdminClient.java @@ -30,8 +30,13 @@ public class KafkaAdminClient { @PostConstruct void init() { - Map conf = new HashMap<>(config); + Map conf = new HashMap<>(); conf.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, DEFAULT_ADMIN_CLIENT_TIMEOUT); + for (Map.Entry entry : config.entrySet()) { + if (AdminClientConfig.configNames().contains(entry.getKey())) { + conf.put(entry.getKey(), entry.getValue().toString()); + } + } client = AdminClient.create(conf); } @@ -81,4 +86,11 @@ public Collection getAclInfo() throws InterruptedException, Executio var options = new DescribeAclsOptions().timeoutMs(1_000); return client.describeAcls(filter, options).values().get(); } + + public Map describeTopics(Collection topicNames) + throws InterruptedException, ExecutionException { + return client.describeTopics(topicNames) + .allTopicNames() + .get(); + } } diff --git a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaTopicClient.java b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaTopicClient.java index 174ef04aa08b8..8a2340fd475de 100644 --- a/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaTopicClient.java +++ b/extensions/kafka-client/runtime/src/main/java/io/quarkus/kafka/client/runtime/ui/KafkaTopicClient.java @@ -9,12 +9,9 @@ import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; -import org.apache.kafka.clients.admin.AdminClient; -import org.apache.kafka.clients.admin.AdminClientConfig; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -27,6 +24,7 @@ import org.apache.kafka.common.serialization.BytesSerializer; import org.apache.kafka.common.utils.Bytes; +import io.quarkus.kafka.client.runtime.KafkaAdminClient; import io.quarkus.kafka.client.runtime.ui.model.Order; import io.quarkus.kafka.client.runtime.ui.model.converter.KafkaModelConverter; import io.quarkus.kafka.client.runtime.ui.model.request.KafkaMessageCreateRequest; @@ -38,8 +36,8 @@ public class KafkaTopicClient { // TODO: make configurable private static final int RETRIES = 3; - //TODO: inject me - private AdminClient adminClient; + @Inject + KafkaAdminClient adminClient; KafkaModelConverter modelConverter = new KafkaModelConverter(); @@ -47,13 +45,6 @@ public class KafkaTopicClient { @Identifier("default-kafka-broker") Map config; - @PostConstruct - void init() { - Map conf = new HashMap<>(config); - conf.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, "5000"); - adminClient = AdminClient.create(conf); - } - private Producer createProducer() { Map config = new HashMap<>(this.config); @@ -260,8 +251,6 @@ var record = new ProducerRecord<>(request.getTopic(), request.getPartition(), By public List partitions(String topicName) throws ExecutionException, InterruptedException { return adminClient.describeTopics(List.of(topicName)) - .allTopicNames() - .get() .values().stream() .reduce((a, b) -> { throw new IllegalStateException( From 5fecfca88768f38e69c36f3902ff33ee2d4a1dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Sat, 1 Oct 2022 13:56:45 +0200 Subject: [PATCH 68/81] RESTEasy client - use our provider factory when accessed statically fixes: #27277 (cherry picked from commit 2116ef34ffc1da7534735e57432d370fa564a093) --- .../restclient/deployment/RestClientProcessor.java | 8 +++++++- .../io/quarkus/restclient/runtime/RestClientRecorder.java | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java b/extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java index da5eafa9b7cd4..537ba5c392865 100644 --- a/extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java +++ b/extensions/resteasy-classic/rest-client/deployment/src/main/java/io/quarkus/restclient/deployment/RestClientProcessor.java @@ -454,7 +454,7 @@ void registerProviders(BuildProducer reflectiveClass, List ignoreClientProviderBuildItems, CombinedIndexBuildItem combinedIndexBuildItem, ResteasyInjectionReadyBuildItem injectorFactory, - RestClientRecorder restClientRecorder) { + RestClientRecorder restClientRecorder, Capabilities capabilities) { for (IgnoreClientProviderBuildItem item : ignoreClientProviderBuildItems) { jaxrsProvidersToRegisterBuildItem.getProviders().remove(item.getProviderClassName()); @@ -465,6 +465,12 @@ void registerProviders(BuildProducer reflectiveClass, jaxrsProvidersToRegisterBuildItem.useBuiltIn(), jaxrsProvidersToRegisterBuildItem.getProviders(), jaxrsProvidersToRegisterBuildItem.getContributedProviders()); + if (!capabilities.isPresent(Capability.RESTEASY)) { + // ResteasyProviderFactory will use our implementation when accessing instance statically. That's not + // necessary when RESTEasy classic is present as then provider factory with correct provider classes is generated. + restClientRecorder.setResteasyProviderFactoryInstance(); + } + // register the providers for reflection for (String providerToRegister : jaxrsProvidersToRegisterBuildItem.getProviders()) { reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, providerToRegister)); diff --git a/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientRecorder.java b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientRecorder.java index 8cf6d8be030b4..da136486a8cad 100644 --- a/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientRecorder.java +++ b/extensions/resteasy-classic/rest-client/runtime/src/main/java/io/quarkus/restclient/runtime/RestClientRecorder.java @@ -46,6 +46,10 @@ public InjectorFactory getInjectorFactory() { providerFactory = clientProviderFactory; } + public void setResteasyProviderFactoryInstance() { + ResteasyProviderFactory.setInstance(providerFactory); + } + private static void registerProviders(ResteasyProviderFactory providerFactory, boolean useBuiltIn, Set providersToRegister, Set contributedProviders) { From 09ca8fcac8aa7f6a7af2924a976eb43f5b4e8f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Fri, 30 Sep 2022 10:30:26 +0200 Subject: [PATCH 69/81] Support record inside JandexUtil (cherry picked from commit 58026a133ff012361a21a569a79aaafd407fe66a) --- .../main/java/io/quarkus/deployment/util/JandexUtil.java | 7 ++++--- .../resteasy/reactive/common/processor/JandexUtil.java | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/JandexUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/JandexUtil.java index 1666ef4e6264f..c2ed9924ed9e9 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/util/JandexUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/JandexUtil.java @@ -29,6 +29,7 @@ public final class JandexUtil { public final static DotName DOTNAME_OBJECT = DotName.createSimple(Object.class.getName()); + public final static DotName DOTNAME_RECORD = DotName.createSimple("java.lang.Record"); private JandexUtil() { } @@ -119,8 +120,8 @@ private static List findParametersRecursively(Type type, DotName target, return null; } - // always end at Object - if (DOTNAME_OBJECT.equals(name)) { + // always end at Object or Record + if (DOTNAME_OBJECT.equals(name) || DOTNAME_RECORD.equals(name)) { return null; } @@ -323,7 +324,7 @@ public static ClassInfo getEnclosingClass(AnnotationInstance annotationInstance) * @throws BuildException if one of the superclasses is not indexed. */ public static boolean isSubclassOf(IndexView index, ClassInfo info, DotName parentName) throws BuildException { - if (info.superName().equals(DOTNAME_OBJECT)) { + if (info.superName().equals(DOTNAME_OBJECT) || info.superName().equals(DOTNAME_RECORD)) { return false; } if (info.superName().equals(parentName)) { diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java index fa40292c38698..9350def2aa98e 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java @@ -35,6 +35,7 @@ public final class JandexUtil { public final static DotName DOTNAME_OBJECT = DotName.createSimple(Object.class.getName()); + public final static DotName DOTNAME_RECORD = DotName.createSimple("java.lang.Record"); private JandexUtil() { } @@ -143,7 +144,7 @@ private static List findParametersRecursively(Type type, DotName target, } // always end at Object - if (DOTNAME_OBJECT.equals(name)) { + if (DOTNAME_OBJECT.equals(name) || DOTNAME_RECORD.equals(name)) { return null; } @@ -338,7 +339,7 @@ public static ClassInfo getEnclosingClass(AnnotationInstance annotationInstance) * @throws RuntimeException if one of the superclasses is not indexed. */ public static boolean isSubclassOf(IndexView index, ClassInfo info, DotName parentName) { - if (info.superName().equals(DOTNAME_OBJECT)) { + if (info.superName().equals(DOTNAME_OBJECT) || info.superName().equals(DOTNAME_RECORD)) { return false; } if (info.superName().equals(parentName)) { From 2c11b5857b1066a35f21144024aad8a4f5bdbfea Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Thu, 29 Sep 2022 13:55:36 +0200 Subject: [PATCH 70/81] Fix JandexUtil.getEnclosingClass() when it comes to record components and types (cherry picked from commit 24539043baf5a058fa313b5632c60dbfa40de91b) --- .../quarkus/deployment/util/JandexUtil.java | 30 ++++++++++++------- .../reactive/common/processor/JandexUtil.java | 30 ++++++++++++------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/JandexUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/JandexUtil.java index c2ed9924ed9e9..4d2d7e01e923d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/util/JandexUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/JandexUtil.java @@ -11,6 +11,7 @@ import java.util.Set; import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassType; @@ -289,25 +290,32 @@ private static ClassInfo fetchFromIndex(DotName dotName, IndexView index) { } /** - * Returns the enclosing class of the given annotation instance. For field or method annotations this - * will return the enclosing class. For parameters, this will return the enclosing class of the enclosing - * method. For classes, it will return the class itself. + * Returns the enclosing class of the given annotation instance. For field, method or record component annotations, + * this will return the enclosing class. For parameters, this will return the enclosing class of the enclosing + * method. For classes, it will return the class itself. For type annotations, it will return the class enclosing + * the annotated type usage. * - * @param annotationInstance the annotation whose enclosing class to look up. - * @return the enclosing class. + * @param annotationInstance the annotation whose enclosing class to look up + * @return the enclosing class */ public static ClassInfo getEnclosingClass(AnnotationInstance annotationInstance) { - switch (annotationInstance.target().kind()) { + return getEnclosingClass(annotationInstance.target()); + } + + private static ClassInfo getEnclosingClass(AnnotationTarget annotationTarget) { + switch (annotationTarget.kind()) { case FIELD: - return annotationInstance.target().asField().declaringClass(); + return annotationTarget.asField().declaringClass(); case METHOD: - return annotationInstance.target().asMethod().declaringClass(); + return annotationTarget.asMethod().declaringClass(); case METHOD_PARAMETER: - return annotationInstance.target().asMethodParameter().method().declaringClass(); + return annotationTarget.asMethodParameter().method().declaringClass(); + case RECORD_COMPONENT: + return annotationTarget.asRecordComponent().declaringClass(); case CLASS: - return annotationInstance.target().asClass(); + return annotationTarget.asClass(); case TYPE: - return annotationInstance.target().asType().asClass(); // TODO is it legal here or should I throw ? + return getEnclosingClass(annotationTarget.asType().enclosingTarget()); default: throw new RuntimeException(); // this should not occur } diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java index 9350def2aa98e..d906cdd2cdef7 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/JandexUtil.java @@ -17,6 +17,7 @@ import java.util.stream.Stream; import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.ArrayType; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.ClassType; @@ -304,25 +305,32 @@ private static ClassInfo fetchFromIndex(DotName dotName, IndexView index) { } /** - * Returns the enclosing class of the given annotation instance. For field or method annotations this - * will return the enclosing class. For parameters, this will return the enclosing class of the enclosing - * method. For classes it will return the class itself. + * Returns the enclosing class of the given annotation instance. For field, method or record component annotations, + * this will return the enclosing class. For parameters, this will return the enclosing class of the enclosing + * method. For classes, it will return the class itself. For type annotations, it will return the class enclosing + * the annotated type usage. * - * @param annotationInstance the annotation whose enclosing class to look up. - * @return the enclosing class. + * @param annotationInstance the annotation whose enclosing class to look up + * @return the enclosing class */ public static ClassInfo getEnclosingClass(AnnotationInstance annotationInstance) { - switch (annotationInstance.target().kind()) { + return getEnclosingClass(annotationInstance.target()); + } + + private static ClassInfo getEnclosingClass(AnnotationTarget annotationTarget) { + switch (annotationTarget.kind()) { case FIELD: - return annotationInstance.target().asField().declaringClass(); + return annotationTarget.asField().declaringClass(); case METHOD: - return annotationInstance.target().asMethod().declaringClass(); + return annotationTarget.asMethod().declaringClass(); case METHOD_PARAMETER: - return annotationInstance.target().asMethodParameter().method().declaringClass(); + return annotationTarget.asMethodParameter().method().declaringClass(); + case RECORD_COMPONENT: + return annotationTarget.asRecordComponent().declaringClass(); case CLASS: - return annotationInstance.target().asClass(); + return annotationTarget.asClass(); case TYPE: - return annotationInstance.target().asType().asClass(); // TODO is it legal here or should I throw ? + return getEnclosingClass(annotationTarget.asType().enclosingTarget()); default: throw new RuntimeException(); // this should not occur } From 46161dd5f4654b4ffa07156843ccf4931c3814b8 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 3 Oct 2022 09:07:59 +0300 Subject: [PATCH 71/81] Remove erroneous RESTEasy Classic import from ResteasyReactiveViolationExceptionMapper (cherry picked from commit 2f59a3aeed030aaca3ffd3b3a42044842b84f587) --- .../jaxrs/ResteasyReactiveViolationExceptionMapper.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java index ec3f4912b1f69..86f10686fcb2c 100644 --- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java @@ -18,11 +18,11 @@ import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; -import org.jboss.resteasy.api.validation.Validation; - @Provider public class ResteasyReactiveViolationExceptionMapper implements ExceptionMapper { + private static final String VALIDATION_HEADER = "validation-exception"; + @Context HttpHeaders headers; @@ -69,7 +69,7 @@ private boolean isReturnValueViolation(ConstraintViolation violation) { private Response buildViolationReportResponse(ConstraintViolationException cve) { Status status = Status.BAD_REQUEST; Response.ResponseBuilder builder = Response.status(status); - builder.header(Validation.VALIDATION_HEADER, "true"); + builder.header(VALIDATION_HEADER, "true"); // Check standard media types. MediaType mediaType = ValidatorMediaTypeUtil.getAcceptMediaTypeFromSupported(headers.getAcceptableMediaTypes()); From c5b5ec464533bfd249902940f98755fb34eace09 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 3 Oct 2022 09:43:47 +0300 Subject: [PATCH 72/81] Properly take produced media type into account when building validation error report Fixes: #28324 (cherry picked from commit 7fa40d0e095e188ba32835850ba12faae606b70a) --- .../hibernate-validator/runtime/pom.xml | 5 ++++ ...teasyReactiveViolationExceptionMapper.java | 16 +++++++---- .../runtime/jaxrs/ValidatorMediaTypeUtil.java | 12 +------- .../it/hibernate/validator/TextResource.java | 27 ++++++++++++++++++ .../hibernate/validator/TextResourceTest.java | 28 +++++++++++++++++++ 5 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 integration-tests/hibernate-validator-resteasy-reactive/src/main/java/io/quarkus/it/hibernate/validator/TextResource.java create mode 100644 integration-tests/hibernate-validator-resteasy-reactive/src/test/java/io/quarkus/it/hibernate/validator/TextResourceTest.java diff --git a/extensions/hibernate-validator/runtime/pom.xml b/extensions/hibernate-validator/runtime/pom.xml index 2ecab2bd809c7..df18cf60093ef 100644 --- a/extensions/hibernate-validator/runtime/pom.xml +++ b/extensions/hibernate-validator/runtime/pom.xml @@ -82,6 +82,11 @@ + + io.quarkus.resteasy.reactive + resteasy-reactive + true + org.jboss.spec.javax.ws.rs jboss-jaxrs-api_2.1_spec diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java index 86f10686fcb2c..7e55a79e80a09 100644 --- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ResteasyReactiveViolationExceptionMapper.java @@ -1,6 +1,9 @@ package io.quarkus.hibernate.validator.runtime.jaxrs; +import static io.quarkus.hibernate.validator.runtime.jaxrs.ValidatorMediaTypeUtil.SUPPORTED_MEDIA_TYPES; + import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -10,22 +13,19 @@ import javax.validation.ElementKind; import javax.validation.Path; import javax.validation.ValidationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; +import org.jboss.resteasy.reactive.server.core.CurrentRequestManager; + @Provider public class ResteasyReactiveViolationExceptionMapper implements ExceptionMapper { private static final String VALIDATION_HEADER = "validation-exception"; - @Context - HttpHeaders headers; - @Override public Response toResponse(ValidationException exception) { if (!(exception instanceof ResteasyReactiveViolationException)) { @@ -71,8 +71,12 @@ private Response buildViolationReportResponse(ConstraintViolationException cve) Response.ResponseBuilder builder = Response.status(status); builder.header(VALIDATION_HEADER, "true"); + var rrContext = CurrentRequestManager.get(); // Check standard media types. - MediaType mediaType = ValidatorMediaTypeUtil.getAcceptMediaTypeFromSupported(headers.getAcceptableMediaTypes()); + MediaType mediaType = ValidatorMediaTypeUtil.getAcceptMediaType( + rrContext.getHttpHeaders().getAcceptableMediaTypes(), + rrContext.getTarget() != null ? Arrays.asList(rrContext.getTarget().getProduces().getSortedMediaTypes()) + : SUPPORTED_MEDIA_TYPES); if (mediaType == null) { mediaType = MediaType.APPLICATION_JSON_TYPE; } diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ValidatorMediaTypeUtil.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ValidatorMediaTypeUtil.java index 8eeede84b2166..90e5ac4f82323 100644 --- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ValidatorMediaTypeUtil.java +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/jaxrs/ValidatorMediaTypeUtil.java @@ -10,7 +10,7 @@ */ public final class ValidatorMediaTypeUtil { - private static final List SUPPORTED_MEDIA_TYPES = Arrays.asList( + static final List SUPPORTED_MEDIA_TYPES = Arrays.asList( MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE, MediaType.TEXT_XML_TYPE, @@ -20,16 +20,6 @@ private ValidatorMediaTypeUtil() { } - /** - * Look up the right media type taking into account the HTTP request and the supported media types. - * - * @param mediaTypesFromRequest list of media types in the HTTP request. - * @return one supported media type from either the HTTP request or the annotation. - */ - public static MediaType getAcceptMediaTypeFromSupported(List mediaTypesFromRequest) { - return getAcceptMediaType(mediaTypesFromRequest, SUPPORTED_MEDIA_TYPES); - } - /** * Look up the right media type taking into account the HTTP request and the media types defined in the `@Produces` * annotation. diff --git a/integration-tests/hibernate-validator-resteasy-reactive/src/main/java/io/quarkus/it/hibernate/validator/TextResource.java b/integration-tests/hibernate-validator-resteasy-reactive/src/main/java/io/quarkus/it/hibernate/validator/TextResource.java new file mode 100644 index 0000000000000..9d7a173286183 --- /dev/null +++ b/integration-tests/hibernate-validator-resteasy-reactive/src/main/java/io/quarkus/it/hibernate/validator/TextResource.java @@ -0,0 +1,27 @@ +package io.quarkus.it.hibernate.validator; + +import javax.validation.constraints.Digits; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("text") +public class TextResource { + + @GET + @Path("/validate/{id}") + public String validate( + @Digits(integer = 5, fraction = 0, message = "numeric value out of bounds") @PathParam("id") String id) { + return id; + } + + @GET + @Path("/validate/text/{id}") + @Produces(MediaType.TEXT_PLAIN) + public String validateText( + @Digits(integer = 5, fraction = 0, message = "numeric value out of bounds") @PathParam("id") String id) { + return id; + } +} diff --git a/integration-tests/hibernate-validator-resteasy-reactive/src/test/java/io/quarkus/it/hibernate/validator/TextResourceTest.java b/integration-tests/hibernate-validator-resteasy-reactive/src/test/java/io/quarkus/it/hibernate/validator/TextResourceTest.java new file mode 100644 index 0000000000000..b7fc426b18367 --- /dev/null +++ b/integration-tests/hibernate-validator-resteasy-reactive/src/test/java/io/quarkus/it/hibernate/validator/TextResourceTest.java @@ -0,0 +1,28 @@ +package io.quarkus.it.hibernate.validator; + +import static io.restassured.RestAssured.when; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; + +@QuarkusTest +public class TextResourceTest { + + @Test + public void fetchDefault() { + when().get("/text/validate/boom") + .then() + .statusCode(400) + .contentType(ContentType.TEXT); + } + + @Test + public void fetchText() { + when().get("/text/validate/boom") + .then() + .statusCode(400) + .contentType(ContentType.TEXT); + } +} From 92f8be353ab68cfaadd79ca56dc33c729e6c816b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Mon, 3 Oct 2022 12:11:56 +0200 Subject: [PATCH 73/81] Docs & Tests - migrate SSL certificate files property from deprecated one (cherry picked from commit bee6f4824aa74b0c0d0627465ccb2034c327b87c) --- docs/src/main/asciidoc/http-reference.adoc | 4 ++-- .../deployment/src/test/resources/conf/disable-http.conf | 4 ++-- .../src/main/resources/application.properties | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/main/asciidoc/http-reference.adoc b/docs/src/main/asciidoc/http-reference.adoc index 0ecb30bb527e2..3633a29e46301 100644 --- a/docs/src/main/asciidoc/http-reference.adoc +++ b/docs/src/main/asciidoc/http-reference.adoc @@ -127,8 +127,8 @@ Your `application.properties` would then look like this: [source,properties] ---- -quarkus.http.ssl.certificate.file=/path/to/certificate -quarkus.http.ssl.certificate.key-file=/path/to/key +quarkus.http.ssl.certificate.files=/path/to/certificate +quarkus.http.ssl.certificate.key-files=/path/to/key ---- === Providing a keystore diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/disable-http.conf b/extensions/vertx-http/deployment/src/test/resources/conf/disable-http.conf index f60ba91d3b3aa..580909ea73575 100644 --- a/extensions/vertx-http/deployment/src/test/resources/conf/disable-http.conf +++ b/extensions/vertx-http/deployment/src/test/resources/conf/disable-http.conf @@ -1,4 +1,4 @@ # Enable SSL, configure the key store quarkus.http.insecure-requests=REDIRECT -quarkus.http.ssl.certificate.file=server-cert.pem -quarkus.http.ssl.certificate.key-file=server-key.pem +quarkus.http.ssl.certificate.files=server-cert.pem +quarkus.http.ssl.certificate.key-files=server-key.pem diff --git a/integration-tests/elytron-undertow/src/main/resources/application.properties b/integration-tests/elytron-undertow/src/main/resources/application.properties index 5ea2086daa428..6ef33186e3884 100644 --- a/integration-tests/elytron-undertow/src/main/resources/application.properties +++ b/integration-tests/elytron-undertow/src/main/resources/application.properties @@ -9,8 +9,8 @@ quarkus.security.users.embedded.users.poul=poul quarkus.security.users.embedded.roles.poul=interns quarkus.security.users.embedded.plain-text=true quarkus.servlet.context-path=/foo -quarkus.http.ssl.certificate.file=target/server-cert.pem -quarkus.http.ssl.certificate.key-file=target/server-key.pem +quarkus.http.ssl.certificate.files=target/server-cert.pem +quarkus.http.ssl.certificate.key-files=target/server-key.pem # Test that server starts with this option # See https://github.com/quarkusio/quarkus/issues/8336 quarkus.http.insecure-requests=disabled \ No newline at end of file From 52896eef5d8258180a9146111189c4b96cd944fc Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Mon, 3 Oct 2022 09:34:36 +0200 Subject: [PATCH 74/81] Update to the Vert.x Mutiny bindings 2.27.0 (cherry picked from commit 2662b0b73b7be31f29849e658e2a2df47f6759ca) --- 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 70099e2b8f2c0..1768f53631851 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -51,7 +51,7 @@ 1.2.2 1.0.13 2.7.0 - 2.26.0 + 2.27.0 3.20.0 1.1.2 1.2.1 From e3639143fbe8eaa86ce15ef7c438fc3d2b64d0be Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 27 Sep 2022 12:20:54 +0300 Subject: [PATCH 75/81] Refactor: Use `io.quarkus.gizmo.BytecodeCreator#loadClassFromTCCL` Use `loadClassFromTCCL` instead of manually getting the TCCL and loading classes in `NativeImageFeatureStep` (cherry picked from commit 02f206b2785c4a85484b0b4d5090409f7d0fbb98) --- .../steps/NativeImageFeatureStep.java | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java index 01786ec5bca51..98cbfc0c6ee52 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java @@ -89,8 +89,6 @@ public class NativeImageFeatureStep { private static final MethodDescriptor LOOKUP_METHOD = ofMethod( ReflectionUtil.class, "lookupMethod", Method.class, Class.class, String.class, Class[].class); - private static final MethodDescriptor FOR_NAME = ofMethod( - Class.class, "forName", Class.class, String.class, boolean.class, ClassLoader.class); private static final MethodDescriptor INVOKE = ofMethod( Method.class, "invoke", Object.class, Object.class, Object[].class); static final String RUNTIME_REFLECTION = RuntimeReflection.class.getName(); @@ -468,14 +466,7 @@ public void write(String s, byte[] bytes) { TryBlock tc = mv.tryBlock(); - ResultHandle currentThread = tc - .invokeStaticMethod(ofMethod(Thread.class, "currentThread", Thread.class)); - ResultHandle tccl = tc.invokeVirtualMethod( - ofMethod(Thread.class, "getContextClassLoader", ClassLoader.class), - currentThread); - ResultHandle clazz = tc.invokeStaticMethod( - ofMethod(Class.class, "forName", Class.class, String.class, boolean.class, ClassLoader.class), - tc.load(entry.getKey()), tc.load(false), tccl); + ResultHandle clazz = tc.loadClassFromTCCL(entry.getKey()); //we call these methods first, so if they are going to throw an exception it happens before anything has been registered ResultHandle constructors = tc .invokeVirtualMethod(ofMethod(Class.class, "getDeclaredConstructors", Constructor[].class), clazz); @@ -575,14 +566,7 @@ public void write(String s, byte[] bytes) { TryBlock tc = mv.tryBlock(); - ResultHandle currentThread = tc - .invokeStaticMethod(ofMethod(Thread.class, "currentThread", Thread.class)); - ResultHandle tccl = tc.invokeVirtualMethod( - ofMethod(Thread.class, "getContextClassLoader", ClassLoader.class), - currentThread); - ResultHandle clazz = tc.invokeStaticMethod( - ofMethod(Class.class, "forName", Class.class, String.class, boolean.class, ClassLoader.class), - tc.load(className), tc.load(false), tccl); + ResultHandle clazz = tc.loadClassFromTCCL(className); //we call these methods first, so if they are going to throw an exception it happens before anything has been registered ResultHandle constructors = tc .invokeVirtualMethod(ofMethod(Class.class, "getDeclaredConstructors", Constructor[].class), clazz); @@ -666,15 +650,7 @@ private MethodDescriptor createRegisterSerializationForClassMethod(ClassCreator TryBlock tc = addSerializationForClass.tryBlock(); - ResultHandle currentThread = tc - .invokeStaticMethod(ofMethod(Thread.class, "currentThread", Thread.class)); - ResultHandle tccl = tc.invokeVirtualMethod( - ofMethod(Thread.class, "getContextClassLoader", ClassLoader.class), - currentThread); - - ResultHandle runtimeSerializationClass = tc.invokeStaticMethod(FOR_NAME, - tc.load("org.graalvm.nativeimage.hosted.RuntimeSerialization"), - tc.load(false), tccl); + ResultHandle runtimeSerializationClass = tc.loadClassFromTCCL("org.graalvm.nativeimage.hosted.RuntimeSerialization"); ResultHandle registerArgTypes = tc.newArray(Class.class, tc.load(1)); tc.writeArrayValue(registerArgTypes, 0, tc.loadClassFromTCCL(Class[].class)); ResultHandle registerLookupMethod = tc.invokeStaticMethod(LOOKUP_METHOD, runtimeSerializationClass, From c39ea8479c29168a14ae8d9d984abf768cf1de8e Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 20 Sep 2022 14:42:04 +0300 Subject: [PATCH 76/81] Use public initializeAtBuildTime and initializeAtRunTime methods Stops using the internal methods from the `org.graalvm.nativeimage.impl` package. (cherry picked from commit dbecbd4c6c2d566dfb8245134a3808ab568c78e8) --- .../steps/NativeImageFeatureStep.java | 18 +++++++--------- .../quarkus/awt/runtime/graal/AwtFeature.java | 21 +++++++++---------- .../awt/runtime/graal/DarwinAwtFeature.java | 9 +++----- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java index 98cbfc0c6ee52..e6143b85e9877 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java @@ -64,15 +64,15 @@ public class NativeImageFeatureStep { private static final MethodDescriptor IMAGE_SINGLETONS_LOOKUP = ofMethod(ImageSingletons.class, "lookup", Object.class, Class.class); - private static final MethodDescriptor BUILD_TIME_INITIALIZATION = ofMethod( - "org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport", - "initializeAtBuildTime", void.class, String.class, String.class); + private static final MethodDescriptor BUILD_TIME_INITIALIZATION = ofMethod(RuntimeClassInitialization.class, + "initializeAtBuildTime", void.class, String[].class); private static final MethodDescriptor INITIALIZE_CLASSES_AT_RUN_TIME = ofMethod(RuntimeClassInitialization.class, "initializeAtRunTime", void.class, Class[].class); private static final MethodDescriptor INITIALIZE_PACKAGES_AT_RUN_TIME = ofMethod(RuntimeClassInitialization.class, "initializeAtRunTime", void.class, String[].class); + public static final String RUNTIME_CLASS_INITIALIZATION_SUPPORT = "org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport"; private static final MethodDescriptor RERUN_INITIALIZATION = ofMethod( - "org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport", + RUNTIME_CLASS_INITIALIZATION_SUPPORT, "rerunInitialization", void.class, Class.class, String.class); public static final String CONFIGURATION_CONDITION = "org.graalvm.nativeimage.impl.ConfigurationCondition"; @@ -205,12 +205,8 @@ public void write(String s, byte[] bytes) { cc.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), cc.getCaughtException()); } - ResultHandle imageSingleton = overallCatch.invokeStaticMethod(IMAGE_SINGLETONS_LOOKUP, - overallCatch.loadClassFromTCCL("org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport")); - overallCatch.invokeInterfaceMethod(BUILD_TIME_INITIALIZATION, - imageSingleton, - overallCatch.load(""), // empty string means everything - overallCatch.load("Quarkus build time init default")); + overallCatch.invokeStaticMethod(BUILD_TIME_INITIALIZATION, + overallCatch.marshalAsArray(String.class, overallCatch.load(""))); // empty string means initialize everything if (!runtimeInitializedClassBuildItems.isEmpty()) { ResultHandle thisClass = overallCatch.loadClassFromTCCL(GRAAL_FEATURE); @@ -249,6 +245,8 @@ public void write(String s, byte[] bytes) { ResultHandle cl = overallCatch.invokeVirtualMethod(ofMethod(Class.class, "getClassLoader", ClassLoader.class), thisClass); ResultHandle quarkus = overallCatch.load("Quarkus"); + ResultHandle imageSingleton = overallCatch.invokeStaticMethod(IMAGE_SINGLETONS_LOOKUP, + overallCatch.loadClassFromTCCL(RUNTIME_CLASS_INITIALIZATION_SUPPORT)); for (RuntimeReinitializedClassBuildItem runtimeReinitializedClass : runtimeReinitializedClassBuildItems) { TryBlock tc = overallCatch.tryBlock(); ResultHandle clazz = tc.invokeStaticMethod( diff --git a/extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/graal/AwtFeature.java b/extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/graal/AwtFeature.java index 17e709d5bdf43..34b8606751dca 100644 --- a/extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/graal/AwtFeature.java +++ b/extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/graal/AwtFeature.java @@ -1,23 +1,22 @@ package io.quarkus.awt.runtime.graal; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; +import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; /** - * Note that this initialization s not enough if user wants to deserialize actual images + * Note that this initialization is not enough if user wants to deserialize actual images * (e.g. from XML). AWT Extension must be loaded for decoding JDK supported image formats. */ public class AwtFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess access) { - final RuntimeClassInitializationSupport runtimeInit = ImageSingletons.lookup(RuntimeClassInitializationSupport.class); - final String reason = "Quarkus run time init for AWT"; - runtimeInit.initializeAtRunTime("com.sun.imageio", reason); - runtimeInit.initializeAtRunTime("java.awt", reason); - runtimeInit.initializeAtRunTime("javax.imageio", reason); - runtimeInit.initializeAtRunTime("sun.awt", reason); - runtimeInit.initializeAtRunTime("sun.font", reason); - runtimeInit.initializeAtRunTime("sun.java2d", reason); + // Quarkus run time init for AWT + RuntimeClassInitialization.initializeAtRunTime( + "com.sun.imageio", + "java.awt", + "javax.imageio", + "sun.awt", + "sun.font", + "sun.java2d"); } } diff --git a/extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/graal/DarwinAwtFeature.java b/extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/graal/DarwinAwtFeature.java index d17c0a8ce6a88..99f32efd5ebbd 100644 --- a/extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/graal/DarwinAwtFeature.java +++ b/extensions/awt/runtime/src/main/java/io/quarkus/awt/runtime/graal/DarwinAwtFeature.java @@ -1,10 +1,9 @@ package io.quarkus.awt.runtime.graal; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; +import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; import io.quarkus.runtime.util.JavaVersionUtil; @@ -13,10 +12,8 @@ public class DarwinAwtFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess access) { if (JavaVersionUtil.isJava17OrHigher()) { - final RuntimeClassInitializationSupport runtimeInit = ImageSingletons - .lookup(RuntimeClassInitializationSupport.class); - final String reason = "Quarkus run time init for AWT in Darwin"; - runtimeInit.initializeAtRunTime("sun.lwawt.macosx", reason); + // Quarkus run time init for AWT in Darwin + RuntimeClassInitialization.initializeAtRunTime("sun.lwawt.macosx"); } } } From 67154c771e3b1998d6a22371dba78ba6eb98ad2f Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 20 Sep 2022 15:05:00 +0300 Subject: [PATCH 77/81] Use `RuntimeSerialization` to register lambda capturing classes Removes use of internal API (cherry picked from commit e8de32f1b201171dfdaea1dd2176044d2de0260b) --- .../steps/NativeImageFeatureStep.java | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java index e6143b85e9877..5ef2a4e24f4b1 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java @@ -102,6 +102,7 @@ public class NativeImageFeatureStep { public static final MethodDescriptor WEAK_REFLECTION_REGISTRATION = MethodDescriptor.ofMethod(WeakReflection.class, "register", void.class, Feature.BeforeAnalysisAccess.class, Class.class, boolean.class, boolean.class, boolean.class); + public static final String RUNTIME_SERIALIZATION = "org.graalvm.nativeimage.hosted.RuntimeSerialization"; @BuildStep GeneratedResourceBuildItem generateNativeResourcesList(List resources, @@ -167,20 +168,43 @@ public void write(String s, byte[] bytes) { MethodCreator duringSetup = file.getMethodCreator("duringSetup", "V", DURING_SETUP_ACCESS); // Register Lambda Capturing Types if (!lambdaCapturingTypeBuildItems.isEmpty()) { - ResultHandle runtimeSerializationSupportSingleton = duringSetup.invokeStaticMethod(IMAGE_SINGLETONS_LOOKUP, - duringSetup.loadClassFromTCCL("org.graalvm.nativeimage.impl.RuntimeSerializationSupport")); - ResultHandle configAlwaysTrue = duringSetup.invokeStaticMethod(CONFIGURATION_ALWAYS_TRUE); - for (LambdaCapturingTypeBuildItem i : lambdaCapturingTypeBuildItems) { - TryBlock tryBlock = duringSetup.tryBlock(); + BranchResult graalVm22_3Test = duringSetup + .ifGreaterEqualZero(duringSetup.invokeVirtualMethod(VERSION_COMPARE_TO, + duringSetup.invokeStaticMethod(VERSION_CURRENT), + duringSetup.marshalAsArray(int.class, duringSetup.load(22), duringSetup.load(3)))); + /* GraalVM >= 22.3 */ + try (BytecodeCreator greaterThan22_2 = graalVm22_3Test.trueBranch()) { + MethodDescriptor registerLambdaCapturingClass = ofMethod(RUNTIME_SERIALIZATION, "registerLambdaCapturingClass", + void.class, Class.class); + for (LambdaCapturingTypeBuildItem i : lambdaCapturingTypeBuildItems) { + TryBlock tryBlock = greaterThan22_2.tryBlock(); + + tryBlock.invokeStaticMethod(registerLambdaCapturingClass, + tryBlock.load(i.getClassName())); + + CatchBlockCreator catchBlock = tryBlock.addCatch(Throwable.class); + catchBlock.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), + catchBlock.getCaughtException()); + } + } + /* GraalVM < 22.3 */ + try (BytecodeCreator smallerThan22_3 = graalVm22_3Test.falseBranch()) { + ResultHandle runtimeSerializationSupportSingleton = smallerThan22_3.invokeStaticMethod(IMAGE_SINGLETONS_LOOKUP, + smallerThan22_3.loadClassFromTCCL("org.graalvm.nativeimage.impl.RuntimeSerializationSupport")); + ResultHandle configAlwaysTrue = smallerThan22_3.invokeStaticMethod(CONFIGURATION_ALWAYS_TRUE); - tryBlock.invokeInterfaceMethod(REGISTER_LAMBDA_CAPTURING_CLASS, runtimeSerializationSupportSingleton, - configAlwaysTrue, - tryBlock.load(i.getClassName())); + for (LambdaCapturingTypeBuildItem i : lambdaCapturingTypeBuildItems) { + TryBlock tryBlock = smallerThan22_3.tryBlock(); - CatchBlockCreator catchBlock = tryBlock.addCatch(Throwable.class); - catchBlock.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), - catchBlock.getCaughtException()); + tryBlock.invokeInterfaceMethod(REGISTER_LAMBDA_CAPTURING_CLASS, runtimeSerializationSupportSingleton, + configAlwaysTrue, + tryBlock.load(i.getClassName())); + + CatchBlockCreator catchBlock = tryBlock.addCatch(Throwable.class); + catchBlock.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), + catchBlock.getCaughtException()); + } } } duringSetup.returnValue(null); @@ -648,7 +672,7 @@ private MethodDescriptor createRegisterSerializationForClassMethod(ClassCreator TryBlock tc = addSerializationForClass.tryBlock(); - ResultHandle runtimeSerializationClass = tc.loadClassFromTCCL("org.graalvm.nativeimage.hosted.RuntimeSerialization"); + ResultHandle runtimeSerializationClass = tc.loadClassFromTCCL(RUNTIME_SERIALIZATION); ResultHandle registerArgTypes = tc.newArray(Class.class, tc.load(1)); tc.writeArrayValue(registerArgTypes, 0, tc.loadClassFromTCCL(Class[].class)); ResultHandle registerLookupMethod = tc.invokeStaticMethod(LOOKUP_METHOD, runtimeSerializationClass, From 9643aea148c6ae1fc1111d99b6408ae80da24b58 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 20 Sep 2022 15:43:52 +0300 Subject: [PATCH 78/81] Use `RuntimeResourceAccess` to add resource bundles Since `RuntimeResourceAccess#addResourceBundle` requires a module to be passed to it we pass the unnamed module by default and extend `NativeImageResourceBundleBuildItem` to include a module name for specifying one when needed, e.g., for JDK internal resources. (cherry picked from commit a5c2014aa443578d3e18ce26a5416f817575f6ad) --- .../NativeImageResourceBundleBuildItem.java | 11 +++++ .../deployment/steps/LocaleProcessor.java | 4 +- .../steps/NativeImageFeatureStep.java | 40 +++++++++---------- .../deployment/steps/ResourceBundleStep.java | 2 +- ...Util.java => NativeImageFeatureUtils.java} | 9 ++++- 5 files changed, 42 insertions(+), 24 deletions(-) rename core/runtime/src/main/java/io/quarkus/runtime/{ReflectionUtil.java => NativeImageFeatureUtils.java} (55%) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceBundleBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceBundleBuildItem.java index d486ee0f7dd19..520e81ea8c051 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceBundleBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceBundleBuildItem.java @@ -8,12 +8,23 @@ public final class NativeImageResourceBundleBuildItem extends MultiBuildItem { private final String bundleName; + private final String moduleName; public NativeImageResourceBundleBuildItem(String bundleName) { this.bundleName = bundleName; + this.moduleName = null; + } + + public NativeImageResourceBundleBuildItem(String bundleName, String moduleName) { + this.bundleName = bundleName; + this.moduleName = moduleName; } public String getBundleName() { return bundleName; } + + public String getModuleName() { + return moduleName; + } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/LocaleProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/LocaleProcessor.java index f9ed878359e32..d616d35e424b6 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/LocaleProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/LocaleProcessor.java @@ -36,8 +36,8 @@ public class LocaleProcessor { @BuildStep(onlyIf = { NativeBuild.class, NonDefaultLocale.class }) void nativeResources(BuildProducer resources) { - resources.produce(new NativeImageResourceBundleBuildItem("sun.util.resources.LocaleNames")); - resources.produce(new NativeImageResourceBundleBuildItem("sun.util.resources.CurrencyNames")); + resources.produce(new NativeImageResourceBundleBuildItem("sun.util.resources.LocaleNames", "java.base")); + resources.produce(new NativeImageResourceBundleBuildItem("sun.util.resources.CurrencyNames", "java.base")); //Adding sun.util.resources.TimeZoneNames is not necessary. } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java index 5ef2a4e24f4b1..d6589e21d8357 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java @@ -51,7 +51,7 @@ import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; import io.quarkus.gizmo.TryBlock; -import io.quarkus.runtime.ReflectionUtil; +import io.quarkus.runtime.NativeImageFeatureUtils; import io.quarkus.runtime.ResourceHelper; import io.quarkus.runtime.graal.ResourcesFeature; import io.quarkus.runtime.graal.WeakReflection; @@ -87,8 +87,12 @@ public class NativeImageFeatureStep { String.class); private static final MethodDescriptor LOOKUP_METHOD = ofMethod( - ReflectionUtil.class, + NativeImageFeatureUtils.class, "lookupMethod", Method.class, Class.class, String.class, Class[].class); + + private static final MethodDescriptor FIND_MODULE_METHOD = ofMethod( + NativeImageFeatureUtils.class, + "findModule", Module.class, String.class); private static final MethodDescriptor INVOKE = ofMethod( Method.class, "invoke", Object.class, Object.class, Object[].class); static final String RUNTIME_REFLECTION = RuntimeReflection.class.getName(); @@ -405,26 +409,22 @@ public void write(String s, byte[] bytes) { /* GraalVM >= 22.3 */ try (BytecodeCreator greaterThan22_2 = graalVm22_3Test.trueBranch()) { - ResultHandle runtimeResourceSupportClass = greaterThan22_2.loadClassFromTCCL(RUNTIME_RESOURCE_SUPPORT); - ResultHandle addResourceBundlesParams = greaterThan22_2.marshalAsArray(Class.class, - greaterThan22_2.loadClassFromTCCL(CONFIGURATION_CONDITION), - greaterThan22_2.loadClassFromTCCL(String.class)); - ResultHandle addResourceBundlesMethod = greaterThan22_2.invokeStaticMethod( - LOOKUP_METHOD, - runtimeResourceSupportClass, greaterThan22_2.load("addResourceBundles"), addResourceBundlesParams); - ResultHandle runtimeResourceSupport = greaterThan22_2.invokeStaticMethod( - IMAGE_SINGLETONS_LOOKUP, - runtimeResourceSupportClass); - ResultHandle configAlwaysTrue = greaterThan22_2.invokeStaticMethod(CONFIGURATION_ALWAYS_TRUE); + MethodDescriptor addResourceBundle = ofMethod("org.graalvm.nativeimage.hosted.RuntimeResourceAccess", + "addResourceBundle", void.class, Module.class, String.class); for (NativeImageResourceBundleBuildItem i : resourceBundles) { - TryBlock et = greaterThan22_2.tryBlock(); - - et.invokeVirtualMethod( - INVOKE, - addResourceBundlesMethod, runtimeResourceSupport, - et.marshalAsArray(Object.class, configAlwaysTrue, et.load(i.getBundleName()))); - CatchBlockCreator c = et.addCatch(Throwable.class); + TryBlock tc = greaterThan22_2.tryBlock(); + + String moduleName = i.getModuleName(); + ResultHandle moduleNameHandle; + if (moduleName == null) { + moduleNameHandle = tc.loadNull(); + } else { + moduleNameHandle = tc.load(moduleName); + } + ResultHandle module = tc.invokeStaticMethod(FIND_MODULE_METHOD, moduleNameHandle); + tc.invokeStaticMethod(addResourceBundle, module, tc.load(i.getBundleName())); + CatchBlockCreator c = tc.addCatch(Throwable.class); //c.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), c.getCaughtException()); } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ResourceBundleStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ResourceBundleStep.java index f2cfba72a2ce0..5f959fad51bac 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ResourceBundleStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ResourceBundleStep.java @@ -12,6 +12,6 @@ public NativeImageResourceBundleBuildItem nativeImageResourceBundle() { * This might no longer be required if GraalVM auto-includes it in a future release. * See https://github.com/oracle/graal/issues/2005 for more details about it. */ - return new NativeImageResourceBundleBuildItem("sun.security.util.Resources"); + return new NativeImageResourceBundleBuildItem("sun.security.util.Resources", "java.base"); } } diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ReflectionUtil.java b/core/runtime/src/main/java/io/quarkus/runtime/NativeImageFeatureUtils.java similarity index 55% rename from core/runtime/src/main/java/io/quarkus/runtime/ReflectionUtil.java rename to core/runtime/src/main/java/io/quarkus/runtime/NativeImageFeatureUtils.java index face04fad7ed8..cdecbd71dcedf 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ReflectionUtil.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/NativeImageFeatureUtils.java @@ -2,7 +2,7 @@ import java.lang.reflect.Method; -public class ReflectionUtil { +public class NativeImageFeatureUtils { public static Method lookupMethod(Class declaringClass, String methodName, Class... parameterTypes) throws NoSuchMethodException { @@ -10,4 +10,11 @@ public static Method lookupMethod(Class declaringClass, String methodName, Cl result.setAccessible(true); return result; } + + public static Module findModule(String moduleName) { + if (moduleName == null) { + return ClassLoader.getSystemClassLoader().getUnnamedModule(); + } + return ModuleLayer.boot().findModule(moduleName).orElseThrow(); + } } From e5750b6c817ad24d84bf22ac7816c5528f4e4818 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Thu, 29 Sep 2022 19:42:41 +0300 Subject: [PATCH 79/81] Fix class cast exception with GraalVM 22.3 Fixes issues introduced in #28093 (cherry picked from commit ea67abe114042f9127734eee9cdfb9fd9e87ebb1) --- .../io/quarkus/deployment/steps/NativeImageFeatureStep.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java index d6589e21d8357..322bf30b8e761 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java @@ -185,7 +185,7 @@ public void write(String s, byte[] bytes) { TryBlock tryBlock = greaterThan22_2.tryBlock(); tryBlock.invokeStaticMethod(registerLambdaCapturingClass, - tryBlock.load(i.getClassName())); + tryBlock.loadClassFromTCCL(i.getClassName())); CatchBlockCreator catchBlock = tryBlock.addCatch(Throwable.class); catchBlock.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), From 77c96275880965b8aa75687a3f45b02874db19d3 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Thu, 29 Sep 2022 19:45:38 +0300 Subject: [PATCH 80/81] Run Version comparison only once in dynamic proxy registration (cherry picked from commit 1235d48d4dfe2d2ffd4f261f810cac7aa3e0d7a9) --- .../quarkus/deployment/steps/NativeImageFeatureStep.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java index 322bf30b8e761..7a4c59c54b82d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java @@ -292,6 +292,10 @@ public void write(String s, byte[] bytes) { exports.produce(new JPMSExportBuildItem("org.graalvm.nativeimage.builder", "com.oracle.svm.core.jdk.proxy", GraalVM.Version.VERSION_22_1_0, GraalVM.Version.VERSION_22_3_0)); + ResultHandle versionCompareto22_3Result = overallCatch.invokeVirtualMethod(VERSION_COMPARE_TO, + overallCatch.invokeStaticMethod(VERSION_CURRENT), + overallCatch.marshalAsArray(int.class, overallCatch.load(22), overallCatch.load(3))); + for (NativeImageProxyDefinitionBuildItem proxy : proxies) { ResultHandle array = overallCatch.newArray(Class.class, overallCatch.load(proxy.getClasses().size())); int i = 0; @@ -302,10 +306,7 @@ public void write(String s, byte[] bytes) { } - BranchResult graalVm22_3Test = overallCatch - .ifGreaterEqualZero(overallCatch.invokeVirtualMethod(VERSION_COMPARE_TO, - overallCatch.invokeStaticMethod(VERSION_CURRENT), - overallCatch.marshalAsArray(int.class, overallCatch.load(22), overallCatch.load(3)))); + BranchResult graalVm22_3Test = overallCatch.ifGreaterEqualZero(versionCompareto22_3Result); /* GraalVM >= 22.3 */ try (BytecodeCreator greaterThan22_2 = graalVm22_3Test.trueBranch()) { MethodDescriptor registerMethod = ofMethod("org.graalvm.nativeimage.hosted.RuntimeProxyCreation", From 5d6b3a356805d03b1fae9c577480219d9d1b3457 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Fri, 30 Sep 2022 11:58:34 +0300 Subject: [PATCH 81/81] Specify the module name when registering jaxp resource bundles (cherry picked from commit 4beeaf3099647a78a3f0c93049939eaa044699d0) --- .../main/java/io/quarkus/jaxp/deployment/JaxpProcessor.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/jaxp/deployment/src/main/java/io/quarkus/jaxp/deployment/JaxpProcessor.java b/extensions/jaxp/deployment/src/main/java/io/quarkus/jaxp/deployment/JaxpProcessor.java index 152b93015fef6..883b1e8ac0c3b 100644 --- a/extensions/jaxp/deployment/src/main/java/io/quarkus/jaxp/deployment/JaxpProcessor.java +++ b/extensions/jaxp/deployment/src/main/java/io/quarkus/jaxp/deployment/JaxpProcessor.java @@ -1,5 +1,6 @@ package io.quarkus.jaxp.deployment; +import java.util.function.Consumer; import java.util.stream.Stream; import io.quarkus.deployment.annotations.BuildProducer; @@ -30,6 +31,8 @@ void reflectiveClasses(BuildProducer reflectiveClass) @BuildStep void resourceBundles(BuildProducer resourceBundle) { + Consumer resourceBundleItemProducer = bundleName -> resourceBundle + .produce(new NativeImageResourceBundleBuildItem(bundleName, "java.xml")); Stream.of( "com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages", "com.sun.org.apache.xml.internal.res.XMLErrorResources", @@ -37,8 +40,7 @@ void resourceBundles(BuildProducer resourceB "com.sun.org.apache.xerces.internal.impl.msg.XMLMessages", "com.sun.org.apache.xerces.internal.impl.msg.XMLSchemaMessages", "com.sun.org.apache.xerces.internal.impl.xpath.regex.message") - .map(NativeImageResourceBundleBuildItem::new) - .forEach(resourceBundle::produce); + .forEach(resourceBundleItemProducer); } @BuildStep