diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 3c37f3131f632..d699b3a03fe18 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -54,7 +54,7 @@ 4.0.4 4.0.0 3.7.0 - 2.6.0 + 2.6.1 6.2.6 4.4.0 2.1.0 @@ -84,7 +84,7 @@ 4.0.1 4.0.4 9.6 - 2.15.0 + 2.15.1 16.0.0.Final 3.0-alpha-2 2.1.0 @@ -136,7 +136,7 @@ 1.2.6 5.3.2 2.2 - 5.10.0 + 5.10.1 1.5.0 14.0.21.Final 4.6.5.Final @@ -146,7 +146,7 @@ 1.0.4 3.5.3.Final 2.5.1 - 3.6.0 + 3.6.1 1.8.0 1.1.10.5 0.100.0 @@ -160,7 +160,7 @@ 1.9.21 1.7.3 0.27.0 - 1.6.0 + 1.6.2 4.0.3 3.2.0 4.2.0 @@ -188,7 +188,7 @@ 5.8.0 4.10.1 2.0.2.Final - 22.0.5 + 23.0.0 1.15.1 3.38.0 2.22.0 @@ -197,10 +197,10 @@ 2.1 4.7.5 1.1.0 - 1.24.0 + 1.25.0 1.11.0 2.10.1 - 1.1.1.Final + 1.1.2.Final 2.20.0 1.3.0.Final 1.11.3 @@ -2207,6 +2207,11 @@ quarkus-funqy-amazon-lambda ${project.version} + + io.quarkus + quarkus-funqy-amazon-lambda-deployment + ${project.version} + io.quarkus quarkus-funqy-google-cloud-functions diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 14eeb01c05c7a..ae8c28c5ea334 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -101,7 +101,7 @@ - 22.0.5 + 23.0.0 19.0.3 quay.io/keycloak/keycloak:${keycloak.version} quay.io/keycloak/keycloak:${keycloak.wildfly.version}-legacy diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java index 6a28d740a1713..41d698b81a3d1 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigDiagnostic.java @@ -184,7 +184,7 @@ public static Set configFiles(Path configFilesLocation) throws IOExcepti Set configFiles = new HashSet<>(); try (DirectoryStream candidates = Files.newDirectoryStream(configFilesLocation, CONFIG_FILES_FILTER)) { for (Path candidate : candidates) { - configFiles.add(candidate.toString()); + configFiles.add(candidate.toUri().getPath()); } } return configFiles; diff --git a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsAdd.java b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsAdd.java index 3b1266ecdae52..4d3e1572d771c 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsAdd.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsAdd.java @@ -17,7 +17,7 @@ public class ProjectExtensionsAdd extends BaseBuildCommand implements Callable extensions; @Override diff --git a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsRemove.java b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsRemove.java index adf82223bccfc..2affcc921bf42 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsRemove.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/ProjectExtensionsRemove.java @@ -17,7 +17,7 @@ public class ProjectExtensionsRemove extends BaseBuildCommand implements Callabl @CommandLine.Mixin RunModeOption runMode; - @CommandLine.Parameters(arity = "1", paramLabel = "EXTENSION", description = "Extension(s) to remove from this project.") + @CommandLine.Parameters(arity = "1", paramLabel = "EXTENSION", description = "Extension(s) to remove from this project.", split = ",") Set extensions; @Override 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 6bbbc63a26463..60af7b5aabb94 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/CliDriver.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/CliDriver.java @@ -319,7 +319,7 @@ public static Result invokeExtensionRemoveQute(Path projectRoot, Path file) thro } public static Result invokeExtensionAddMultiple(Path projectRoot, Path file) throws Exception { - // add the qute extension + // add amazon-lambda-http and jackson extensions Result result = execute(projectRoot, "extension", "add", "amazon-lambda-http", "jackson", "-e", "-B", "--verbose"); Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, "Expected OK return code. Result:\n" + result); @@ -345,7 +345,7 @@ public static Result invokeExtensionAddMultiple(Path projectRoot, Path file) thr } public static Result invokeExtensionRemoveMultiple(Path projectRoot, Path file) throws Exception { - // add the qute extension + // remove amazon-lambda-http and jackson extensions Result result = execute(projectRoot, "extension", "remove", "amazon-lambda-http", "jackson", "-e", "-B", "--verbose"); Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, "Expected OK return code. Result:\n" + result); @@ -370,6 +370,52 @@ public static Result invokeExtensionRemoveMultiple(Path projectRoot, Path file) return result; } + public static Result invokeExtensionAddMultipleCommas(Path projectRoot, Path file) throws Exception { + Result result = execute(projectRoot, "extension", "add", + "quarkus-resteasy-reactive-jsonb,quarkus-resteasy-reactive-jackson", "-e", "-B", "--verbose"); + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, + "Expected OK return code. Result:\n" + result); + + result = invokeValidateExtensionList(projectRoot); + Assertions.assertTrue(result.stdout.contains("quarkus-qute"), + "Expected quarkus-qute to be in the list of extensions. Result:\n" + result); + Assertions.assertTrue(result.stdout.contains("quarkus-resteasy-reactive-jsonb"), + "Expected quarkus-resteasy-reactive-jsonb to be in the list of extensions. Result:\n" + result); + Assertions.assertTrue(result.stdout.contains("quarkus-resteasy-reactive-jackson"), + "Expected quarkus-resteasy-reactive-jackson to be in the list of extensions. Result:\n" + result); + + String content = CliDriver.readFileAsString(file); + Assertions.assertTrue(content.contains("quarkus-qute"), + "quarkus-qute should still be listed as a dependency. Result:\n" + content); + Assertions.assertTrue(content.contains("quarkus-resteasy-reactive-jsonb"), + "quarkus-resteasy-reactive-jsonb should be listed as a dependency. Result:\n" + content); + Assertions.assertTrue(content.contains("quarkus-resteasy-reactive-jackson"), + "quarkus-resteasy-reactive-jackson should be listed as a dependency. Result:\n" + content); + + return result; + } + + public static Result invokeExtensionRemoveMultipleCommas(Path projectRoot, Path file) throws Exception { + Result result = execute(projectRoot, "extension", "remove", + "quarkus-resteasy-reactive-jsonb,quarkus-resteasy-reactive-jackson", "-e", "-B", "--verbose"); + Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, + "Expected OK return code. Result:\n" + result); + + result = invokeValidateExtensionList(projectRoot); + Assertions.assertFalse(result.stdout.contains("quarkus-resteasy-reactive-jsonb"), + "quarkus-resteasy-reactive-jsonb should not be in the list of extensions. Result:\n" + result); + Assertions.assertFalse(result.stdout.contains("quarkus-resteasy-reactive-jackson"), + "quarkus-resteasy-reactive-jackson should not be in the list of extensions. Result:\n" + result); + + String content = CliDriver.readFileAsString(file); + Assertions.assertFalse(content.contains("quarkus-resteasy-reactive-jsonb"), + "quarkus-resteasy-reactive-jsonb should not be listed as a dependency. Result:\n" + content); + Assertions.assertFalse(content.contains("quarkus-resteasy-reactive-jackson"), + "quarkus-resteasy-reactive-jackson should not be listed as a dependency. Result:\n" + content); + + return result; + } + public static Result invokeExtensionListInstallable(Path projectRoot) throws Exception { Result result = CliDriver.execute(projectRoot, "extension", "list", "-e", "-B", "--verbose", "-i"); Assertions.assertEquals(CommandLine.ExitCode.OK, result.exitCode, diff --git a/devtools/cli/src/test/java/io/quarkus/cli/CliProjectMavenTest.java b/devtools/cli/src/test/java/io/quarkus/cli/CliProjectMavenTest.java index d8782c4f1b0d2..4653a21a73723 100644 --- a/devtools/cli/src/test/java/io/quarkus/cli/CliProjectMavenTest.java +++ b/devtools/cli/src/test/java/io/quarkus/cli/CliProjectMavenTest.java @@ -145,8 +145,10 @@ public void testExtensionList() throws Exception { CliDriver.invokeExtensionAddRedundantQute(project); CliDriver.invokeExtensionListInstallable(project); CliDriver.invokeExtensionAddMultiple(project, pom); + CliDriver.invokeExtensionAddMultipleCommas(project, pom); CliDriver.invokeExtensionRemoveQute(project, pom); CliDriver.invokeExtensionRemoveMultiple(project, pom); + CliDriver.invokeExtensionRemoveMultipleCommas(project, pom); CliDriver.invokeExtensionListInstallableSearch(project); CliDriver.invokeExtensionListFormatting(project); diff --git a/docs/src/main/asciidoc/kafka.adoc b/docs/src/main/asciidoc/kafka.adoc index 819d70d996dbe..b559acfa4d85d 100644 --- a/docs/src/main/asciidoc/kafka.adoc +++ b/docs/src/main/asciidoc/kafka.adoc @@ -1952,10 +1952,8 @@ mp.messaging.outgoing.fruit-out.compression.type=snappy ---- In JVM mode, it will work out of the box. -However, to compile your application to a native executable, you need to: - -1. Uses GraalVM 21.+ -2. Add `quarkus.kafka.snappy.enabled=true` to your `application.properties` +However, to compile your application to a native executable, you need to +add `quarkus.kafka.snappy.enabled=true` to your `application.properties`. In native mode, Snappy is disabled by default as the use of Snappy requires embedding a native library and unpacking it when the application starts. diff --git a/docs/src/main/asciidoc/logging.adoc b/docs/src/main/asciidoc/logging.adoc index a9329d517f4b1..81afed44a01c7 100644 --- a/docs/src/main/asciidoc/logging.adoc +++ b/docs/src/main/asciidoc/logging.adoc @@ -841,4 +841,5 @@ NOTE: If applicable, MDC data is stored in a _duplicated context_, which is an i [[loggingConfigurationReference]] == Logging configuration reference +include::{generated-dir}/config/quarkus-log-logging-log-build-time-config.adoc[opts=optional, leveloffset=+1] include::{generated-dir}/config/quarkus-log-logging-log-config.adoc[opts=optional, leveloffset=+1] diff --git a/docs/src/main/asciidoc/native-reference.adoc b/docs/src/main/asciidoc/native-reference.adoc index 5aaf0176fef95..4880f74a2051b 100644 --- a/docs/src/main/asciidoc/native-reference.adoc +++ b/docs/src/main/asciidoc/native-reference.adoc @@ -846,8 +846,8 @@ we need the following cypher script which will import the data within the CSV fi [source,cypher] ---- -CREATE CONSTRAINT unique_vm_id ON (v:VM) ASSERT v.vmId IS UNIQUE; -CREATE CONSTRAINT unique_method_id ON (m:Method) ASSERT m.methodId IS UNIQUE; +CREATE CONSTRAINT unique_vm_id FOR (v:VM) REQUIRE v.vmId IS UNIQUE; +CREATE CONSTRAINT unique_method_id FOR (m:Method) REQUIRE m.methodId IS UNIQUE; LOAD CSV WITH HEADERS FROM 'file:///reports/call_tree_vm.csv' AS row MERGE (v:VM {vmId: row.Id, name: row.Name}) diff --git a/docs/src/main/asciidoc/rest-client-reactive.adoc b/docs/src/main/asciidoc/rest-client-reactive.adoc index b3c063bccdda2..1fea2b5a03736 100644 --- a/docs/src/main/asciidoc/rest-client-reactive.adoc +++ b/docs/src/main/asciidoc/rest-client-reactive.adoc @@ -974,7 +974,7 @@ public interface SseClient { Multi> get(); - class HeartbeatFilter implements Predicate> { + class HeartbeatFilter implements Predicate> { @Override public boolean test(SseEvent event) { diff --git a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc index ee38209a69427..0d99374a5b348 100644 --- a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc @@ -225,7 +225,7 @@ For more information, see xref:security-oidc-bearer-token-authentication.adoc#in [[keycloak-initialization]] === Keycloak Initialization -The `quay.io/keycloak/keycloak:22.0.5` image which contains a Keycloak distribution powered by Quarkus is used to start a container by default. +The `quay.io/keycloak/keycloak:23.0.0` image which contains a Keycloak distribution powered by Quarkus is used to start a container by default. `quarkus.keycloak.devservices.image-name` can be used to change the Keycloak image name. For example, set it to `quay.io/keycloak/keycloak:19.0.3-legacy` to use a Keycloak distribution powered by WildFly. Note that only a Quarkus based Keycloak distribution is available starting from Keycloak `20.0.0`. diff --git a/docs/sync-web-site.sh b/docs/sync-web-site.sh index 353e45c2c58e0..6f310b98c61dd 100755 --- a/docs/sync-web-site.sh +++ b/docs/sync-web-site.sh @@ -32,6 +32,7 @@ fi if [ -z $TARGET_DIR ]; then TARGET_DIR=target/web-site + rm -rf ${TARGET_DIR} GIT_OPTIONS="" if [[ "$QUARKUS_WEB_SITE_PUSH" != "true" ]]; then GIT_OPTIONS="--depth=1" 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 065acd241b0c0..e36b279d99b4a 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 @@ -220,7 +220,7 @@ private DevServicesResultBuildItem.RunningDevService startElasticsearch( container.withLabel(DEV_SERVICE_LABEL, config.serviceName); } if (config.port.isPresent()) { - container.setPortBindings(List.of(config.port.get() + ":" + config.port.get())); + container.setPortBindings(List.of(config.port.get() + ":" + ELASTICSEARCH_PORT)); } timeout.ifPresent(container::withStartupTimeout); diff --git a/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/DevServicesElasticsearchDevModeCustomPortTestCase.java b/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/DevServicesElasticsearchDevModeCustomPortTestCase.java new file mode 100644 index 0000000000000..2f9a9e39a3ec9 --- /dev/null +++ b/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/DevServicesElasticsearchDevModeCustomPortTestCase.java @@ -0,0 +1,45 @@ +package io.quarkus.elasticsearch.restclient.lowlevel.runtime; + +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; + +public class DevServicesElasticsearchDevModeCustomPortTestCase { + @RegisterExtension + static QuarkusDevModeTest test = new QuarkusDevModeTest() + .withApplicationRoot((jar) -> jar + .addClass(TestResource.class) + .addAsResource(new StringAsset("quarkus.elasticsearch.devservices.port=19200"), "application.properties")); + + @Test + public void checkConfiguredPort() { + RestAssured + .when().get("/fruits/configured-hosts") + .then().body(endsWith(":19200")); + + } + + @Test + public void testDatasource() throws Exception { + var fruit = new TestResource.Fruit(); + fruit.id = "1"; + fruit.name = "banana"; + fruit.color = "yellow"; + + RestAssured + .given().body(fruit).contentType("application/json") + .when().post("/fruits") + .then().statusCode(204); + + RestAssured.when().get("/fruits/search?term=color&match=yellow") + .then() + .statusCode(200) + .body(equalTo("[{\"id\":\"1\",\"name\":\"banana\",\"color\":\"yellow\"}]")); + } +} diff --git a/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/TestResource.java b/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/TestResource.java index 5a10c4ebb8d67..4ab00a0f385d7 100644 --- a/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/TestResource.java +++ b/extensions/elasticsearch-rest-client/deployment/src/test/java/io/quarkus/elasticsearch/restclient/lowlevel/runtime/TestResource.java @@ -11,6 +11,7 @@ import jakarta.ws.rs.QueryParam; import org.apache.http.util.EntityUtils; +import org.eclipse.microprofile.config.ConfigProvider; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; @@ -57,6 +58,12 @@ public List search(@QueryParam("term") String term, @QueryParam("match") return results; } + @GET + @Path("/configured-hosts") + public String configuredHosts() { + return ConfigProvider.getConfig().getConfigValue("quarkus.elasticsearch.hosts").getValue(); + } + public static class Fruit { public String id; public String name; diff --git a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/mapping/id/optimizer/optimizer/IdOptimizerDefaultNoneTest.java b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/mapping/id/optimizer/optimizer/IdOptimizerDefaultNoneTest.java index 2ff9f955a727c..fcc9da07c9937 100644 --- a/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/mapping/id/optimizer/optimizer/IdOptimizerDefaultNoneTest.java +++ b/extensions/hibernate-reactive/deployment/src/test/java/io/quarkus/hibernate/reactive/mapping/id/optimizer/optimizer/IdOptimizerDefaultNoneTest.java @@ -2,6 +2,7 @@ import org.hibernate.id.enhanced.NoopOptimizer; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.hibernate.reactive.SchemaUtil; @@ -22,6 +23,7 @@ public class IdOptimizerDefaultNoneTest extends AbstractIdOptimizerDefaultTest { .overrideConfigKey("quarkus.hibernate-orm.mapping.id.optimizer.default", "none"); @Override + @Test @Disabled("The 'none' optimizer will produce a different stream of IDs (1 then 51 then 101 then ...)") public void ids(UniAsserter asserter) { super.ids(asserter); diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/mpmetrics/MetricRegistryAdapter.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/mpmetrics/MetricRegistryAdapter.java index be354011e3b89..4bffa61ee7830 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/mpmetrics/MetricRegistryAdapter.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/mpmetrics/MetricRegistryAdapter.java @@ -90,13 +90,13 @@ public Counter counter(Metadata metadata, Tag... tags) { Counter interceptorCounter(Metadata metadata, String... tags) { return internalCounter(internalGetMetadata(metadata, MetricType.COUNTER), - new MetricDescriptor(metadata.getName(), tags)); + new MetricDescriptor(metadata.getName(), scopeTags(tags))); } Counter injectedCounter(org.eclipse.microprofile.metrics.annotation.Metric annotation) { return internalCounter( internalGetMetadata(annotation.name(), MetricType.COUNTER).merge(annotation), - new MetricDescriptor(annotation.name(), annotation.tags())); + new MetricDescriptor(annotation.name(), scopeTags(annotation.tags()))); } CounterAdapter internalCounter(MpMetadata metadata, MetricDescriptor id) { @@ -138,13 +138,13 @@ public ConcurrentGauge concurrentGauge(Metadata metadata, Tag... tags) { ConcurrentGaugeImpl interceptorConcurrentGauge(Metadata metadata, String... tags) { return internalConcurrentGauge(internalGetMetadata(metadata, MetricType.CONCURRENT_GAUGE), - new MetricDescriptor(metadata.getName(), tags)); + new MetricDescriptor(metadata.getName(), scopeTags(tags))); } ConcurrentGaugeImpl injectedConcurrentGauge(org.eclipse.microprofile.metrics.annotation.Metric annotation) { return internalConcurrentGauge( internalGetMetadata(annotation.name(), MetricType.CONCURRENT_GAUGE).merge(annotation), - new MetricDescriptor(annotation.name(), annotation.tags())); + new MetricDescriptor(annotation.name(), scopeTags(annotation.tags()))); } ConcurrentGaugeImpl internalConcurrentGauge(MpMetadata metadata, MetricDescriptor id) { @@ -276,7 +276,7 @@ public Histogram histogram(Metadata metadata, Tag... tags) { HistogramAdapter injectedHistogram(org.eclipse.microprofile.metrics.annotation.Metric annotation) { return internalHistogram( internalGetMetadata(annotation.name(), MetricType.HISTOGRAM).merge(annotation), - new MetricDescriptor(annotation.name(), annotation.tags())); + new MetricDescriptor(annotation.name(), scopeTags(annotation.tags()))); } HistogramAdapter internalHistogram(MpMetadata metadata, MetricDescriptor id) { @@ -319,7 +319,7 @@ public Meter meter(Metadata metadata, Tag... tags) { MeterAdapter injectedMeter(org.eclipse.microprofile.metrics.annotation.Metric annotation) { return internalMeter( internalGetMetadata(annotation.name(), MetricType.METERED).merge(annotation), - new MetricDescriptor(annotation.name(), annotation.tags())); + new MetricDescriptor(annotation.name(), scopeTags(annotation.tags()))); } MeterAdapter internalMeter(MpMetadata metadata, MetricDescriptor id) { @@ -363,12 +363,12 @@ public Timer timer(Metadata metadata, Tag... tags) { TimerAdapter injectedTimer(org.eclipse.microprofile.metrics.annotation.Metric annotation) { return internalTimer( internalGetMetadata(annotation.name(), MetricType.TIMER).merge(annotation), - new MetricDescriptor(annotation.name(), annotation.tags())); + new MetricDescriptor(annotation.name(), scopeTags(annotation.tags()))); } TimerAdapter interceptorTimer(Metadata metadata, String... tags) { return internalTimer(internalGetMetadata(metadata, MetricType.TIMER), - new MetricDescriptor(metadata.getName(), tags)); + new MetricDescriptor(metadata.getName(), scopeTags(tags))); } TimerAdapter internalTimer(MpMetadata metadata, MetricDescriptor id) { @@ -465,7 +465,7 @@ public Metadata getMetadata(String name) { TimerAdapter injectedSimpleTimer(org.eclipse.microprofile.metrics.annotation.Metric annotation) { return internalSimpleTimer( internalGetMetadata(annotation.name(), MetricType.SIMPLE_TIMER).merge(annotation), - new MetricDescriptor(annotation.name(), annotation.tags())); + new MetricDescriptor(annotation.name(), scopeTags(annotation.tags()))); } TimerAdapter internalSimpleTimer(MpMetadata metadata, MetricDescriptor id) { @@ -657,14 +657,23 @@ public Type getType() { return null; } + Tags scopeTags() { + return Tags.of("scope", this.type.getName()); + } + Tags scopeTags(Tag... tags) { - Tags out = Tags.of("scope", this.type.getName()); + Tags out = scopeTags(); for (Tag t : tags) { out = out.and(t.getTagName(), t.getTagValue()); } return out; } + Tags scopeTags(String... tags) { + Tags in = Tags.of(tags); + return scopeTags().and(in); + } + private MpMetadata internalGetMetadata(String name, MetricType type) { MpMetadata result = metadataMap.computeIfAbsent(name, k -> new MpMetadata(name, type)); if (result.type != type) { diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java index 05a69f6df1015..fd3ee7e96b0a9 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java @@ -35,7 +35,7 @@ public class DevServicesConfig { * string. * Set 'quarkus.keycloak.devservices.keycloak-x-image' to override this check. */ - @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:22.0.5") + @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:23.0.0") public String imageName; /** 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 23ffebaa2e6a2..019a3e4fad943 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 @@ -108,7 +108,7 @@ public class KeycloakDevServicesProcessor { private static final String KEYCLOAK_QUARKUS_HOSTNAME = "KC_HOSTNAME"; private static final String KEYCLOAK_QUARKUS_ADMIN_PROP = "KEYCLOAK_ADMIN"; private static final String KEYCLOAK_QUARKUS_ADMIN_PASSWORD_PROP = "KEYCLOAK_ADMIN_PASSWORD"; - private static final String KEYCLOAK_QUARKUS_START_CMD = "start --storage=chm --http-enabled=true --hostname-strict=false --hostname-strict-https=false"; + private static final String KEYCLOAK_QUARKUS_START_CMD = "start --http-enabled=true --hostname-strict=false --hostname-strict-https=false"; private static final String JAVA_OPTS = "JAVA_OPTS"; private static final String OIDC_USERS = "oidc.users"; diff --git a/extensions/redis-cache/runtime/src/main/java/io/quarkus/cache/redis/runtime/RedisCacheInfoBuilder.java b/extensions/redis-cache/runtime/src/main/java/io/quarkus/cache/redis/runtime/RedisCacheInfoBuilder.java index 80113eee560ba..7fd99591c1ba9 100644 --- a/extensions/redis-cache/runtime/src/main/java/io/quarkus/cache/redis/runtime/RedisCacheInfoBuilder.java +++ b/extensions/redis-cache/runtime/src/main/java/io/quarkus/cache/redis/runtime/RedisCacheInfoBuilder.java @@ -31,7 +31,7 @@ public static Set build(Set cacheNames, RedisCachesBuild if (namedRuntimeConfig != null && namedRuntimeConfig.expireAfterWrite.isPresent()) { cacheInfo.expireAfterWrite = namedRuntimeConfig.expireAfterWrite; - } else if (defaultRuntimeConfig.expireAfterAccess.isPresent()) { + } else if (defaultRuntimeConfig.expireAfterWrite.isPresent()) { cacheInfo.expireAfterWrite = defaultRuntimeConfig.expireAfterWrite; } diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index b96c574255bfa..cea258ce2771c 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -47,7 +47,6 @@ import java.util.function.Predicate; import java.util.function.Supplier; import java.util.regex.Pattern; -import java.util.stream.Collectors; import jakarta.ws.rs.ProcessingException; import jakarta.ws.rs.RuntimeType; @@ -287,9 +286,8 @@ void setupClientProxies(JaxrsClientReactiveRecorder recorder, scannedParameterContainers.addAll(parameterContainersBuildItem.getClassNames()); } reflectiveClassBuildItemBuildProducer.produce(ReflectiveClassBuildItem - .builder(scannedParameterContainers.stream().map(name -> name.toString()).collect(Collectors.toSet()) - .toArray(new String[0])) - .fields().build()); + .builder(scannedParameterContainers.stream().map(DotName::toString).distinct().toArray(String[]::new)) + .methods().fields().build()); if (resourceScanningResultBuildItem.isEmpty() || resourceScanningResultBuildItem.get().getResult().getClientInterfaces().isEmpty()) { diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization-common/runtime/src/main/java/io/quarkus/resteasy/reactive/kotlin/serialization/common/runtime/JsonConfig.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization-common/runtime/src/main/java/io/quarkus/resteasy/reactive/kotlin/serialization/common/runtime/JsonConfig.java index df47f1c32e7c0..bc64347ff3d76 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization-common/runtime/src/main/java/io/quarkus/resteasy/reactive/kotlin/serialization/common/runtime/JsonConfig.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization-common/runtime/src/main/java/io/quarkus/resteasy/reactive/kotlin/serialization/common/runtime/JsonConfig.java @@ -129,6 +129,12 @@ public class JsonConfig { @ConfigItem(defaultValue = "false") public boolean decodeEnumsCaseInsensitive = false; + /** + * Specifies if trailing comma is allowed. + */ + @ConfigItem(defaultValue = "false") + public boolean allowTrailingComma = false; + @Override public String toString() { return new StringJoiner(", ", JsonConfig.class.getSimpleName() + "[", "]") @@ -144,6 +150,7 @@ public String toString() { .add("allowSpecialFloatingPointValues=" + allowSpecialFloatingPointValues) .add("useAlternativeNames=" + useAlternativeNames) .add("decodeEnumsCaseInsensitive=" + decodeEnumsCaseInsensitive) + .add("allowTrailingComma=" + allowTrailingComma) .toString(); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization-common/runtime/src/main/kotlin/io/quarkus/resteasy/reactive/kotlin/serialization/common/runtime/JsonProducer.kt b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization-common/runtime/src/main/kotlin/io/quarkus/resteasy/reactive/kotlin/serialization/common/runtime/JsonProducer.kt index 45ea46cde67a8..89dc0289b1104 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization-common/runtime/src/main/kotlin/io/quarkus/resteasy/reactive/kotlin/serialization/common/runtime/JsonProducer.kt +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization-common/runtime/src/main/kotlin/io/quarkus/resteasy/reactive/kotlin/serialization/common/runtime/JsonProducer.kt @@ -38,6 +38,7 @@ class JsonProducer { useAlternativeNames = configuration.json.useAlternativeNames useArrayPolymorphism = configuration.json.useArrayPolymorphism decodeEnumsCaseInsensitive = configuration.json.decodeEnumsCaseInsensitive + allowTrailingComma = configuration.json.allowTrailingComma configuration.json.namingStrategy.ifPresent { strategy -> loadStrategy(this, strategy, this@JsonProducer) diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java index 851e82a60e179..f73cfa65aeeeb 100644 --- a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java +++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java @@ -244,6 +244,7 @@ void registerAutoSecurityFilter(BuildProducer syntheticB @BuildStep @Record(ExecutionTime.STATIC_INIT) void registerAnnotatedUserDefinedRuntimeFilters(BuildProducer syntheticBeans, + BuildProducer reflectiveClass, OpenApiFilteredIndexViewBuildItem apiFilteredIndexViewBuildItem, OpenApiRecorder recorder) { Config config = ConfigProvider.getConfig(); @@ -256,6 +257,7 @@ void registerAnnotatedUserDefinedRuntimeFilters(BuildProducer springComponent.principalNameFromObject(new Person("user")), "user", USER); } + @Test + public void testPrincipalNameFromObjectIsNot() { + assertFailureFor(() -> springComponent.principalNameFromObjectIsNot(new Person("whatever")), + UnauthorizedException.class, + ANONYMOUS); + assertSuccess(() -> springComponent.principalNameFromObjectIsNot(new Person("whatever")), "whatever", USER); + assertFailureFor(() -> springComponent.principalNameFromObjectIsNot(new Person("user")), ForbiddenException.class, + USER); + } + @Test public void testNotSecured() { assertSuccess(() -> springComponent.notSecured(), "notSecured", ANONYMOUS); diff --git a/extensions/spring-security/deployment/src/test/java/io/quarkus/spring/security/deployment/springapp/SpringComponent.java b/extensions/spring-security/deployment/src/test/java/io/quarkus/spring/security/deployment/springapp/SpringComponent.java index 0d86e8406fcf3..68a8af6552c6d 100644 --- a/extensions/spring-security/deployment/src/test/java/io/quarkus/spring/security/deployment/springapp/SpringComponent.java +++ b/extensions/spring-security/deployment/src/test/java/io/quarkus/spring/security/deployment/springapp/SpringComponent.java @@ -37,6 +37,11 @@ public String principalNameFromObject(Person person) { return person.getName(); } + @PreAuthorize("#person.name != authentication.principal.username") + public String principalNameFromObjectIsNot(Person person) { + return person.getName(); + } + public String notSecured() { return "notSecured"; } diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/SpringSecurityRecorder.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/SpringSecurityRecorder.java index 8101bdd0991b6..591c08d8bd892 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/SpringSecurityRecorder.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/SpringSecurityRecorder.java @@ -70,8 +70,9 @@ public SecurityCheck fromGeneratedClass(String generatedClassName) { } public SecurityCheck principalNameFromParameterObjectSecurityCheck(int index, String expectedParameterClass, - String stringPropertyAccessorClass, String propertyName) { + String stringPropertyAccessorClass, String propertyName, + PrincipalNameFromParameterObjectSecurityCheck.CheckType checkType) { return PrincipalNameFromParameterObjectSecurityCheck.of(index, expectedParameterClass, stringPropertyAccessorClass, - propertyName); + propertyName, checkType); } } diff --git a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterObjectSecurityCheck.java b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterObjectSecurityCheck.java index c34e97b6d5f7b..63da575e48187 100644 --- a/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterObjectSecurityCheck.java +++ b/extensions/spring-security/runtime/src/main/java/io/quarkus/spring/security/runtime/interceptor/check/PrincipalNameFromParameterObjectSecurityCheck.java @@ -23,22 +23,24 @@ public class PrincipalNameFromParameterObjectSecurityCheck implements SecurityCh private final Class expectedParameterClass; private final Class stringPropertyAccessorClass; private final String propertyName; + private final CheckType checkType; private PrincipalNameFromParameterObjectSecurityCheck(int index, String expectedParameterClass, - String stringPropertyAccessorClass, String propertyName) throws ClassNotFoundException { + String stringPropertyAccessorClass, String propertyName, CheckType checkType) throws ClassNotFoundException { this.index = index; this.expectedParameterClass = Class.forName(expectedParameterClass, false, Thread.currentThread().getContextClassLoader()); this.stringPropertyAccessorClass = (Class) Class.forName(stringPropertyAccessorClass, false, Thread.currentThread().getContextClassLoader()); this.propertyName = propertyName; + this.checkType = checkType; } public static PrincipalNameFromParameterObjectSecurityCheck of(int index, String expectedParameterClass, - String stringPropertyAccessorClass, String propertyName) { + String stringPropertyAccessorClass, String propertyName, CheckType checkType) { try { return new PrincipalNameFromParameterObjectSecurityCheck(index, expectedParameterClass, stringPropertyAccessorClass, - propertyName); + propertyName, checkType); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } @@ -70,8 +72,14 @@ private void doApply(SecurityIdentity identity, Object[] parameters, String clas } String name = identity.getPrincipal().getName(); - if (!name.equals(parameterValueStr)) { - throw new ForbiddenException(); + if (checkType == CheckType.EQ) { + if (!name.equals(parameterValueStr)) { + throw new ForbiddenException(); + } + } else if (checkType == CheckType.NEQ) { + if (name.equals(parameterValueStr)) { + throw new ForbiddenException(); + } } } @@ -84,4 +92,9 @@ private IllegalStateException genericNotApplicableException(String className, St "PrincipalNameFromParameterObjectSecurityCheck with index " + index + " cannot be applied to '" + className + "#" + methodName + "'"); } + + public enum CheckType { + EQ, + NEQ + } } diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml index 9fcf219e160dd..5cf3128c08d0c 100644 --- a/independent-projects/arc/pom.xml +++ b/independent-projects/arc/pom.xml @@ -54,7 +54,7 @@ 1.6.Final 3.24.2 - 5.10.0 + 5.10.1 1.9.21 1.7.3 5.7.0 diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index f64a96a1c42f5..8b22177b9da75 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -953,8 +953,14 @@ private List findBeans(Collection beanDefiningAnnotations, Li .map(StereotypeInfo::getName) .collect(Collectors.toSet()); + Set seenClasses = new HashSet<>(); + // If needed use the specialized immutable index to discover beans for (ClassInfo beanClass : beanArchiveImmutableIndex.getKnownClasses()) { + if (!seenClasses.add(beanClass.name())) { + // avoid discovering the same bean twice + continue; + } if (Modifier.isInterface(beanClass.flags()) || Modifier.isAbstract(beanClass.flags()) || beanClass.isAnnotation() || beanClass.isEnum()) { @@ -1579,6 +1585,29 @@ private void validateBeans(List errors, Consumer } } } + + List>> duplicateBeanIds = beans.stream() + .collect(Collectors.groupingBy(BeanInfo::getIdentifier)) + .entrySet() + .stream() + .filter(entry -> entry.getValue().size() > 1) + .collect(Collectors.toList()); + if (!duplicateBeanIds.isEmpty()) { + String separator = "===================="; + StringBuilder error = new StringBuilder("\n") + .append(separator).append(separator).append(separator).append(separator).append("\n") + .append("Multiple beans with the same identifier found!\n") + .append("----------------------------------------------\n") + .append("This is an internal error. Please report a bug and attach the following listing.\n\n"); + for (Map.Entry> entry : duplicateBeanIds) { + error.append(entry.getKey()).append(" -> ").append(entry.getValue().size()).append(" beans:\n"); + for (BeanInfo bean : entry.getValue()) { + error.append("- ").append(bean).append("\n"); + } + } + error.append(separator).append(separator).append(separator).append(separator).append("\n"); + errors.add(new DeploymentException(error.toString())); + } } private void findNamespaces(BeanInfo bean, Set namespaces) { diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index 42aa44afb02e2..ad9f2f590dadf 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -46,7 +46,7 @@ 3.24.2 0.9.5 3.5.3.Final - 5.10.0 + 5.10.1 3.9.6 0.9.0.M2 3.10.2 @@ -61,7 +61,7 @@ 4.0.1 2.0.1 1.16.0 - 2.15.0 + 2.15.1 3.13.0 32.1.3-jre 1.0.1 diff --git a/independent-projects/extension-maven-plugin/pom.xml b/independent-projects/extension-maven-plugin/pom.xml index 909d80325c1ff..3b7c4993340c4 100644 --- a/independent-projects/extension-maven-plugin/pom.xml +++ b/independent-projects/extension-maven-plugin/pom.xml @@ -44,7 +44,7 @@ 3.10.2 2.15.3 1.3.2 - 5.10.0 + 5.10.1 diff --git a/independent-projects/junit5-virtual-threads/pom.xml b/independent-projects/junit5-virtual-threads/pom.xml index d30d68dcb74ef..cdb450b45d62b 100644 --- a/independent-projects/junit5-virtual-threads/pom.xml +++ b/independent-projects/junit5-virtual-threads/pom.xml @@ -48,7 +48,7 @@ 2.23.0 1.9.0 - 5.10.0 + 5.10.1 diff --git a/independent-projects/qute/pom.xml b/independent-projects/qute/pom.xml index ca6c9c5db9f07..f618d9bee11b4 100644 --- a/independent-projects/qute/pom.xml +++ b/independent-projects/qute/pom.xml @@ -41,7 +41,7 @@ 11 11 11 - 5.10.0 + 5.10.1 3.24.2 3.1.6 1.7.0 diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientReaderInterceptorContextImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientReaderInterceptorContextImpl.java index 200f0c5bcc8eb..99fa3837fd647 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientReaderInterceptorContextImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientReaderInterceptorContextImpl.java @@ -91,7 +91,8 @@ public Object proceed() throws IOException, WebApplicationException { } } - StringBuilder errorMessage = new StringBuilder("Response could not be mapped to type " + entityType); + StringBuilder errorMessage = new StringBuilder( + "Response could not be mapped to type " + entityType + " for response with media type " + mediaType); if (!contextualizers.isEmpty()) { var input = new MissingMessageBodyReaderErrorMessageContextualizer.Input() { @Override diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 5089c255879c7..5ce2d22175a19 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -50,7 +50,7 @@ 4.0.1 3.1.6 1.12.12 - 5.10.0 + 5.10.1 3.9.6 3.24.2 3.5.3.Final diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java index dedaf7dd127cd..2248a8f7763ac 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java @@ -226,18 +226,20 @@ public void setMaxPathParams(int maxPathParams) { } } - public String getPathParam(int index) { - return doGetPathParam(index, pathParamValues); + public String getPathParam(int index, boolean encoded) { + return doGetPathParam(index, pathParamValues, encoded); } - private String doGetPathParam(int index, Object pathParamValues) { + private String doGetPathParam(int index, Object pathParamValues, boolean encoded) { if (pathParamValues instanceof String[]) { - return ((String[]) pathParamValues)[index]; + String pathParam = ((String[]) pathParamValues)[index]; + return encoded ? pathParam : Encode.decodePath(pathParam); } if (index > 1) { throw new IndexOutOfBoundsException(); } - return (String) pathParamValues; + String pathParam = (String) pathParamValues; + return encoded ? pathParam : Encode.decodePath(pathParam); } public ResteasyReactiveRequestContext setPathParamValue(int index, String value) { @@ -926,18 +928,11 @@ public String getPathParameter(String name, boolean encoded) { Integer index = target.getPathParameterIndexes().get(name); String value; if (index != null) { - value = getPathParam(index); - } else { - // Check previous resources if the path is not defined in the current target - value = getResourceLocatorPathParam(name); - } - - // It's possible to inject a path param that's not defined, return null in this case - if (encoded && value != null) { - return Encode.encodeQueryParam(value); + return getPathParam(index, encoded); } - return value; + // Check previous resources if the path is not defined in the current target + return getResourceLocatorPathParam(name, encoded); } @Override @@ -996,8 +991,8 @@ public ResteasyReactiveResourceInfo getResteasyReactiveResourceInfo() { public abstract Runnable registerTimer(long millis, Runnable task); - public String getResourceLocatorPathParam(String name) { - return getResourceLocatorPathParam(name, (PreviousResource) getProperty(PreviousResource.PROPERTY_KEY)); + public String getResourceLocatorPathParam(String name, boolean encoded) { + return getResourceLocatorPathParam(name, (PreviousResource) getProperty(PreviousResource.PROPERTY_KEY), encoded); } public FormData getFormData() { @@ -1009,7 +1004,7 @@ public ResteasyReactiveRequestContext setFormData(FormData formData) { return this; } - private String getResourceLocatorPathParam(String name, PreviousResource previousResource) { + private String getResourceLocatorPathParam(String name, PreviousResource previousResource, boolean encoded) { if (previousResource == null) { return null; } @@ -1020,13 +1015,13 @@ private String getResourceLocatorPathParam(String name, PreviousResource previou for (URITemplate.TemplateComponent component : classPath.components) { if (component.name != null) { if (component.name.equals(name)) { - return doGetPathParam(index, previousResource.locatorPathParamValues); + return doGetPathParam(index, previousResource.locatorPathParamValues, encoded); } index++; } else if (component.names != null) { for (String nm : component.names) { if (nm.equals(name)) { - return doGetPathParam(index, previousResource.locatorPathParamValues); + return doGetPathParam(index, previousResource.locatorPathParamValues, encoded); } } index++; @@ -1036,19 +1031,19 @@ private String getResourceLocatorPathParam(String name, PreviousResource previou for (URITemplate.TemplateComponent component : previousResource.locatorTarget.getPath().components) { if (component.name != null) { if (component.name.equals(name)) { - return doGetPathParam(index, previousResource.locatorPathParamValues); + return doGetPathParam(index, previousResource.locatorPathParamValues, encoded); } index++; } else if (component.names != null) { for (String nm : component.names) { if (nm.equals(name)) { - return doGetPathParam(index, previousResource.locatorPathParamValues); + return doGetPathParam(index, previousResource.locatorPathParamValues, encoded); } } index++; } } - return getResourceLocatorPathParam(name, previousResource.prev); + return getResourceLocatorPathParam(name, previousResource.prev, encoded); } public abstract boolean resumeExternalProcessing(); diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/LocatableResourcePathParamExtractor.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/LocatableResourcePathParamExtractor.java index 835be0b515340..e478141b7c45c 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/LocatableResourcePathParamExtractor.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/LocatableResourcePathParamExtractor.java @@ -12,7 +12,7 @@ public LocatableResourcePathParamExtractor(String name) { @Override public Object extractParameter(ResteasyReactiveRequestContext context) { - return context.getResourceLocatorPathParam(name); + return context.getResourceLocatorPathParam(name, false); } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/PathParamExtractor.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/PathParamExtractor.java index ccfa99edca5c3..d56f72b856bf5 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/PathParamExtractor.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/PathParamExtractor.java @@ -1,6 +1,8 @@ package org.jboss.resteasy.reactive.server.core.parameters; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.jboss.resteasy.reactive.common.util.Encode; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; @@ -19,14 +21,14 @@ public PathParamExtractor(int index, boolean encoded, boolean single) { @Override public Object extractParameter(ResteasyReactiveRequestContext context) { - String pathParam = context.getPathParam(index); - if (encoded) { - pathParam = Encode.encodeQueryParam(pathParam); - } + String pathParam = context.getPathParam(index, true); if (single) { - return pathParam; + return encoded ? pathParam : Encode.decodePath(pathParam); } else { - return List.of(pathParam.split("/")); + return encoded + ? List.of(pathParam.split("/")) + : Arrays.stream(pathParam.split("/")).map(Encode::decodePath) + .collect(Collectors.toList()); } } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java index 7cbd13cb272a6..987bbf2838113 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java @@ -152,7 +152,7 @@ public MultivaluedMap getPathParameters(boolean decode) { RuntimeResource target = currentRequest.getTarget(); if (target != null) { // a target can be null if this happens in a filter that runs before the target is set for (Entry pathParam : target.getPathParameterIndexes().entrySet()) { - pathParams.add(pathParam.getKey(), currentRequest.getPathParam(pathParam.getValue())); + pathParams.add(pathParam.getKey(), currentRequest.getPathParam(pathParam.getValue(), false)); } } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/mapping/RequestMapper.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/mapping/RequestMapper.java index 5d8d5fc931191..7fd7cb535f518 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/mapping/RequestMapper.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/mapping/RequestMapper.java @@ -8,8 +8,6 @@ import java.util.Map; import java.util.regex.Matcher; -import org.jboss.resteasy.reactive.common.util.URIDecoder; - public class RequestMapper { private static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -111,7 +109,7 @@ private RequestMatch mapFromPathMatcher(String path, PathMatcher.PathMatch3.24.2 2.15.3 4.0.1 - 5.10.0 - 1.24.0 + 5.10.1 + 1.25.0 3.5.3.Final 5.3.1 3.2.1 diff --git a/integration-tests/micrometer-mp-metrics/src/main/java/io/quarkus/it/micrometer/mpmetrics/MessageResource.java b/integration-tests/micrometer-mp-metrics/src/main/java/io/quarkus/it/micrometer/mpmetrics/MessageResource.java index 2105c7ef33831..6ce3276f3f970 100644 --- a/integration-tests/micrometer-mp-metrics/src/main/java/io/quarkus/it/micrometer/mpmetrics/MessageResource.java +++ b/integration-tests/micrometer-mp-metrics/src/main/java/io/quarkus/it/micrometer/mpmetrics/MessageResource.java @@ -1,34 +1,60 @@ package io.quarkus.it.micrometer.mpmetrics; +import java.util.Collection; +import java.util.Objects; + import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; +import org.eclipse.microprofile.metrics.Counter; +import org.eclipse.microprofile.metrics.annotation.Metric; + +import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.search.Search; @Path("/message") public class MessageResource { private final MeterRegistry registry; + private final Counter first; + private final Counter second; - public MessageResource(MeterRegistry registry) { + public MessageResource(MeterRegistry registry, + @Metric(name = "first-counter") final Counter first, + @Metric(name = "second-counter") final Counter second) { this.registry = registry; + this.first = Objects.requireNonNull(first); + this.second = Objects.requireNonNull(second); } @GET public String message() { + first.inc(); + second.inc(); return registry.getClass().getName(); } @GET @Path("fail") public String fail() { + first.inc(); throw new NullPointerException("Failed on purpose"); } @GET @Path("item/{id}") public String item(@PathParam("id") String id) { + second.inc(); return "return message with id " + id; } + + @GET + @Path("mpmetrics") + public String metrics() { + Collection meters = Search.in(registry).name(s -> s.contains("mpmetrics")).meters(); + meters.addAll(Search.in(registry).name(s -> s.endsWith("-counter")).meters()); + return meters.stream().allMatch(x -> x.getId().getTag("scope") != null) ? "OK" : "FAIL"; + } } diff --git a/integration-tests/micrometer-mp-metrics/src/test/java/io/quarkus/it/micrometer/mpmetrics/MPMetricsTest.java b/integration-tests/micrometer-mp-metrics/src/test/java/io/quarkus/it/micrometer/mpmetrics/MPMetricsTest.java index 7d47a0f85cba2..d7c2e9fc02344 100644 --- a/integration-tests/micrometer-mp-metrics/src/test/java/io/quarkus/it/micrometer/mpmetrics/MPMetricsTest.java +++ b/integration-tests/micrometer-mp-metrics/src/test/java/io/quarkus/it/micrometer/mpmetrics/MPMetricsTest.java @@ -70,7 +70,10 @@ void validateMetricsOutput_1() { "io_quarkus_it_micrometer_mpmetrics_PrimeResource_highestPrimeNumberSoFar2{scope=\"application\"} 887.0")) // the counter associated with a timed method should have been removed - .body(not(containsString("io_quarkus_it_micrometer_mpmetrics_PrimeResource_checkPrime"))); + .body(not(containsString("io_quarkus_it_micrometer_mpmetrics_PrimeResource_checkPrime"))) + + // no calls to /message + .body(not(containsString("/message"))); } @Test @@ -83,7 +86,7 @@ void callPrimeGen_4() { } @Test - @Order(8) + @Order(6) void callMessage() { given() .when().get("/message") @@ -91,6 +94,24 @@ void callMessage() { .statusCode(200); } + @Test + @Order(7) + void callMessageFail() { + given() + .when().get("/message/fail") + .then() + .statusCode(500); + } + + @Test + @Order(8) + void callMessageId() { + given() + .when().get("/message/item/35") + .then() + .statusCode(200); + } + @Test @Order(9) void validateMetricsOutput_2() { @@ -106,7 +127,10 @@ void validateMetricsOutput_2() { "highestPrimeNumberSoFar 887.0")) .body(containsString( "io_quarkus_it_micrometer_mpmetrics_InjectedInstance_notPrime_total{scope=\"application\"}")) - .body(not(containsString("/message"))); + .body(containsString( + "first_counter_total{scope=\"application\"}")) + .body(containsString( + "second_counter_total{scope=\"application\"}")); } @Test @@ -123,4 +147,13 @@ void validateJsonOutput() { Matchers.equalTo(887.0f)); } + @Test + @Order(11) + void meters() { + given() + .when().get("/message/mpmetrics") + .then() + .statusCode(200) + .log().body(); + } } diff --git a/pom.xml b/pom.xml index 90c5862d3c077..3e14413b69ea7 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ jdbc:postgresql:hibernate_orm_test 4.5.1 - 0.0.99 + 0.0.100 false false diff --git a/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/ReportCreator.java b/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/ReportCreator.java index e118708a6c684..2d440c3762e13 100644 --- a/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/ReportCreator.java +++ b/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/ReportCreator.java @@ -37,6 +37,16 @@ public ReportCreator(ReportInfo reportInfo, JacocoConfig config) { @Override public void run() { + // Ugly workaround: + // Multiple ReportCreator shutdown hooks might run concurrently, possibly corrupting the report file(s) - e.g. when using @TestProfile. + // By locking on a class from the parent CL, all hooks are "serialized", one after another. + // In the long run there should only be as many hooks as there are different Jacoco configs...usually there will be only one config anyway! + synchronized (ExecFileLoader.class) { + doRun(); + } + } + + private void doRun() { File targetdir = new File(reportInfo.reportDir); targetdir.mkdirs(); try {