From 337a8c7be519dc66fb8bae14b526721e60d4a9d6 Mon Sep 17 00:00:00 2001 From: Lars Andringa Date: Wed, 15 Jan 2025 11:48:38 +0100 Subject: [PATCH 01/50] Allow null for query parameters in Kotlin panache (cherry picked from commit dbdc588e6c0767497b61338ae912b0de23823c1a) --- .../panache/kotlin/PanacheRepositoryBase.kt | 36 +++++++++---------- .../panache/kotlin/PanacheRepositoryBase.kt | 28 +++++++-------- .../quarkus/it/panache/kotlin/AddressDao.kt | 2 +- .../it/panache/kotlin/PersonRepository.kt | 2 +- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/PanacheRepositoryBase.kt b/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/PanacheRepositoryBase.kt index 689a9169f20bf..e2076d4ed9a41 100644 --- a/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/PanacheRepositoryBase.kt +++ b/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/PanacheRepositoryBase.kt @@ -115,7 +115,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.stream] */ @GenerateBridge - fun find(query: String, vararg params: Any): PanacheQuery = + fun find(query: String, vararg params: Any?): PanacheQuery = throw implementationInjectionMissing() /** @@ -129,7 +129,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.stream] */ @GenerateBridge - fun find(query: String, sort: Sort, vararg params: Any): PanacheQuery = + fun find(query: String, sort: Sort, vararg params: Any?): PanacheQuery = throw implementationInjectionMissing() /** @@ -142,7 +142,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.stream] */ @GenerateBridge - fun find(query: String, params: Map): PanacheQuery = + fun find(query: String, params: Map): PanacheQuery = throw implementationInjectionMissing() /** @@ -156,7 +156,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.stream] */ @GenerateBridge - fun find(query: String, sort: Sort, params: Map): PanacheQuery = + fun find(query: String, sort: Sort, params: Map): PanacheQuery = throw implementationInjectionMissing() /** @@ -217,7 +217,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.stream] */ @GenerateBridge - fun list(query: String, vararg params: Any): List = + fun list(query: String, vararg params: Any?): List = throw implementationInjectionMissing() /** @@ -232,7 +232,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.stream] */ @GenerateBridge - fun list(query: String, sort: Sort, vararg params: Any): List = + fun list(query: String, sort: Sort, vararg params: Any?): List = throw implementationInjectionMissing() /** @@ -246,7 +246,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.stream] */ @GenerateBridge - fun list(query: String, params: Map): List = + fun list(query: String, params: Map): List = throw implementationInjectionMissing() /** @@ -261,7 +261,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.stream] */ @GenerateBridge - fun list(query: String, sort: Sort, params: Map): List = + fun list(query: String, sort: Sort, params: Map): List = throw implementationInjectionMissing() /** @@ -325,7 +325,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.list] */ @GenerateBridge - fun stream(query: String, vararg params: Any): Stream = + fun stream(query: String, vararg params: Any?): Stream = throw implementationInjectionMissing() /** @@ -342,7 +342,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.list] */ @GenerateBridge - fun stream(query: String, sort: Sort, vararg params: Any): Stream = + fun stream(query: String, sort: Sort, vararg params: Any?): Stream = throw implementationInjectionMissing() /** @@ -357,7 +357,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.list] */ @GenerateBridge - fun stream(query: String, params: Map): Stream = + fun stream(query: String, params: Map): Stream = throw implementationInjectionMissing() /** @@ -373,7 +373,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.list] */ @GenerateBridge - fun stream(query: String, sort: Sort, params: Map): Stream = + fun stream(query: String, sort: Sort, params: Map): Stream = throw implementationInjectionMissing() /** @@ -447,7 +447,7 @@ interface PanacheRepositoryBase { * @return the number of entities counted. */ @GenerateBridge - fun count(query: String, vararg params: Any): Long = throw implementationInjectionMissing() + fun count(query: String, vararg params: Any?): Long = throw implementationInjectionMissing() /** * Counts the number of this type of entity matching the given query, with named parameters. @@ -457,7 +457,7 @@ interface PanacheRepositoryBase { * @return the number of entities counted. */ @GenerateBridge - fun count(query: String, params: Map): Long = + fun count(query: String, params: Map): Long = throw implementationInjectionMissing() /** @@ -493,7 +493,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.deleteAll] */ @GenerateBridge - fun delete(query: String, vararg params: Any): Long = throw implementationInjectionMissing() + fun delete(query: String, vararg params: Any?): Long = throw implementationInjectionMissing() /** * Delete all entities of this type matching the given query, with named parameters. @@ -507,7 +507,7 @@ interface PanacheRepositoryBase { * @see [PanacheRepositoryBase.deleteAll] */ @GenerateBridge - fun delete(query: String, params: Map): Long = + fun delete(query: String, params: Map): Long = throw implementationInjectionMissing() /** @@ -567,7 +567,7 @@ interface PanacheRepositoryBase { * @return the number of entities updated. */ @GenerateBridge - fun update(query: String, vararg params: Any): Int = throw implementationInjectionMissing() + fun update(query: String, vararg params: Any?): Int = throw implementationInjectionMissing() /** * Update all entities of this type matching the given query, with named parameters. @@ -577,7 +577,7 @@ interface PanacheRepositoryBase { * @return the number of entities updated. */ @GenerateBridge - fun update(query: String, params: Map): Int = + fun update(query: String, params: Map): Int = throw implementationInjectionMissing() /** diff --git a/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/PanacheRepositoryBase.kt b/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/PanacheRepositoryBase.kt index f0a6c4d8a81ea..d8eec6b82df78 100644 --- a/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/PanacheRepositoryBase.kt +++ b/extensions/panache/hibernate-reactive-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/reactive/panache/kotlin/PanacheRepositoryBase.kt @@ -104,7 +104,7 @@ interface PanacheRepositoryBase { * @see [list] list */ @GenerateBridge - fun find(query: String, vararg params: Any): PanacheQuery = + fun find(query: String, vararg params: Any?): PanacheQuery = throw INSTANCE.implementationInjectionMissing() /** @@ -117,7 +117,7 @@ interface PanacheRepositoryBase { * @see [list] list */ @GenerateBridge - fun find(query: String, sort: Sort, vararg params: Any): PanacheQuery = + fun find(query: String, sort: Sort, vararg params: Any?): PanacheQuery = throw INSTANCE.implementationInjectionMissing() /** @@ -129,7 +129,7 @@ interface PanacheRepositoryBase { * @see [list] list */ @GenerateBridge - fun find(query: String, params: Map): PanacheQuery = + fun find(query: String, params: Map): PanacheQuery = throw INSTANCE.implementationInjectionMissing() /** @@ -142,7 +142,7 @@ interface PanacheRepositoryBase { * @see [list] list */ @GenerateBridge - fun find(query: String, sort: Sort, params: Map): PanacheQuery = + fun find(query: String, sort: Sort, params: Map): PanacheQuery = throw INSTANCE.implementationInjectionMissing() /** @@ -200,7 +200,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun list(query: String, vararg params: Any): Uni> = + fun list(query: String, vararg params: Any?): Uni> = throw INSTANCE.implementationInjectionMissing() /** @@ -215,7 +215,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun list(query: String, sort: Sort, vararg params: Any): Uni> = + fun list(query: String, sort: Sort, vararg params: Any?): Uni> = throw INSTANCE.implementationInjectionMissing() /** @@ -229,7 +229,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun list(query: String, params: Map): Uni> = + fun list(query: String, params: Map): Uni> = throw INSTANCE.implementationInjectionMissing() /** @@ -244,7 +244,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun list(query: String, sort: Sort, params: Map): Uni> = + fun list(query: String, sort: Sort, params: Map): Uni> = throw INSTANCE.implementationInjectionMissing() /** @@ -319,7 +319,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun count(query: String, vararg params: Any): Uni = + fun count(query: String, vararg params: Any?): Uni = throw INSTANCE.implementationInjectionMissing() /** @@ -331,7 +331,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun count(query: String, params: Map): Uni = + fun count(query: String, params: Map): Uni = throw INSTANCE.implementationInjectionMissing() /** @@ -382,7 +382,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun delete(query: String, vararg params: Any): Uni = + fun delete(query: String, vararg params: Any?): Uni = throw INSTANCE.implementationInjectionMissing() /** @@ -398,7 +398,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun delete(query: String, params: Map): Uni = + fun delete(query: String, params: Map): Uni = throw INSTANCE.implementationInjectionMissing() /** @@ -453,7 +453,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun update(query: String, vararg params: Any): Uni = + fun update(query: String, vararg params: Any?): Uni = throw INSTANCE.implementationInjectionMissing() /** @@ -465,7 +465,7 @@ interface PanacheRepositoryBase { */ @CheckReturnValue @GenerateBridge - fun update(query: String, params: Map): Uni = + fun update(query: String, params: Map): Uni = throw INSTANCE.implementationInjectionMissing() /** diff --git a/integration-tests/hibernate-orm-panache-kotlin/src/main/kotlin/io/quarkus/it/panache/kotlin/AddressDao.kt b/integration-tests/hibernate-orm-panache-kotlin/src/main/kotlin/io/quarkus/it/panache/kotlin/AddressDao.kt index da09183897537..58f2bec88ab0a 100644 --- a/integration-tests/hibernate-orm-panache-kotlin/src/main/kotlin/io/quarkus/it/panache/kotlin/AddressDao.kt +++ b/integration-tests/hibernate-orm-panache-kotlin/src/main/kotlin/io/quarkus/it/panache/kotlin/AddressDao.kt @@ -14,5 +14,5 @@ open class AddressDao(private val dummyService: DummyService) : } } - override fun count(query: String, params: Map): Long = shouldBeOverridden() + override fun count(query: String, params: Map): Long = shouldBeOverridden() } diff --git a/integration-tests/hibernate-orm-panache-kotlin/src/main/kotlin/io/quarkus/it/panache/kotlin/PersonRepository.kt b/integration-tests/hibernate-orm-panache-kotlin/src/main/kotlin/io/quarkus/it/panache/kotlin/PersonRepository.kt index 240b0a1cf298d..14ba08da1ae03 100644 --- a/integration-tests/hibernate-orm-panache-kotlin/src/main/kotlin/io/quarkus/it/panache/kotlin/PersonRepository.kt +++ b/integration-tests/hibernate-orm-panache-kotlin/src/main/kotlin/io/quarkus/it/panache/kotlin/PersonRepository.kt @@ -6,7 +6,7 @@ import jakarta.enterprise.context.ApplicationScoped @ApplicationScoped open class PersonRepository : PanacheRepository { - override fun count(query: String, params: Map): Long { + override fun count(query: String, params: Map): Long { return INSTANCE.count(Person::class.java, query, params) } } From ec991d2ee1666e25ed64933323f14ee121e61913 Mon Sep 17 00:00:00 2001 From: Rostislav Svoboda Date: Wed, 15 Jan 2025 14:20:34 +0100 Subject: [PATCH 02/50] Replace JavaVersion.VERSION_11 with JavaVersion.VERSION_21 (cherry picked from commit d21a1da524437607f71bf50423605b3b0f07e1aa) --- docs/src/main/asciidoc/kotlin.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/main/asciidoc/kotlin.adoc b/docs/src/main/asciidoc/kotlin.adoc index 767e610cf8af8..65b0a83a26ff5 100644 --- a/docs/src/main/asciidoc/kotlin.adoc +++ b/docs/src/main/asciidoc/kotlin.adoc @@ -221,8 +221,8 @@ group = '...' // set your group version = '1.0.0-SNAPSHOT' java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } allOpen { // <2> @@ -233,12 +233,12 @@ allOpen { // <2> } compileKotlin { - kotlinOptions.jvmTarget = JavaVersion.VERSION_11 + kotlinOptions.jvmTarget = JavaVersion.VERSION_21 kotlinOptions.javaParameters = true } compileTestKotlin { - kotlinOptions.jvmTarget = JavaVersion.VERSION_11 + kotlinOptions.jvmTarget = JavaVersion.VERSION_21 } ---- @@ -290,8 +290,8 @@ group = '...' // set your group version = "1.0.0-SNAPSHOT" java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } allOpen { // <2> From d47763e37ce629ff3713d81b72a3203470f5805c Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Wed, 15 Jan 2025 13:31:06 +0100 Subject: [PATCH 03/50] Fix typo (cherry picked from commit a9bb751074f238fbe1644d5a5f425dc14a2ab684) --- .../dev-ui/echarts/echarts-horizontal-stacked-bar.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/echarts/echarts-horizontal-stacked-bar.js b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/echarts/echarts-horizontal-stacked-bar.js index 7c5f286a01931..bbe38d10b0d5e 100644 --- a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/echarts/echarts-horizontal-stacked-bar.js +++ b/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/echarts/echarts-horizontal-stacked-bar.js @@ -45,7 +45,7 @@ class EchartsHorizontalStackedBar extends EchartsAbstractCanvas { const sectionTitlesArray = this.sectionTitles.split(','); const sectionValuesArray = this.sectionValues.split(','); - + const option = new Object(); // Tooltip option.tooltip = new Object(); @@ -53,7 +53,7 @@ class EchartsHorizontalStackedBar extends EchartsAbstractCanvas { // Legend option.legend = new Object(); option.legend.show = false; - + // Grid option.grid = new Object(); option.grid.left = '3%'; @@ -86,7 +86,7 @@ class EchartsHorizontalStackedBar extends EchartsAbstractCanvas { serie.name = title; serie.type = 'bar'; serie.stack = 'total'; - serie.data = [value], + serie.data = [value]; serie.color = color; option.series.push(serie); } @@ -95,4 +95,4 @@ class EchartsHorizontalStackedBar extends EchartsAbstractCanvas { } } -customElements.define('echarts-horizontal-stacked-bar', EchartsHorizontalStackedBar); \ No newline at end of file +customElements.define('echarts-horizontal-stacked-bar', EchartsHorizontalStackedBar); From 02fe8a73bb63020af01155835ed96b8477df789b Mon Sep 17 00:00:00 2001 From: Ales Justin Date: Thu, 16 Jan 2025 02:17:19 +0100 Subject: [PATCH 04/50] Fix missing addChannel flag usage (cherry picked from commit 8e09eb0706697d475a4098afd5e2ce8229cb5ce1) --- .../java/io/quarkus/grpc/deployment/GrpcClientBuildItem.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcClientBuildItem.java b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcClientBuildItem.java index 34facd278f88d..2520b0f2d8770 100644 --- a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcClientBuildItem.java +++ b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcClientBuildItem.java @@ -30,7 +30,9 @@ public void addClient(ClientInfo client) { public void addClient(ClientInfo client, boolean addChannel) { clients.add(client); - clients.add(new ClientInfo(GrpcDotNames.CHANNEL, ClientType.CHANNEL, client.interceptors)); + if (addChannel) { + clients.add(new ClientInfo(GrpcDotNames.CHANNEL, ClientType.CHANNEL, client.interceptors)); + } } public String getClientName() { From b897387ab040e7506d95d87d7b185a928159c8c5 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 16 Jan 2025 08:52:03 +0200 Subject: [PATCH 05/50] Fix file handling in REST Client Fixes: #45627 (cherry picked from commit 30ee33980b34bc8a6fdb09317adc95ee01406369) --- .../client/reactive/FileDownloadTest.java | 89 +++++++++++++++++++ .../handlers/ClientSendRequestHandler.java | 32 +++---- 2 files changed, 103 insertions(+), 18 deletions(-) create mode 100644 extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/FileDownloadTest.java diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/FileDownloadTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/FileDownloadTest.java new file mode 100644 index 0000000000000..18752674478c3 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/FileDownloadTest.java @@ -0,0 +1,89 @@ +package io.quarkus.rest.client.reactive; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.net.URI; +import java.time.Duration; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.rest.client.reactive.redirect.RedirectingResource; +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.smallrye.mutiny.Uni; + +public class FileDownloadTest { + + private static final long FIFTY_MEGA = 50 * 1024L * 1024L; + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(Client.class, RedirectingResource.class)) + .overrideRuntimeConfigKey("quarkus.rest-client.follow-redirects", "true"); + + @TestHTTPResource + URI uri; + + @Test + void test() { + Client client = RestClientBuilder.newBuilder().baseUri(uri).build(Client.class); + File file = client.file(); + assertThat(file).exists().hasSize(FIFTY_MEGA); + + java.nio.file.Path path = client.path(); + assertThat(path).exists().hasSize(FIFTY_MEGA); + + file = client.uniFile().await().atMost(Duration.ofSeconds(10)); + assertThat(file).exists().hasSize(FIFTY_MEGA); + + path = client.uniPath().await().atMost(Duration.ofSeconds(10)); + assertThat(path).exists().hasSize(FIFTY_MEGA); + } + + @Path("/test") + public interface Client { + @GET + @Path("file") + File file(); + + @GET + @Path("file") + java.nio.file.Path path(); + + @GET + @Path("file") + Uni uniFile(); + + @GET + @Path("file") + Uni uniPath(); + } + + @Path("test") + public static class Resource { + + @Path("file") + @GET + public File file() throws IOException { + File file = createTempFileToDownload(); + RandomAccessFile f = new RandomAccessFile(file, "rw"); + f.setLength(FIFTY_MEGA); + return file; + } + + private static File createTempFileToDownload() throws IOException { + File file = File.createTempFile("toDownload", ".txt"); + file.deleteOnExit(); + return file; + } + } +} diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSendRequestHandler.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSendRequestHandler.java index 2d0392166403d..cbdcfb0f1c79c 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSendRequestHandler.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSendRequestHandler.java @@ -311,24 +311,20 @@ public void handle(AsyncResult asyncFileOpened) { new Handler<>() { @Override public void handle(AsyncResult event) { - tmpAsyncFile.flush(new Handler<>() { - public void handle(AsyncResult flushed) { - if (flushed.failed()) { - reportFinish(flushed.cause(), - requestContext); - requestContext.resume(flushed.cause()); - return; - } - - if (loggingScope != LoggingScope.NONE) { - clientLogger.logRequest( - httpClientRequest, null, false); - } - - requestContext.setTmpFilePath(tmpFilePath); - requestContext.resume(); - } - }); + if (event.failed()) { + reportFinish(event.cause(), + requestContext); + requestContext.resume(event.cause()); + return; + } + + if (loggingScope != LoggingScope.NONE) { + clientLogger.logRequest( + httpClientRequest, null, false); + } + + requestContext.setTmpFilePath(tmpFilePath); + requestContext.resume(); } }); clientResponse.resume(); From 2e71d9879e2ef6d7c57a671380722fc28db1b02d Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 15 Jan 2025 14:37:03 +0200 Subject: [PATCH 06/50] Use VertxLogDelegateFactory for internal Vert.x logging This is done because I assume that was the original intent, but also because it avoids the non-zero cost incurred by Vert.x to look up the various supported options (cherry picked from commit cf6c2cd81ee259729cd53b482b8d95488bb70f97) --- .../vertx/core/deployment/VertxCoreProcessor.java | 6 ++++++ .../io/quarkus/vertx/core/runtime/VertxCoreRecorder.java | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java index 41acd5fe59f51..0feaf4c912316 100644 --- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java +++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/core/deployment/VertxCoreProcessor.java @@ -220,6 +220,12 @@ IOThreadDetectorBuildItem ioThreadDetector(VertxCoreRecorder recorder) { return new IOThreadDetectorBuildItem(recorder.detector()); } + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void configureLogging(VertxCoreRecorder recorder) { + recorder.configureQuarkusLoggerFactory(); + } + @BuildStep @Produce(ServiceStartBuildItem.class) @Record(value = ExecutionTime.RUNTIME_INIT) 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 15ab4cd34fc98..80fcfc52ce597 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 @@ -69,6 +69,8 @@ @Recorder public class VertxCoreRecorder { + private static final String LOGGER_FACTORY_NAME_SYS_PROP = "vertx.logger-delegate-factory-class-name"; + static { System.setProperty("vertx.disableTCCL", "true"); } @@ -667,6 +669,13 @@ public static Supplier recoverFailedStart(VertxConfiguration config, Thre } + public void configureQuarkusLoggerFactory() { + String loggerClassName = System.getProperty(LOGGER_FACTORY_NAME_SYS_PROP); + if (loggerClassName == null) { + System.setProperty(LOGGER_FACTORY_NAME_SYS_PROP, VertxLogDelegateFactory.class.getName()); + } + } + static class VertxSupplier implements Supplier { final LaunchMode launchMode; final VertxConfiguration config; From 92b08aba17b328ca7e5d8f297708205e84a9352a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Thu, 16 Jan 2025 13:52:11 +0100 Subject: [PATCH 07/50] docs,tests(oidc): Use OidcTestClient with Dev Svc for OIDC (cherry picked from commit e227b9c793c79843eff0bbdb19d172c28f55238c) --- ...rity-oidc-bearer-token-authentication.adoc | 42 +++++++++++++++++++ .../security-openid-connect-dev-services.adoc | 1 + .../oidc/OidcDevServicesConfig.java | 4 +- .../oidc/OidcDevServicesProcessor.java | 19 --------- integration-tests/oidc-dev-services/pom.xml | 5 +++ .../src/main/resources/application.properties | 3 ++ ...arerAuthenticationOidcDevServicesTest.java | 25 ++++++----- 7 files changed, 64 insertions(+), 35 deletions(-) diff --git a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc index 1e5bc86d05267..0e3c6a1bfe588 100644 --- a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc @@ -857,6 +857,48 @@ For a test like this to work, the test `Auth0` application must have the `passwo This example code also shows how to pass additional parameters. For `Auth0`, these are the `audience` and `scope` parameters. +===== Test OIDC DevService + +You can also use `OidcTestClient` to test Quarkus endpoints supported by xref:security-openid-connect-dev-services.adoc#dev-services-for-oidc[Dev Services for OIDC]. +No configuration in the `application.properties` file is needed, Quarkus will configure `OidcTestClient` for you: + +[source, java] +---- +package org.acme; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.oidc.client.OidcTestClient; + +@QuarkusTest +public class GreetingResourceTest { + + static final OidcTestClient oidcTestClient = new OidcTestClient(); + + @AfterAll + public static void close() { + oidcTestClient.close(); + } + + @Test + public void testHelloEndpoint() { + String accessToken = oidcTestClient.getAccessToken("alice", "alice"); + given() + .auth().oauth2(accessToken) + .when().get("/hello") + .then() + .statusCode(200) + .body(is("Hello, Alice")); + } + +} +---- + ifndef::no-deprecated-test-resource[] [[bearer-token-integration-testing-keycloak]] ==== `KeycloakTestResourceLifecycleManager` 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 538f7c1b7e040..5bfb9256f947b 100644 --- a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc @@ -380,6 +380,7 @@ This document refers to the `http://localhost:8080/q/dev-ui` Dev UI URL in sever If you customize `quarkus.http.root-path` or `quarkus.http.non-application-root-path` properties, then replace `q` accordingly. For more information, see the https://quarkus.io/blog/path-resolution-in-quarkus/[Path resolution in Quarkus] blog post. +[[dev-services-for-oidc]] == Dev Services for OIDC When you work with Keycloak in production, <> provides the best dev mode experience. diff --git a/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesConfig.java b/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesConfig.java index e97eef86dad8d..e1adb8178adb7 100644 --- a/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesConfig.java +++ b/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesConfig.java @@ -25,9 +25,7 @@ public interface OidcDevServicesConfig { /** * A map of roles for OIDC identity provider users. *

- * If empty, default roles are assigned: `alice` receives `admin` and `user` roles, while other users receive - * `user` role. - * This map is used for role creation when no realm file is found at the `realm-path`. + * If empty, default roles are assigned: user `alice` receives `admin` and `user` roles and user `bob` receives role `user`. */ @ConfigDocMapKey("role-name") Map> roles(); diff --git a/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesProcessor.java b/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesProcessor.java index 1729e3ad64e70..1e03ae0158540 100644 --- a/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesProcessor.java +++ b/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesProcessor.java @@ -192,9 +192,6 @@ private static void registerRoutes(Router router) { router.get("/logout").handler(OidcDevServicesProcessor::logout); router.get("/userinfo").handler(OidcDevServicesProcessor::userInfo); - // can be used for testing of bearer token authentication - router.get("/testing/generate/access-token").handler(OidcDevServicesProcessor::generateAccessToken); - KeyPairGenerator kpg; try { kpg = KeyPairGenerator.getInstance("RSA"); @@ -206,22 +203,6 @@ private static void registerRoutes(Router router) { kid = createKeyId(); } - private static void generateAccessToken(RoutingContext rc) { - String user = rc.request().getParam("user"); - if (user == null || user.isEmpty()) { - rc.response().setStatusCode(400).endAndForget("Missing required parameter: user"); - return; - } - String rolesParam = rc.request().getParam("roles"); - Set roles = new HashSet<>(); - if (rolesParam == null || rolesParam.isEmpty()) { - roles.addAll(getUserRoles(user)); - } else { - roles.addAll(Arrays.asList(rolesParam.split(","))); - } - rc.response().endAndForget(createAccessToken(user, roles, Set.of("openid", "email"))); - } - private static List getUsers() { if (userToDefaultRoles.isEmpty()) { return Arrays.asList("alice", "bob"); diff --git a/integration-tests/oidc-dev-services/pom.xml b/integration-tests/oidc-dev-services/pom.xml index eace1af7f7741..50458d925f246 100644 --- a/integration-tests/oidc-dev-services/pom.xml +++ b/integration-tests/oidc-dev-services/pom.xml @@ -44,6 +44,11 @@ + + io.quarkus + quarkus-test-oidc-server + test + io.quarkus diff --git a/integration-tests/oidc-dev-services/src/main/resources/application.properties b/integration-tests/oidc-dev-services/src/main/resources/application.properties index 636d87caec1ef..02f7a3cbb7aa3 100644 --- a/integration-tests/oidc-dev-services/src/main/resources/application.properties +++ b/integration-tests/oidc-dev-services/src/main/resources/application.properties @@ -1,3 +1,6 @@ quarkus.oidc.devservices.enabled=true +quarkus.oidc.devservices.roles.Ronald=admin +%code-flow.quarkus.oidc.devservices.roles.alice=admin,user +%code-flow.quarkus.oidc.devservices.roles.bob=user %code-flow.quarkus.oidc.application-type=web-app diff --git a/integration-tests/oidc-dev-services/src/test/java/io/quarkus/it/oidc/dev/services/BearerAuthenticationOidcDevServicesTest.java b/integration-tests/oidc-dev-services/src/test/java/io/quarkus/it/oidc/dev/services/BearerAuthenticationOidcDevServicesTest.java index eac0592af5e07..623c51403e732 100644 --- a/integration-tests/oidc-dev-services/src/test/java/io/quarkus/it/oidc/dev/services/BearerAuthenticationOidcDevServicesTest.java +++ b/integration-tests/oidc-dev-services/src/test/java/io/quarkus/it/oidc/dev/services/BearerAuthenticationOidcDevServicesTest.java @@ -1,25 +1,34 @@ package io.quarkus.it.oidc.dev.services; import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.oidc.client.OidcTestClient; import io.restassured.RestAssured; @QuarkusTest public class BearerAuthenticationOidcDevServicesTest { + static final OidcTestClient oidcTestClient = new OidcTestClient(); + + @AfterAll + public static void close() { + oidcTestClient.close(); + } + @Test public void testLoginAsCustomUser() { RestAssured.given() - .auth().oauth2(getAccessToken("Ronald", "admin")) + .auth().oauth2(getAccessToken("Ronald")) .get("/secured/admin-only") .then() .statusCode(200) .body(Matchers.containsString("Ronald")) .body(Matchers.containsString("admin")); RestAssured.given() - .auth().oauth2(getAccessToken("Ronald", "admin")) + .auth().oauth2(getAccessToken("Ronald")) .get("/secured/user-only") .then() .statusCode(403); @@ -62,16 +71,6 @@ public void testLoginAsBob() { } private String getAccessToken(String user) { - return RestAssured.given().get(getAuthServerUrl() + "/testing/generate/access-token?user=" + user).asString(); - } - - private String getAccessToken(String user, String... roles) { - return RestAssured.given() - .get(getAuthServerUrl() + "/testing/generate/access-token?user=" + user + "&roles=" + String.join(",", roles)) - .asString(); - } - - private static String getAuthServerUrl() { - return RestAssured.get("/secured/auth-server-url").then().statusCode(200).extract().body().asString(); + return oidcTestClient.getAccessToken(user, user); } } From da51b6a24c120a2de237b1f8c8aee2b26f046c6a Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 16 Jan 2025 13:47:19 +0000 Subject: [PATCH 08/50] Enable public access to OidcProviderClientImpl#getWebClient (cherry picked from commit 930d0f2d57fc8525b9c7b2aade50082a89217267) --- .../java/io/quarkus/oidc/runtime/OidcProviderClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClientImpl.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClientImpl.java index 56d53f00498ea..123853e154ef6 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClientImpl.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClientImpl.java @@ -466,7 +466,7 @@ Vertx getVertx() { return vertx; } - WebClient getWebClient() { + public WebClient getWebClient() { return client; } From 1dbb70613764faa0202e6f018d07e6f47f66e87e Mon Sep 17 00:00:00 2001 From: brunobat Date: Wed, 15 Jan 2025 17:37:21 +0000 Subject: [PATCH 09/50] Update grafana image and remove dashboard hack (cherry picked from commit 9acf79d64ffb9512f094d2bade82e1a8f84026b4) --- .../common/ContainerConstants.java | 2 +- .../testcontainers/LgtmContainer.java | 79 ++++++++++++++----- .../ObservabilityContainer.java | 2 +- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java index bb50ac4513475..55576ccd82b1e 100644 --- a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java +++ b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java @@ -4,7 +4,7 @@ public final class ContainerConstants { // Images - public static final String LGTM = "docker.io/grafana/otel-lgtm:0.7.5"; + public static final String LGTM = "docker.io/grafana/otel-lgtm:0.8.2"; // Ports diff --git a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java index 89992c23f8102..1f9fdc301c3a0 100644 --- a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java +++ b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java @@ -15,18 +15,57 @@ public class LgtmContainer extends GrafanaContainer { protected static final String LGTM_NETWORK_ALIAS = "ltgm.testcontainer.docker"; protected static final String PROMETHEUS_CONFIG = """ - global: - scrape_interval: 10s - evaluation_interval: 10s - storage: - tsdb: - out_of_order_time_window: 10m - scrape_configs: - - job_name: '%s' - metrics_path: '%s%s' - scrape_interval: 10s - static_configs: - - targets: ['%s:%d'] + --- + otlp: + # Recommended attributes to be promoted to labels. + promote_resource_attributes: + - service.instance.id + - service.name + - service.namespace + - service.version + - cloud.availability_zone + - cloud.region + - container.name + - deployment.environment.name + - k8s.cluster.name + - k8s.container.name + - k8s.cronjob.name + - k8s.daemonset.name + - k8s.deployment.name + - k8s.job.name + - k8s.namespace.name + - k8s.pod.name + - k8s.replicaset.name + - k8s.statefulset.name + storage: + tsdb: + # A 10min time window is enough because it can easily absorb retries and network delays. + out_of_order_time_window: 10m + global: + scrape_interval: 5s + evaluation_interval: 5s + scrape_configs: + - job_name: '%s' + metrics_path: '%s%s' + scrape_interval: 5s + static_configs: + - targets: ['%s:%d'] + """; + + protected static final String DASHBOARDS_CONFIG = """ + apiVersion: 1 + + providers: + - name: "Quarkus Micrometer Prometheus" + type: file + options: + path: /otel-lgtm/grafana-dashboard-quarkus-micrometer-prometheus.json + foldersFromFilesStructure: false + - name: "Quarkus Micrometer with OTLP output" + type: file + options: + path: /otel-lgtm/grafana-dashboard-quarkus-micrometer-otlp.json + foldersFromFilesStructure: false """; public LgtmContainer() { @@ -37,18 +76,16 @@ public LgtmContainer(LgtmConfig config) { super(config); // always expose both -- since the LGTM image already does that as well addExposedPorts(ContainerConstants.OTEL_GRPC_EXPORTER_PORT, ContainerConstants.OTEL_HTTP_EXPORTER_PORT); - // cannot override grafana-dashboards.yaml in the container because it's on a version dependent path: - // ./grafana-v11.0.0/conf/provisioning/dashboards/grafana-dashboards.yaml - // will replace contents of current dashboards + + // Replacing bundled dashboards with our own + addFileToContainer(DASHBOARDS_CONFIG.getBytes(), + "/otel-lgtm/grafana/conf/provisioning/dashboards/grafana-dashboards.yaml"); withCopyFileToContainer( MountableFile.forClasspathResource("/grafana-dashboard-quarkus-micrometer-prometheus.json"), - "/otel-lgtm/grafana-dashboard-red-metrics-classic.json"); + "/otel-lgtm/grafana-dashboard-quarkus-micrometer-prometheus.json"); withCopyFileToContainer( MountableFile.forClasspathResource("/grafana-dashboard-quarkus-micrometer-otlp.json"), - "/otel-lgtm/grafana-dashboard-red-metrics-native.json"); - withCopyFileToContainer( - MountableFile.forClasspathResource("/empty.json"), - "/otel-lgtm/grafana-dashboard-jvm-metrics.json"); + "/otel-lgtm/grafana-dashboard-quarkus-micrometer-otlp.json"); addFileToContainer(getPrometheusConfig().getBytes(), "/otel-lgtm/prometheus.yaml"); } @@ -82,7 +119,7 @@ private String getPrometheusConfig() { Config runtimeConfig = ConfigProvider.getConfig(); String rootPath = runtimeConfig.getOptionalValue("quarkus.management.root-path", String.class).orElse("/q"); String metricsPath = runtimeConfig.getOptionalValue("quarkus.management.metrics.path", String.class).orElse("/metrics"); - int httpPort = runtimeConfig.getOptionalValue("quarkus.http.port", Integer.class).orElse(0); + int httpPort = runtimeConfig.getOptionalValue("quarkus.http.port", Integer.class).orElse(8080); // when not set use default return String.format(PROMETHEUS_CONFIG, config.serviceName(), rootPath, metricsPath, "host.docker.internal", httpPort); } diff --git a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/ObservabilityContainer.java b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/ObservabilityContainer.java index f47f85c98fd43..a3e6c6610890f 100644 --- a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/ObservabilityContainer.java +++ b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/ObservabilityContainer.java @@ -51,7 +51,7 @@ protected byte[] getResourceAsBytes(String resource) { @SuppressWarnings("OctalInteger") protected void addFileToContainer(byte[] content, String pathInContainer) { - log.infof("Content [%s]: \n%s", pathInContainer, new String(content, StandardCharsets.UTF_8)); + log.debugf("Content [%s]: \n%s", pathInContainer, new String(content, StandardCharsets.UTF_8)); withCopyToContainer(Transferable.of(content, 0777), pathInContainer); } From 4ad58444011545f826335b96a5b13590ed1b3b0c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 16 Jan 2025 13:08:19 +0000 Subject: [PATCH 10/50] Getting Started with Security updates (cherry picked from commit 9e7926541ad8ac49ba6ed26201c77e915c8e48aa) --- .../security-getting-started-tutorial.adoc | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/docs/src/main/asciidoc/security-getting-started-tutorial.adoc b/docs/src/main/asciidoc/security-getting-started-tutorial.adoc index 6b07d7c9c1f9c..c4652f465ae79 100644 --- a/docs/src/main/asciidoc/security-getting-started-tutorial.adoc +++ b/docs/src/main/asciidoc/security-getting-started-tutorial.adoc @@ -324,13 +324,16 @@ For example: ---- quarkus.http.auth.basic=true -quarkus.datasource.db-kind=postgresql -quarkus.datasource.username=quarkus -quarkus.datasource.password=quarkus -quarkus.datasource.jdbc.url=jdbc:postgresql:security_jpa +%prod.quarkus.datasource.db-kind=postgresql +%prod.quarkus.datasource.username=quarkus +%prod.quarkus.datasource.password=quarkus +%prod.quarkus.datasource.jdbc.url=jdbc:postgresql:security_jpa quarkus.hibernate-orm.database.generation=drop-and-create ---- + +By adding the `%prod.` profile prefix, you ensure that the data source properties are only observed by an application running in production mode. + ==== + . To initialize the database with users and roles, implement the `Startup` class, as outlined in the following code snippet: @@ -384,7 +387,7 @@ In a production environment, do not store plain text passwords. As a result, the `quarkus-security-jpa` defaults to using bcrypt-hashed passwords. ==== -== Test your application by using Dev Services for PostgreSQL +== Test your application in dev mode by using Dev Services for PostgreSQL Complete the integration testing of your application in JVM and native modes by using xref:dev-services.adoc#databases[Dev Services for PostgreSQL] before you run your application in production mode. @@ -411,21 +414,8 @@ To run your application in dev mode: include::{includes}/devtools/dev.adoc[] -The following properties configuration demonstrates how to enable PostgreSQL testing to run only in production (`prod`) mode. In this scenario, `Dev Services for PostgreSQL` launches and configures a `PostgreSQL` test container. -[source,properties] ----- -%prod.quarkus.datasource.db-kind=postgresql -%prod.quarkus.datasource.username=quarkus -%prod.quarkus.datasource.password=quarkus -%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus - -quarkus.hibernate-orm.database.generation=drop-and-create ----- - -If you add the `%prod.` profile prefix, data source properties are not visible to `Dev Services for PostgreSQL` and are only observed by an application running in production mode. - To write the integration test, use the following code sample: [source,java] @@ -503,7 +493,7 @@ While developing your application, you can add and run tests individually by usi Dev Services for PostgreSQL supports testing while you develop by providing a separate PostgreSQL test container that does not conflict with the dev mode container. ==== -== Test your application using Curl or browser +== Test your application in production mode by using Curl or browser To test your application using Curl or the browser, you must first start a PostgreSQL server, then compile and run your application either in JVM or native mode. @@ -513,7 +503,7 @@ To test your application using Curl or the browser, you must first start a Postg ---- docker run --rm=true --name security-getting-started -e POSTGRES_USER=quarkus \ -e POSTGRES_PASSWORD=quarkus -e POSTGRES_DB=quarkus \ - -p 5432:5432 postgres:14.1 + -p 5432:5432 postgres:17 ---- === Compile and run the application From d5a946d4519755f0df867642ee3a7cb85b5bf4f6 Mon Sep 17 00:00:00 2001 From: brunobat Date: Thu, 16 Jan 2025 16:10:26 +0000 Subject: [PATCH 11/50] Fix flaky otel quickstart test (cherry picked from commit d605c315847cc60f340b272f64519eb059c29de8) --- .../OpenTelemetryDisabledTest.java | 19 ++++++++++++++++++- .../it/opentelemetry/OpenTelemetryTest.java | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/integration-tests/opentelemetry-quickstart/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryDisabledTest.java b/integration-tests/opentelemetry-quickstart/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryDisabledTest.java index 92a8e66aa8537..fb2e5a34c0364 100644 --- a/integration-tests/opentelemetry-quickstart/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryDisabledTest.java +++ b/integration-tests/opentelemetry-quickstart/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryDisabledTest.java @@ -2,12 +2,16 @@ import static io.restassured.RestAssured.get; import static io.restassured.RestAssured.given; +import static java.net.HttpURLConnection.HTTP_OK; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.awaitility.Awaitility.await; import static org.hamcrest.CoreMatchers.is; +import java.util.List; import java.util.Map; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; @@ -18,6 +22,19 @@ @TestProfile(OpenTelemetryDisabledTest.MyProfile.class) public class OpenTelemetryDisabledTest extends BaseTest { + @BeforeEach + void reset() { + await().atMost(5, SECONDS).until(() -> { + List> spans = getSpans(); + if (spans.size() == 0) { + return true; + } else { + given().get("/reset").then().statusCode(HTTP_OK); + return false; + } + }); + } + @Test void buildTimeDisabled() { given() @@ -26,7 +43,7 @@ void buildTimeDisabled() { .statusCode(200) .body(is("Hello from Quarkus REST")); // Service will start nevertheless. - await().atMost(200, MILLISECONDS).until(() -> getSpans().size() == 0); + await().atMost(300, MILLISECONDS).until(() -> getSpans().size() == 0); } public static class MyProfile implements QuarkusTestProfile { diff --git a/integration-tests/opentelemetry-quickstart/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryTest.java b/integration-tests/opentelemetry-quickstart/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryTest.java index e4963ec759d1e..746a31747db2b 100644 --- a/integration-tests/opentelemetry-quickstart/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryTest.java +++ b/integration-tests/opentelemetry-quickstart/src/test/java/io/quarkus/it/opentelemetry/OpenTelemetryTest.java @@ -2,16 +2,35 @@ import static io.restassured.RestAssured.get; import static io.restassured.RestAssured.given; +import static java.net.HttpURLConnection.HTTP_OK; import static java.util.concurrent.TimeUnit.SECONDS; import static org.awaitility.Awaitility.await; import static org.hamcrest.CoreMatchers.is; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest public class OpenTelemetryTest extends BaseTest { + + @BeforeEach + void reset() { + await().atMost(5, SECONDS).until(() -> { + List> spans = getSpans(); + if (spans.size() == 0) { + return true; + } else { + given().get("/reset").then().statusCode(HTTP_OK); + return false; + } + }); + } + @Test void buildTimeEnabled() { given() From 2effce01699c7121d096f4f189ce6fe1f801d0a3 Mon Sep 17 00:00:00 2001 From: brunobat Date: Thu, 16 Jan 2025 11:04:32 +0000 Subject: [PATCH 12/50] Update grafana image and remove dashboard hack Fix Otel logging message formating (cherry picked from commit 81f3e206539b9993fe31b6c4cbca7f954c182558) --- .../deployment/logs/OtelLoggingTest.java | 84 +++++++++++++++++++ .../runtime/logs/OpenTelemetryLogHandler.java | 11 ++- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/logs/OtelLoggingTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/logs/OtelLoggingTest.java index 7099ab11bf565..e5c07c0eb9a9d 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/logs/OtelLoggingTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/logs/OtelLoggingTest.java @@ -172,6 +172,80 @@ public void testException() { .doesNotContainKey("sampled")); } + @Test + public void testLogFormatingData() { + final String message = "Replacement string"; + final String expected = "infof " + message; + assertEquals("hello " + message, jBossLoggingBean.helloLogFormating(message)); + + List finishedLogRecordItems = logRecordExporter.getFinishedLogRecordItemsAtLeast(1); + LogRecordData last = finishedLogRecordItems.get(finishedLogRecordItems.size() - 1); + + assertThat(last.getSpanContext().getSpanId()).isEqualTo("0000000000000000"); + assertThat(last.getSpanContext().getTraceId()).isEqualTo("00000000000000000000000000000000"); + assertThat(last.getSpanContext().getTraceFlags().asHex()).isEqualTo("00"); + assertThat(last.getTimestampEpochNanos()).isNotNull().isLessThan(System.currentTimeMillis() * 1_000_000); + + assertThat(last) + .hasSeverity(Severity.INFO) + .hasSeverityText("INFO") + .hasBody(expected) + .hasAttributesSatisfying( + attributes -> assertThat(attributes) + .containsEntry(CODE_NAMESPACE.getKey(), + "io.quarkus.opentelemetry.deployment.logs.OtelLoggingTest$JBossLoggingBean") + .containsEntry(CODE_FUNCTION.getKey(), "helloLogFormating") + .containsEntry(THREAD_NAME.getKey(), Thread.currentThread().getName()) + .containsEntry(THREAD_ID.getKey(), Thread.currentThread().getId()) + .containsEntry("log.logger.namespace", "org.jboss.logging.Logger") + .containsKey(CODE_LINENO.getKey()) + .doesNotContainKey(EXCEPTION_TYPE) + .doesNotContainKey(EXCEPTION_MESSAGE) + .doesNotContainKey(EXCEPTION_STACKTRACE) + .doesNotContainKey(LOG_FILE_PATH) + // attributed do not duplicate tracing data + .doesNotContainKey("spanId") + .doesNotContainKey("traceId") + .doesNotContainKey("sampled")); + } + + @Test + public void testLogParameterValue() { + final String message = "Replacement parameter value"; + final String expected = "infov " + message; + assertEquals("hello " + message, jBossLoggingBean.helloLogParameterValue(message)); + + List finishedLogRecordItems = logRecordExporter.getFinishedLogRecordItemsAtLeast(1); + LogRecordData last = finishedLogRecordItems.get(finishedLogRecordItems.size() - 1); + + assertThat(last.getSpanContext().getSpanId()).isEqualTo("0000000000000000"); + assertThat(last.getSpanContext().getTraceId()).isEqualTo("00000000000000000000000000000000"); + assertThat(last.getSpanContext().getTraceFlags().asHex()).isEqualTo("00"); + assertThat(last.getTimestampEpochNanos()).isNotNull().isLessThan(System.currentTimeMillis() * 1_000_000); + + assertThat(last) + .hasSeverity(Severity.INFO) + .hasSeverityText("INFO") + .hasBody(expected) + .hasAttributesSatisfying( + attributes -> assertThat(attributes) + .containsEntry(CODE_NAMESPACE.getKey(), + "io.quarkus.opentelemetry.deployment.logs.OtelLoggingTest$JBossLoggingBean") + .containsEntry(CODE_FUNCTION.getKey(), "helloLogParameterValue") + .containsEntry(THREAD_NAME.getKey(), Thread.currentThread().getName()) + .containsEntry(THREAD_ID.getKey(), Thread.currentThread().getId()) + .containsEntry("log.logger.namespace", "org.jboss.logging.Logger") + .containsKey(CODE_LINENO.getKey()) + .doesNotContainKey(EXCEPTION_TYPE) + .doesNotContainKey(EXCEPTION_MESSAGE) + .doesNotContainKey(EXCEPTION_STACKTRACE) + .doesNotContainKey(LOG_FILE_PATH) + // attributed do not duplicate tracing data + .doesNotContainKey("spanId") + .doesNotContainKey("traceId") + .doesNotContainKey("sampled")); + } + private String extractStackTrace(final Throwable throwable) { try (StringWriter sw = new StringWriter(1024); PrintWriter pw = new PrintWriter(sw)) { throwable.printStackTrace(pw); @@ -201,5 +275,15 @@ public boolean logException(final Throwable throwable) { LOG.error("logging an exception", throwable); return true; } + + public String helloLogFormating(final String replacement) { + LOG.infof("infof %s", replacement); + return "hello " + replacement; + } + + public String helloLogParameterValue(final String replacement) { + LOG.infov("infov {0}", replacement); + return "hello " + replacement; + } } } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/logs/OpenTelemetryLogHandler.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/logs/OpenTelemetryLogHandler.java index 65e2de1270e70..382ea4af501e8 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/logs/OpenTelemetryLogHandler.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/logs/OpenTelemetryLogHandler.java @@ -13,6 +13,7 @@ import java.time.Instant; import java.util.Map; import java.util.Optional; +import java.util.logging.Formatter; import java.util.logging.Level; import org.eclipse.microprofile.config.Config; @@ -52,7 +53,15 @@ protected void doPublish(ExtLogRecord record) { } if (record.getMessage() != null) { - logRecordBuilder.setBody(record.getMessage()); + // Get the message + final Formatter formatter = getFormatter(); + String logMsg; + if (formatter != null) { + logMsg = formatter.format(record); + } else { + logMsg = record.getFormattedMessage(); + } + logRecordBuilder.setBody(logMsg); } final AttributesBuilder attributes = Attributes.builder(); From 60bc7f9a98e8b35c8eb934a9c53c682d6e47781b Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 16 Jan 2025 17:41:57 +0100 Subject: [PATCH 13/50] Mark quarkus-oidc-client-registration as experimental in doc It's marked as experimental in the metadata of the extension but not in the documentation. (cherry picked from commit 7f6a0465fa301dde7ece1cae3d8f205110e07b87) --- .../asciidoc/security-openid-connect-client-registration.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/src/main/asciidoc/security-openid-connect-client-registration.adoc b/docs/src/main/asciidoc/security-openid-connect-client-registration.adoc index 71035c4af5812..1a90ca07d6234 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-registration.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-registration.adoc @@ -9,6 +9,9 @@ include::_attributes.adoc[] :categories: security :topics: security,oidc,client :extensions: io.quarkus:quarkus-oidc-client-registration +:extension-status: experimental + +include::{includes}/extension-status.adoc[] Typically, you have to register an OIDC client (application) manually in your OIDC provider's dashboard. During this process, you specify the human readable application name, allowed redirect and post logout URLs, and other properties. From 562e3fbc50c2f7da7459a6a8ca56024f06bfdbea Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 16 Jan 2025 16:39:08 +0000 Subject: [PATCH 14/50] Update to HTTP error outputs in getting Started with Security (cherry picked from commit 5aa963ba2e678e86e23b21de6a9793038b48c843) --- .../main/asciidoc/security-getting-started-tutorial.adoc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/src/main/asciidoc/security-getting-started-tutorial.adoc b/docs/src/main/asciidoc/security-getting-started-tutorial.adoc index c4652f465ae79..72fee1f4e9401 100644 --- a/docs/src/main/asciidoc/security-getting-started-tutorial.adoc +++ b/docs/src/main/asciidoc/security-getting-started-tutorial.adoc @@ -566,11 +566,7 @@ public $ curl -i -X GET http://localhost:8080/api/admin HTTP/1.1 401 Unauthorized -Content-Length: 14 -Content-Type: text/html;charset=UTF-8 WWW-Authenticate: Basic - -Not authorized ---- ==== * Connect to a protected endpoint as an authorized user: @@ -606,10 +602,6 @@ If a resource is protected with `@RolesAllowed("user")`, the user `admin` is not $ curl -i -X GET -u admin:admin http://localhost:8080/api/users/me HTTP/1.1 403 Forbidden -Content-Length: 34 -Content-Type: text/html;charset=UTF-8 - -Forbidden ---- Finally, the user named `user` is authorized, and the security context contains the principal details, for example, the username. From 16c0ed76200eb9b731aef7fb31ab8c352aeeeeb9 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 16 Jan 2025 09:52:57 +0100 Subject: [PATCH 15/50] Gradle - Correctly pass platform.quarkus.* properties Fixes #45472 (cherry picked from commit 2ed6293ec0cd5000f3789dd6e0b790b104c24267) --- .../io/quarkus/gradle/tasks/AbstractQuarkusExtension.java | 4 ++-- .../main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java | 3 +++ .../src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java index 58976d97f65a9..e717ca883d7da 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java @@ -80,7 +80,7 @@ private BaseConfig buildBaseConfig() { // Used to handle the (deprecated) buildNative and testNative tasks. project.getExtensions().getExtraProperties().getProperties().forEach((k, v) -> { - if (k.startsWith("quarkus.")) { + if (k.startsWith("quarkus.") || k.startsWith("platform.quarkus.")) { forcedPropertiesProperty.put(k, v.toString()); } }); @@ -126,7 +126,7 @@ protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArti // Used to handle the (deprecated) buildNative and testNative tasks. project.getExtensions().getExtraProperties().getProperties().forEach((k, v) -> { - if (k.startsWith("quarkus.")) { + if (k.startsWith("quarkus.") || k.startsWith("platform.quarkus.")) { forcedPropertiesProperty.put(k, v.toString()); } }); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java index f5f75c86681ed..c6d4f6b160d3b 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java @@ -251,6 +251,9 @@ void generateBuild() { for (String key : config.getMapKeys("quarkus").values()) { values.put(key, config.getConfigValue(key).getValue()); } + for (String key : config.getMapKeys("platform.quarkus").values()) { + values.put(key, config.getConfigValue(key).getValue()); + } return values; }); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java index 17b4822d9e923..11060269f5e54 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java @@ -19,7 +19,7 @@ import io.quarkus.utilities.OS; public abstract class QuarkusTask extends DefaultTask { - private static final List WORKER_BUILD_FORK_OPTIONS = List.of("quarkus."); + private static final List WORKER_BUILD_FORK_OPTIONS = List.of("quarkus.", "platform.quarkus."); private final transient QuarkusPluginExtension extension; protected final File projectDir; From 581f2f7d9d745769ad77333026aaf33c5cc541f6 Mon Sep 17 00:00:00 2001 From: Holly Cummins Date: Thu, 16 Jan 2025 15:29:36 +0000 Subject: [PATCH 16/50] Make sure to delegate to instance variable for releasing config of a classloader (cherry picked from commit c1c3bdf5b77777b9cbd3e48809c78db043516517) --- .../io/quarkus/test/config/TestConfigProviderResolver.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java index 9bda00bace7f1..13416d0ad087f 100644 --- a/test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java +++ b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java @@ -19,6 +19,8 @@ * classloader. */ public class TestConfigProviderResolver extends SmallRyeConfigProviderResolver { + + // Note that this class both *extends* and *consumes* SmallRyeConfigProviderResolver. Every method in SmallRyeConfigProviderResolver should be replicated here with a delegation to the instance variable, or there will be subtle and horrible bugs. private final SmallRyeConfigProviderResolver resolver; private final ClassLoader classLoader; private final Map configs; @@ -96,4 +98,9 @@ public void registerConfig(final Config config, final ClassLoader classLoader) { public void releaseConfig(final Config config) { resolver.releaseConfig(config); } + + @Override + public void releaseConfig(final ClassLoader classLoader) { + resolver.releaseConfig(classLoader); + } } From 8240476d563cff6966676c4bd528888fc8e0dcc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damien=20Cl=C3=A9ment=20d=27Huart?= Date: Fri, 17 Jan 2025 09:08:45 +0100 Subject: [PATCH 17/50] Update drools.adoc An IT is based on RestAssured only. The name should end by Test. (cherry picked from commit 7aa12cbe3832453f2c440e6ebf21ebaff532ad1a) --- docs/src/main/asciidoc/drools.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/drools.adoc b/docs/src/main/asciidoc/drools.adoc index 02a3f46bfcc68..80a2d970481a3 100644 --- a/docs/src/main/asciidoc/drools.adoc +++ b/docs/src/main/asciidoc/drools.adoc @@ -509,7 +509,7 @@ We can create a standard Quarkus and JUnit test to check the behaviour of the Ru package org.drools.quarkus.quickstart.test; @QuarkusTest -public class RuntimeIT { +public class RuntimeTest { @Inject RuleUnit ruleUnit; From 648777c5a0e6ccf3c9d20d63f9d15f485b34bb26 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 17 Jan 2025 10:28:10 +0100 Subject: [PATCH 18/50] Add missing @ConfigItem to FilterConfig Fixes #45676 (cherry picked from commit 6adfaff5ea854cce7ccae90b6c24879caa726cbd) --- .../main/java/io/quarkus/vertx/http/runtime/FilterConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/FilterConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/FilterConfig.java index e7cfbf4aa50cc..e7f85fece611e 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/FilterConfig.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/FilterConfig.java @@ -34,5 +34,6 @@ public class FilterConfig { /** * Order in which this path config is applied. Higher priority takes precedence */ + @ConfigItem public OptionalInt order; } From b4fdc4720fb829a22e48ab51c5b26e9538073716 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 27 Dec 2024 16:12:38 +0100 Subject: [PATCH 19/50] Do not prefix URL when URL is already absolute Management URLs were prefixed twice when absolute: http://localhost:9000http://localhost:9000/q/health Which was defeating the logic removing the host when collecting suppressed URIs. Fixes #36510 (cherry picked from commit 5da616bbc1a25c3dfcbeef057ed259e8c06ee24f) --- .../vertx/http/deployment/devmode/ConfiguredPathInfo.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/ConfiguredPathInfo.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/ConfiguredPathInfo.java index a2cceab2f5cee..bb93a19b566b2 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/ConfiguredPathInfo.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/devmode/ConfiguredPathInfo.java @@ -33,12 +33,12 @@ public String getEndpointPath(HttpRootPathBuildItem httpRoot) { public String getEndpointPath(NonApplicationRootPathBuildItem nonAppRoot, ManagementInterfaceBuildTimeConfig mibt, LaunchModeBuildItem mode) { + if (absolutePath) { + return endpointPath; + } if (management && mibt.enabled) { var prefix = NonApplicationRootPathBuildItem.getManagementUrlPrefix(mode); return prefix + endpointPath; - } - if (absolutePath) { - return endpointPath; } else { return TemplateHtmlBuilder.adjustRoot(nonAppRoot.getNormalizedHttpRootPath(), endpointPath); } From 0214f2dbaab052c3a481e3febe93153c53aeab1d Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 10 Jan 2025 15:16:52 +0100 Subject: [PATCH 20/50] Identify non application routes with a name The name is used in the metrics if defined and thus we can pass a fully qualifier path for the route. (cherry picked from commit c377d4abb02ff0b27dcbf905689c9d49612b77aa) --- .../NonApplicationRootPathBuildItem.java | 9 +++++- .../vertx/http/deployment/RouteBuildItem.java | 14 +++++++++ .../vertx/http/runtime/BasicRoute.java | 31 +++++++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/NonApplicationRootPathBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/NonApplicationRootPathBuildItem.java index 0dc4b5e1f280c..8f0b9f91369a4 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/NonApplicationRootPathBuildItem.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/NonApplicationRootPathBuildItem.java @@ -270,6 +270,7 @@ public static class Builder extends RouteBuildItem.Builder { private final NonApplicationRootPathBuildItem buildItem; private RouteBuildItem.RouteType routeType = RouteBuildItem.RouteType.FRAMEWORK_ROUTE; private RouteBuildItem.RouteType routerType = RouteBuildItem.RouteType.FRAMEWORK_ROUTE; + private String name; private String path; Builder(NonApplicationRootPathBuildItem buildItem) { @@ -330,7 +331,13 @@ public Builder orderedRoute(String route, Integer order, Consumer routeFu this.path = route; this.routerType = RouteBuildItem.RouteType.ABSOLUTE_ROUTE; } - super.orderedRoute(this.path, order, routeFunction); + + // we normalize the route name to remove trailing *, this is to be consistent with the path + // see RouteImpl#setPath() + String routeName = route.charAt(route.length() - 1) == '*' ? route.substring(0, route.length() - 1) : route; + + // we pass a route name for proper identification in the metrics + super.orderedRoute(routeName, this.path, order, routeFunction); return this; } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RouteBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RouteBuildItem.java index 7156e0db32b43..c4cf342638e4a 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RouteBuildItem.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RouteBuildItem.java @@ -176,6 +176,20 @@ public Builder orderedRoute(String route, Integer order, Consumer routeCu return this; } + /** + * @param name The name of the route. It is used to identify the route in the metrics. + * @param route A normalized path used to define a basic route + * (e.g. use HttpRootPathBuildItem to construct/resolve the path value). This path this is also + * used on the "Not Found" page in dev mode. + * @param order Priority ordering of the route + * @param routeCustomizer Route customizer. + */ + public Builder orderedRoute(String name, String route, Integer order, Consumer routeCustomizer) { + this.routeFunction = new BasicRoute(name, route, order, routeCustomizer); + this.notFoundPagePath = this.routePath = route; + return this; + } + public Builder handler(Handler handler) { this.handler = handler; return this; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/BasicRoute.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/BasicRoute.java index 064145e894d45..bd5ec80d091a8 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/BasicRoute.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/BasicRoute.java @@ -8,6 +8,8 @@ public class BasicRoute implements Function { + private String name; + private String path; private Integer order; @@ -15,15 +17,29 @@ public class BasicRoute implements Function { private Consumer customizer; public BasicRoute(String path) { - this(path, null); + this(null, path); } public BasicRoute(String path, Integer order) { + this(null, path, order); + } + + public BasicRoute(String path, Integer order, Consumer customizer) { + this(null, path, order, customizer); + } + + public BasicRoute(String name, String path) { + this(name, path, null); + } + + public BasicRoute(String name, String path, Integer order) { + this.name = name; this.path = path; this.order = order; } - public BasicRoute(String path, Integer order, Consumer customizer) { + public BasicRoute(String name, String path, Integer order, Consumer customizer) { + this.name = name; this.path = path; this.order = order; this.customizer = customizer; @@ -32,6 +48,14 @@ public BasicRoute(String path, Integer order, Consumer customizer) { public BasicRoute() { } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + public String getPath() { return path; } @@ -60,6 +84,9 @@ public BasicRoute setCustomizer(Consumer customizer) { @Override public Route apply(Router router) { Route route = router.route(path); + if (name != null) { + route.setName(name); + } if (order != null) { route.order(order); } From 64156ec843b0e9b53a1b6d12c852666a5c137af6 Mon Sep 17 00:00:00 2001 From: brunobat Date: Wed, 15 Jan 2025 11:25:13 +0000 Subject: [PATCH 21/50] Test for non app uris (cherry picked from commit 9f806c0b7f25aa79f8a70fdce67d66e30d7a90bf) --- extensions/opentelemetry/deployment/pom.xml | 5 + .../OpenTelemetrySuppressNonAppUriTest.java | 103 ++++++++++++++++++ .../common/exporter/TestSpanExporter.java | 19 +++- 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriTest.java diff --git a/extensions/opentelemetry/deployment/pom.xml b/extensions/opentelemetry/deployment/pom.xml index cac0090542ced..3105248374ae8 100644 --- a/extensions/opentelemetry/deployment/pom.xml +++ b/extensions/opentelemetry/deployment/pom.xml @@ -140,6 +140,11 @@ quarkus-security-deployment test + + io.quarkus + quarkus-vertx-http-dev-ui-tests + test + io.opentelemetry opentelemetry-sdk-testing diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriTest.java new file mode 100644 index 0000000000000..6a6e314aa3f48 --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriTest.java @@ -0,0 +1,103 @@ +package io.quarkus.opentelemetry.deployment; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.exporter.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.exporter.TestSpanExporterProvider; +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; + +public class OpenTelemetrySuppressNonAppUriTest { + + @RegisterExtension + final static QuarkusDevModeTest TEST = new QuarkusDevModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloResource.class, TestSpanExporter.class, TestSpanExporterProvider.class) + .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .add(new StringAsset( + """ + quarkus.otel.traces.exporter=test-span-exporter + quarkus.otel.metrics.exporter=none + quarkus.otel.bsp.export.timeout=1s + quarkus.otel.bsp.schedule.delay=50 + """), + "application.properties")); + + @Test + void test() { + + // Must not be traced + RestAssured.given() + .get("/q/health/") + .then() + .statusCode(200); + RestAssured.given() + .get("/q/dev-ui") + .then() + .statusCode(200); + RestAssured.given() + .get("/q/dev-ui/icon/font-awesome.js") + .then() + .statusCode(200); + // Valid trace + RestAssured.given() + .get("/hello") + .then() + .statusCode(200); + // Get span names + List spans = Arrays.asList( + RestAssured.given() + .get("/hello/spans") + .then() + .statusCode(200) + .extract().body() + .asString() + .split(";")); + + assertThat(spans.size()) + .withFailMessage("Expected only one span but found: " + spans) + .isEqualTo(1); + + assertThat(spans).contains("GET /hello"); + } + + @Path("/hello") + public static class HelloResource { + + @Inject + TestSpanExporter spanExporter; + + @GET + public String greetings() { + return "Hello test"; + } + + /** + * Gets a string with the received spans names concatenated by ; + * + * @return + */ + @GET + @Path("/spans") + public String greetingsInsertAtLeast() { + String spanNames = spanExporter.getFinishedSpanItemsAtLeast(1).stream() + .map(SpanData::getName) + .reduce((s1, s2) -> s1 + ";" + s2).orElse(""); + System.out.println(spanNames); + return spanNames; + } + } +} diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/exporter/TestSpanExporter.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/exporter/TestSpanExporter.java index 573816c3ed9d0..c55856ea52c1e 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/exporter/TestSpanExporter.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/exporter/TestSpanExporter.java @@ -4,6 +4,7 @@ import static java.util.stream.Collectors.toList; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collection; import java.util.List; @@ -50,11 +51,27 @@ public List getFinishedSpanItems(int spanCount) { return finishedSpanItems.stream().collect(toList()); } + /** + * Careful when retrieving the list of finished spans. There is a chance when the response is already sent to the + * client and Vert.x still writing the end of the spans. This means that a response is available to assert from the + * test side but not all spans may be available yet. For this reason, this method requires the number of expected + * spans. + */ + public List getFinishedSpanItemsAtLeast(int spanCount) { + assertSpanAtLeast(spanCount); + return finishedSpanItems; + } + public void assertSpanCount(int spanCount) { - await().atMost(30, SECONDS).untilAsserted( + await().atMost(5, SECONDS).untilAsserted( () -> assertEquals(spanCount, finishedSpanItems.size(), "Spans: " + finishedSpanItems.toString())); } + public void assertSpanAtLeast(int spanCount) { + await().atMost(5, SECONDS).untilAsserted( + () -> assertTrue(spanCount < finishedSpanItems.size(), "Spans: " + finishedSpanItems.toString())); + } + public void reset() { finishedSpanItems.clear(); } From dbb78f3d4d531e987485dfff486a8bb66ff07003 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 16 Jan 2025 11:04:57 +0100 Subject: [PATCH 22/50] Avoid duplicates in FrameworkEndpointsBuildItem (cherry picked from commit ac983ac86ee9c801079a5f9f5f3e1701b2fadaab) --- .../http/deployment/spi/FrameworkEndpointsBuildItem.java | 8 ++++---- .../quarkus/vertx/http/deployment/VertxHttpProcessor.java | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/extensions/vertx-http/deployment-spi/src/main/java/io/quarkus/vertx/http/deployment/spi/FrameworkEndpointsBuildItem.java b/extensions/vertx-http/deployment-spi/src/main/java/io/quarkus/vertx/http/deployment/spi/FrameworkEndpointsBuildItem.java index 90c140b6d6aad..d13509e196b4a 100644 --- a/extensions/vertx-http/deployment-spi/src/main/java/io/quarkus/vertx/http/deployment/spi/FrameworkEndpointsBuildItem.java +++ b/extensions/vertx-http/deployment-spi/src/main/java/io/quarkus/vertx/http/deployment/spi/FrameworkEndpointsBuildItem.java @@ -1,17 +1,17 @@ package io.quarkus.vertx.http.deployment.spi; -import java.util.List; +import java.util.Set; import io.quarkus.builder.item.SimpleBuildItem; public final class FrameworkEndpointsBuildItem extends SimpleBuildItem { - private final List endpoints; + private final Set endpoints; - public FrameworkEndpointsBuildItem(final List endpoints) { + public FrameworkEndpointsBuildItem(final Set endpoints) { this.endpoints = endpoints; } - public List getEndpoints() { + public Set getEndpoints() { return endpoints; } } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java index 0b6349b91db9a..fc70a7c4eafd3 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java @@ -8,8 +8,10 @@ import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.SubmissionPublisher; import java.util.logging.Level; import java.util.stream.Collectors; @@ -131,7 +133,7 @@ NonApplicationRootPathBuildItem frameworkRoot(HttpBuildTimeConfig httpBuildTimeC FrameworkEndpointsBuildItem frameworkEndpoints(NonApplicationRootPathBuildItem nonApplicationRootPath, ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig, LaunchModeBuildItem launchModeBuildItem, List routes) { - List frameworkEndpoints = new ArrayList<>(); + Set frameworkEndpoints = new HashSet<>(); for (RouteBuildItem route : routes) { if (FRAMEWORK_ROUTE.equals(route.getRouteType())) { if (route.getConfiguredPathInfo() != null) { From aec727ad9ba4e81b6d6e3b027c01312dd8aa1772 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 16 Jan 2025 11:11:24 +0100 Subject: [PATCH 23/50] Simplify OpenTelemetrySuppressNonAppUriTest a bit (cherry picked from commit 7c3e93a35e5de16aa995831be52f67bf677d62e8) --- .../deployment/OpenTelemetrySuppressNonAppUriTest.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriTest.java index 6a6e314aa3f48..8c685a11b9568 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriTest.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriTest.java @@ -45,7 +45,7 @@ void test() { .then() .statusCode(200); RestAssured.given() - .get("/q/dev-ui") + .get("/q/dev-ui/") .then() .statusCode(200); RestAssured.given() @@ -67,11 +67,7 @@ void test() { .asString() .split(";")); - assertThat(spans.size()) - .withFailMessage("Expected only one span but found: " + spans) - .isEqualTo(1); - - assertThat(spans).contains("GET /hello"); + assertThat(spans).containsExactly("GET /hello"); } @Path("/hello") From 51095df8ec572bf672bb059ea568900ef854633c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 16 Jan 2025 13:44:33 +0100 Subject: [PATCH 24/50] Use an HashSet contract for DropTargetsSamplerTest It's a detail but it's the contract we want and matching will be faster. (cherry picked from commit 9cd2b132cec1637d0dae7cbe60fe74f90ff5d07b) --- .../AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java | 5 +++-- .../opentelemetry/runtime/tracing/DropTargetsSampler.java | 5 +++-- .../runtime/tracing/DropTargetsSamplerTest.java | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java index cbfc7d6e2d79b..3706eb9fd5c73 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/AutoConfiguredOpenTelemetrySdkBuilderCustomizer.java @@ -5,8 +5,9 @@ import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; @@ -171,7 +172,7 @@ public Sampler apply(Sampler existingSampler, ConfigProperties configProperties) .orElse(existingSampler); //collect default filtering targets (Needed for all samplers) - List dropTargets = new ArrayList<>(); + Set dropTargets = new HashSet<>(); if (oTelRuntimeConfig.traces().suppressNonApplicationUris()) {//default is true dropTargets.addAll(TracerRecorder.dropNonApplicationUriTargets); } diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java index f42b26fbaa9e8..e327e0e5efba0 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java @@ -4,6 +4,7 @@ import static io.opentelemetry.semconv.UrlAttributes.URL_QUERY; import java.util.List; +import java.util.Set; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; @@ -14,10 +15,10 @@ public class DropTargetsSampler implements Sampler { private final Sampler sampler; - private final List dropTargets; + private final Set dropTargets; public DropTargetsSampler(final Sampler sampler, - final List dropTargets) { + final Set dropTargets) { this.sampler = sampler; this.dropTargets = dropTargets; } diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java index c2aae5f318f7a..7997cde9b18ad 100644 --- a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java +++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java @@ -1,10 +1,10 @@ package io.quarkus.opentelemetry.runtime.tracing; import static io.opentelemetry.semconv.UrlAttributes.URL_PATH; -import static io.quarkus.opentelemetry.runtime.OpenTelemetryUtil.*; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Test; @@ -21,7 +21,7 @@ class DropTargetsSamplerTest { @Test void testDropTargets() { CountingSampler countingSampler = new CountingSampler(); - var sut = new DropTargetsSampler(countingSampler, List.of("/q/swagger-ui", "/q/swagger-ui*")); + var sut = new DropTargetsSampler(countingSampler, Set.of("/q/swagger-ui", "/q/swagger-ui*")); assertEquals(SamplingResult.recordAndSample(), getShouldSample(sut, "/other")); assertEquals(1, countingSampler.count.get()); From c0d81ef29643a52cbd6d62e4d605d28cf2e5741c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 16 Jan 2025 14:03:50 +0100 Subject: [PATCH 25/50] Let TestSpanExporter when we have at least X elements included The current condition is weird because we end up saying we want at least 1, and it only returns when there are 2. (cherry picked from commit 081c7b174d4de50fcf439fd928ea6159bddd6998) --- .../deployment/common/exporter/TestSpanExporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/exporter/TestSpanExporter.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/exporter/TestSpanExporter.java index c55856ea52c1e..7c0fbf5a3fbb5 100644 --- a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/exporter/TestSpanExporter.java +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/common/exporter/TestSpanExporter.java @@ -69,7 +69,7 @@ public void assertSpanCount(int spanCount) { public void assertSpanAtLeast(int spanCount) { await().atMost(5, SECONDS).untilAsserted( - () -> assertTrue(spanCount < finishedSpanItems.size(), "Spans: " + finishedSpanItems.toString())); + () -> assertTrue(spanCount <= finishedSpanItems.size(), "Spans: " + finishedSpanItems.toString())); } public void reset() { From 858681700ab8557995fe8df1bf1c196a07d1ca79 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 16 Jan 2025 14:12:52 +0100 Subject: [PATCH 26/50] Fix wildcard dropping in DropTargetsSampler While /q/dev-ui/* would exclude: - /q/dev-ui/ - /q/dev-ui/whatever it wouldn't exclude /q/dev-ui/whatever/whenever/, which was problematic. See additional tests. Note that I think this should be improved with a proper matcher that handles all our matching rules efficiently but for now, it will have to do. (cherry picked from commit 7615750541c405523f7de9de89f9e703124da10c) --- .../runtime/tracing/DropTargetsSampler.java | 26 +++++++++++------- .../tracing/DropTargetsSamplerTest.java | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java index e327e0e5efba0..9e4a718dbcda8 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java @@ -3,8 +3,10 @@ import static io.opentelemetry.semconv.UrlAttributes.URL_PATH; import static io.opentelemetry.semconv.UrlAttributes.URL_QUERY; +import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; @@ -15,12 +17,18 @@ public class DropTargetsSampler implements Sampler { private final Sampler sampler; - private final Set dropTargets; + private final Set dropTargetsExact; + private final Set dropTargetsWildcard; public DropTargetsSampler(final Sampler sampler, final Set dropTargets) { this.sampler = sampler; - this.dropTargets = dropTargets; + this.dropTargetsExact = dropTargets.stream().filter(s -> !s.endsWith("*")) + .collect(Collectors.toCollection(HashSet::new)); + this.dropTargetsWildcard = dropTargets.stream() + .filter(s -> s.endsWith("*")) + .map(s -> s.substring(0, s.length() - 1)) + .collect(Collectors.toCollection(HashSet::new)); } @Override @@ -49,26 +57,24 @@ private boolean shouldDrop(String target) { if ((target == null) || target.isEmpty()) { return false; } - if (safeContains(target)) { // check exact match + if (containsExactly(target)) { // check exact match return true; } if (target.charAt(target.length() - 1) == '/') { // check if the path without the ending slash matched - if (safeContains(target.substring(0, target.length() - 1))) { + if (containsExactly(target.substring(0, target.length() - 1))) { return true; } } - int lastSlashIndex = target.lastIndexOf('/'); - if (lastSlashIndex != -1) { - if (safeContains(target.substring(0, lastSlashIndex) + "*") - || safeContains(target.substring(0, lastSlashIndex) + "/*")) { // check if a wildcard matches + for (String dropTargetsWildcard : dropTargetsWildcard) { + if (target.startsWith(dropTargetsWildcard)) { return true; } } return false; } - private boolean safeContains(String target) { - return dropTargets.contains(target); + private boolean containsExactly(String target) { + return dropTargetsExact.contains(target); } @Override diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java index 7997cde9b18ad..db3896ed7ce2b 100644 --- a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java +++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java @@ -39,6 +39,33 @@ void testDropTargets() { assertEquals(2, countingSampler.count.get()); } + @Test + void testDropTargetsWildcards() { + CountingSampler countingSampler = new CountingSampler(); + var sut = new DropTargetsSampler(countingSampler, Set.of("/q/dev-ui", "/q/dev-ui/*")); + + assertEquals(SamplingResult.recordAndSample(), getShouldSample(sut, "/other")); + assertEquals(1, countingSampler.count.get()); + + assertEquals(SamplingResult.recordAndSample(), getShouldSample(sut, "/q/dev-ui-test")); + assertEquals(2, countingSampler.count.get()); + + assertEquals(SamplingResult.drop(), getShouldSample(sut, "/q/dev-ui")); + assertEquals(2, countingSampler.count.get()); + + assertEquals(SamplingResult.drop(), getShouldSample(sut, "/q/dev-ui/")); + assertEquals(2, countingSampler.count.get()); + + assertEquals(SamplingResult.drop(), getShouldSample(sut, "/q/dev-ui/whatever")); + assertEquals(2, countingSampler.count.get()); + + assertEquals(SamplingResult.drop(), getShouldSample(sut, "/q/dev-ui/whatever/wherever/whenever")); + assertEquals(2, countingSampler.count.get()); + + assertEquals(SamplingResult.recordAndSample(), getShouldSample(sut, "/q/test")); + assertEquals(3, countingSampler.count.get()); + } + private static SamplingResult getShouldSample(DropTargetsSampler sut, String target) { return sut.shouldSample(null, null, null, SpanKind.SERVER, Attributes.of(URL_PATH, target), null); From f1d1740cb562824bb6a389b7bd474f67fc183c6f Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 16 Jan 2025 14:22:39 +0100 Subject: [PATCH 27/50] Add a SuppressNonAppUri test when management interface is enabled (cherry picked from commit d76ade7630818bbda6fbbc82fd8260960d8968b1) --- ...pressNonAppUriManagementInterfaceTest.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriManagementInterfaceTest.java diff --git a/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriManagementInterfaceTest.java b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriManagementInterfaceTest.java new file mode 100644 index 0000000000000..f4d2edfeeeded --- /dev/null +++ b/extensions/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySuppressNonAppUriManagementInterfaceTest.java @@ -0,0 +1,101 @@ +package io.quarkus.opentelemetry.deployment; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.List; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.opentelemetry.deployment.common.exporter.TestSpanExporter; +import io.quarkus.opentelemetry.deployment.common.exporter.TestSpanExporterProvider; +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; + +public class OpenTelemetrySuppressNonAppUriManagementInterfaceTest { + + @RegisterExtension + final static QuarkusDevModeTest TEST = new QuarkusDevModeTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloResource.class, TestSpanExporter.class, TestSpanExporterProvider.class) + .addAsResource(new StringAsset(TestSpanExporterProvider.class.getCanonicalName()), + "META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider") + .add(new StringAsset( + """ + quarkus.otel.traces.exporter=test-span-exporter + quarkus.otel.metrics.exporter=none + quarkus.otel.bsp.export.timeout=1s + quarkus.otel.bsp.schedule.delay=50 + quarkus.management.enabled=true + quarkus.management.port=9001 + """), + "application.properties")); + + @Test + void test() { + + // Must not be traced + RestAssured.given() + .get("http://localhost:9001/q/health/") + .then() + .statusCode(200); + RestAssured.given() + .get("/q/dev-ui/") + .then() + .statusCode(200); + RestAssured.given() + .get("/q/dev-ui/icon/font-awesome.js") + .then() + .statusCode(200); + // Valid trace + RestAssured.given() + .get("/hello") + .then() + .statusCode(200); + // Get span names + List spans = Arrays.asList( + RestAssured.given() + .get("/hello/spans") + .then() + .statusCode(200) + .extract().body() + .asString() + .split(";")); + + assertThat(spans).containsExactly("GET /hello"); + } + + @Path("/hello") + public static class HelloResource { + + @Inject + TestSpanExporter spanExporter; + + @GET + public String greetings() { + return "Hello test"; + } + + /** + * Gets a string with the received spans names concatenated by ; + * + * @return + */ + @GET + @Path("/spans") + public String greetingsInsertAtLeast() { + String spanNames = spanExporter.getFinishedSpanItemsAtLeast(1).stream() + .map(SpanData::getName) + .reduce((s1, s2) -> s1 + ";" + s2).orElse(""); + System.out.println(spanNames); + return spanNames; + } + } +} From 1892b9144afeca2bf208bb715f4ca7c4f6e9791e Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 17 Jan 2025 14:46:15 +0100 Subject: [PATCH 28/50] Gradle - Resolve platform properties when possible We were always passing a <> value for the native builder which is a problem. We now correctly get the values from the platform properties when possible. (cherry picked from commit 17d805a8248c43227d0c5d47545b5e0cfec474a9) --- .../extension/QuarkusPluginExtension.java | 3 ++- .../tasks/AbstractQuarkusExtension.java | 4 +++- .../java/io/quarkus/gradle/tasks/Deploy.java | 3 ++- .../quarkus/gradle/tasks/EffectiveConfig.java | 19 ++++++++++++++++--- .../tasks/QuarkusBuildDependencies.java | 3 ++- .../gradle/tasks/QuarkusBuildTask.java | 3 ++- .../gradle/tasks/QuarkusGenerateCode.java | 3 ++- .../tasks/QuarkusPluginExtensionView.java | 3 ++- .../io/quarkus/gradle/tasks/QuarkusRun.java | 3 ++- .../tasks/QuarkusShowEffectiveConfig.java | 2 +- 10 files changed, 34 insertions(+), 12 deletions(-) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java index 7e45c652dbcf4..42af9ba6b15bb 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java @@ -74,7 +74,8 @@ public void beforeTest(Test task) { Map props = task.getSystemProperties(); ApplicationModel appModel = getApplicationModel(TEST); - SmallRyeConfig config = buildEffectiveConfiguration(appModel.getAppArtifact()).getConfig(); + SmallRyeConfig config = buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties()) + .getConfig(); config.getOptionalValue(TEST.getProfileKey(), String.class) .ifPresent(value -> props.put(TEST.getProfileKey(), value)); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java index e717ca883d7da..773194ad850d7 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java @@ -117,7 +117,8 @@ protected Manifest manifest() { return baseConfig().manifest(); } - protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArtifact) { + protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArtifact, + Map platformProperties) { Map properties = new HashMap<>(); exportCustomManifestProperties(properties); @@ -140,6 +141,7 @@ protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArti defaultProperties.putIfAbsent("quarkus.application.version", appArtifact.getVersion()); return EffectiveConfig.builder() + .withPlatformProperties(platformProperties) .withForcedProperties(forcedPropertiesProperty.get()) .withTaskProperties(properties) .withBuildProperties(quarkusBuildProperties.get()) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java index 4833c5be53da2..0670c80862004 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java @@ -91,7 +91,8 @@ public Deploy() { public void checkRequiredExtensions() { ApplicationModel appModel = resolveAppModelForBuild(); Properties sysProps = new Properties(); - sysProps.putAll(extension().buildEffectiveConfiguration(appModel.getAppArtifact()).getValues()); + sysProps.putAll(extension().buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties()) + .getValues()); try (CuratedApplication curatedApplication = QuarkusBootstrap.builder() .setBaseClassLoader(getClass().getClassLoader()) .setExistingModel(appModel) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/EffectiveConfig.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/EffectiveConfig.java index 52583a3088096..8e49f6a2b5a10 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/EffectiveConfig.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/EffectiveConfig.java @@ -60,6 +60,15 @@ private EffectiveConfig(Builder builder) { // 100 -> microprofile.properties in classpath (provided by default sources) // 0 -> fallback config source for error workaround (see below) + PropertiesConfigSource platformPropertiesConfigSource; + if (builder.platformProperties.isEmpty()) { + // we don't have the model yet so we don't have the Platform properties around + platformPropertiesConfigSource = new PropertiesConfigSource( + Map.of("platform.quarkus.native.builder-image", "<>"), "platformProperties", 0); + } else { + platformPropertiesConfigSource = new PropertiesConfigSource(builder.platformProperties, "platformProperties", 0); + } + this.config = ConfigUtils.emptyConfigBuilder() .forClassLoader(toUrlClassloader(builder.sourceDirectories)) .withSources(new PropertiesConfigSource(builder.forcedProperties, "forcedProperties", 600)) @@ -70,9 +79,7 @@ private EffectiveConfig(Builder builder) { .withSources(new YamlConfigSourceLoader.InFileSystem()) .withSources(new YamlConfigSourceLoader.InClassPath()) .addPropertiesSources() - // todo: this is due to ApplicationModel#getPlatformProperties not being included in the effective config - .withSources(new PropertiesConfigSource(Map.of("platform.quarkus.native.builder-image", "<>"), - "NativeConfig#builderImage", 0)) + .withSources(platformPropertiesConfigSource) .withDefaultValues(builder.defaultProperties) .withProfile(builder.profile) .withMapping(PackageConfig.class) @@ -122,6 +129,7 @@ static Builder builder() { } static final class Builder { + private Map platformProperties = emptyMap(); private Map forcedProperties = emptyMap(); private Map taskProperties = emptyMap(); private Map buildProperties = emptyMap(); @@ -134,6 +142,11 @@ EffectiveConfig build() { return new EffectiveConfig(this); } + Builder withPlatformProperties(Map platformProperties) { + this.platformProperties = platformProperties; + return this; + } + Builder withForcedProperties(Map forcedProperties) { this.forcedProperties = forcedProperties; return this; diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java index 40e811c1b02a2..e60c7e27e92f4 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java @@ -144,7 +144,8 @@ private void jarDependencies(Path libBoot, Path libMain) { } ApplicationModel appModel = resolveAppModelForBuild(); - SmallRyeConfig config = getExtensionView().buildEffectiveConfiguration(appModel.getAppArtifact(), new HashMap<>()) + SmallRyeConfig config = getExtensionView() + .buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties(), new HashMap<>()) .getConfig(); // see https://quarkus.io/guides/class-loading-reference#configuring-class-loading diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java index c6d4f6b160d3b..b6da8946952c1 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java @@ -244,7 +244,8 @@ void generateBuild() { ApplicationModel appModel = resolveAppModelForBuild(); SmallRyeConfig config = getExtensionView() - .buildEffectiveConfiguration(appModel.getAppArtifact(), getAdditionalForcedProperties().get().getProperties()) + .buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties(), + getAdditionalForcedProperties().get().getProperties()) .getConfig(); Map quarkusProperties = Expressions.withoutExpansion(() -> { Map values = new HashMap<>(); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java index a86e1b4e794f9..e9aa494652e06 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java @@ -119,7 +119,8 @@ public Set getInputDirectory() { public void generateCode() throws IOException { ApplicationModel appModel = ToolingUtils.deserializeAppModel(getApplicationModel().get().getAsFile().toPath()); Map configMap = getExtensionView() - .buildEffectiveConfiguration(appModel.getAppArtifact(), new HashMap<>()).getValues(); + .buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties(), new HashMap<>()) + .getValues(); File outputPath = getGeneratedOutputDirectory().get().getAsFile(); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPluginExtensionView.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPluginExtensionView.java index 9dc97a6c53ea7..269effd01e20b 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPluginExtensionView.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPluginExtensionView.java @@ -210,7 +210,7 @@ private void exportCustomManifestProperties(Map properties) { } protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArtifact, - Map additionalForcedProperties) { + Map platformProperties, Map additionalForcedProperties) { Map properties = new HashMap<>(); exportCustomManifestProperties(properties); @@ -235,6 +235,7 @@ protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArti forced.put("quarkus.native.enabled", "true"); } return EffectiveConfig.builder() + .withPlatformProperties(platformProperties) .withForcedProperties(forced) .withTaskProperties(properties) .withBuildProperties(getQuarkusBuildProperties().get()) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java index e65e7e8c2759b..b8bf3cdcc70a3 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java @@ -102,7 +102,8 @@ public void setJvmArgs(List jvmArgs) { public void runQuarkus() { ApplicationModel appModel = resolveAppModelForBuild(); Properties sysProps = new Properties(); - sysProps.putAll(extension().buildEffectiveConfiguration(appModel.getAppArtifact()).getValues()); + sysProps.putAll(extension().buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties()) + .getValues()); try (CuratedApplication curatedApplication = QuarkusBootstrap.builder() .setBaseClassLoader(getClass().getClassLoader()) .setExistingModel(appModel) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java index 39882f3e5434c..c6b61ab8da86b 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java @@ -49,7 +49,7 @@ public void dumpEffectiveConfiguration() { try { ApplicationModel appModel = resolveAppModelForBuild(); EffectiveConfig effectiveConfig = getExtensionView() - .buildEffectiveConfiguration(appModel.getAppArtifact(), + .buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties(), getAdditionalForcedProperties().get().getProperties()); SmallRyeConfig config = effectiveConfig.getConfig(); List sourceNames = new ArrayList<>(); From 2dde19987dff0bcc18bee66abdfd6dfa73ade2fb Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 17 Jan 2025 15:38:53 +0100 Subject: [PATCH 29/50] Pass ApplicationModel to build configuration (cherry picked from commit be3edfab06a1d455208606e8e93f40fbb72d784e) --- .../quarkus/gradle/extension/QuarkusPluginExtension.java | 2 +- .../quarkus/gradle/tasks/AbstractQuarkusExtension.java | 8 +++++--- .../src/main/java/io/quarkus/gradle/tasks/Deploy.java | 3 +-- .../quarkus/gradle/tasks/QuarkusBuildDependencies.java | 2 +- .../java/io/quarkus/gradle/tasks/QuarkusBuildTask.java | 3 +-- .../io/quarkus/gradle/tasks/QuarkusGenerateCode.java | 3 +-- .../quarkus/gradle/tasks/QuarkusPluginExtensionView.java | 9 ++++++--- .../main/java/io/quarkus/gradle/tasks/QuarkusRun.java | 3 +-- .../quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java | 2 +- 9 files changed, 18 insertions(+), 17 deletions(-) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java index 42af9ba6b15bb..cbf5e6e62ee96 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java @@ -74,7 +74,7 @@ public void beforeTest(Test task) { Map props = task.getSystemProperties(); ApplicationModel appModel = getApplicationModel(TEST); - SmallRyeConfig config = buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties()) + SmallRyeConfig config = buildEffectiveConfiguration(appModel) .getConfig(); config.getOptionalValue(TEST.getProfileKey(), String.class) .ifPresent(value -> props.put(TEST.getProfileKey(), value)); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java index 773194ad850d7..81301d6c807ad 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/AbstractQuarkusExtension.java @@ -27,6 +27,7 @@ import org.gradle.api.tasks.SourceSet; import org.gradle.process.JavaForkOptions; +import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.gradle.dsl.Manifest; import io.quarkus.maven.dependency.ResolvedDependency; import io.smallrye.common.expression.Expression; @@ -117,8 +118,9 @@ protected Manifest manifest() { return baseConfig().manifest(); } - protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArtifact, - Map platformProperties) { + protected EffectiveConfig buildEffectiveConfiguration(ApplicationModel appModel) { + ResolvedDependency appArtifact = appModel.getAppArtifact(); + Map properties = new HashMap<>(); exportCustomManifestProperties(properties); @@ -141,7 +143,7 @@ protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArti defaultProperties.putIfAbsent("quarkus.application.version", appArtifact.getVersion()); return EffectiveConfig.builder() - .withPlatformProperties(platformProperties) + .withPlatformProperties(appModel.getPlatformProperties()) .withForcedProperties(forcedPropertiesProperty.get()) .withTaskProperties(properties) .withBuildProperties(quarkusBuildProperties.get()) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java index 0670c80862004..da99d0a7386b3 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/Deploy.java @@ -91,8 +91,7 @@ public Deploy() { public void checkRequiredExtensions() { ApplicationModel appModel = resolveAppModelForBuild(); Properties sysProps = new Properties(); - sysProps.putAll(extension().buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties()) - .getValues()); + sysProps.putAll(extension().buildEffectiveConfiguration(appModel).getValues()); try (CuratedApplication curatedApplication = QuarkusBootstrap.builder() .setBaseClassLoader(getClass().getClassLoader()) .setExistingModel(appModel) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java index e60c7e27e92f4..7885ce37c0d1d 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java @@ -145,7 +145,7 @@ private void jarDependencies(Path libBoot, Path libMain) { ApplicationModel appModel = resolveAppModelForBuild(); SmallRyeConfig config = getExtensionView() - .buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties(), new HashMap<>()) + .buildEffectiveConfiguration(appModel, new HashMap<>()) .getConfig(); // see https://quarkus.io/guides/class-loading-reference#configuring-class-loading diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java index b6da8946952c1..39eae2b51e20b 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildTask.java @@ -244,8 +244,7 @@ void generateBuild() { ApplicationModel appModel = resolveAppModelForBuild(); SmallRyeConfig config = getExtensionView() - .buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties(), - getAdditionalForcedProperties().get().getProperties()) + .buildEffectiveConfiguration(appModel, getAdditionalForcedProperties().get().getProperties()) .getConfig(); Map quarkusProperties = Expressions.withoutExpansion(() -> { Map values = new HashMap<>(); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java index e9aa494652e06..226a1cccacc19 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusGenerateCode.java @@ -119,8 +119,7 @@ public Set getInputDirectory() { public void generateCode() throws IOException { ApplicationModel appModel = ToolingUtils.deserializeAppModel(getApplicationModel().get().getAsFile().toPath()); Map configMap = getExtensionView() - .buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties(), new HashMap<>()) - .getValues(); + .buildEffectiveConfiguration(appModel, new HashMap<>()).getValues(); File outputPath = getGeneratedOutputDirectory().get().getAsFile(); diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPluginExtensionView.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPluginExtensionView.java index 269effd01e20b..22bb019e69da6 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPluginExtensionView.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusPluginExtensionView.java @@ -38,6 +38,7 @@ import org.gradle.process.JavaForkOptions; import org.gradle.util.GradleVersion; +import io.quarkus.bootstrap.model.ApplicationModel; import io.quarkus.deployment.pkg.PackageConfig; import io.quarkus.gradle.QuarkusPlugin; import io.quarkus.gradle.dsl.Manifest; @@ -209,8 +210,10 @@ private void exportCustomManifestProperties(Map properties) { } } - protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArtifact, - Map platformProperties, Map additionalForcedProperties) { + protected EffectiveConfig buildEffectiveConfiguration(ApplicationModel appModel, + Map additionalForcedProperties) { + ResolvedDependency appArtifact = appModel.getAppArtifact(); + Map properties = new HashMap<>(); exportCustomManifestProperties(properties); @@ -235,7 +238,7 @@ protected EffectiveConfig buildEffectiveConfiguration(ResolvedDependency appArti forced.put("quarkus.native.enabled", "true"); } return EffectiveConfig.builder() - .withPlatformProperties(platformProperties) + .withPlatformProperties(appModel.getPlatformProperties()) .withForcedProperties(forced) .withTaskProperties(properties) .withBuildProperties(getQuarkusBuildProperties().get()) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java index b8bf3cdcc70a3..f944cbd1cd6ba 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusRun.java @@ -102,8 +102,7 @@ public void setJvmArgs(List jvmArgs) { public void runQuarkus() { ApplicationModel appModel = resolveAppModelForBuild(); Properties sysProps = new Properties(); - sysProps.putAll(extension().buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties()) - .getValues()); + sysProps.putAll(extension().buildEffectiveConfiguration(appModel).getValues()); try (CuratedApplication curatedApplication = QuarkusBootstrap.builder() .setBaseClassLoader(getClass().getClassLoader()) .setExistingModel(appModel) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java index c6b61ab8da86b..6d5af8ef63caa 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusShowEffectiveConfig.java @@ -49,7 +49,7 @@ public void dumpEffectiveConfiguration() { try { ApplicationModel appModel = resolveAppModelForBuild(); EffectiveConfig effectiveConfig = getExtensionView() - .buildEffectiveConfiguration(appModel.getAppArtifact(), appModel.getPlatformProperties(), + .buildEffectiveConfiguration(appModel, getAdditionalForcedProperties().get().getProperties()); SmallRyeConfig config = effectiveConfig.getConfig(); List sourceNames = new ArrayList<>(); From ca23957bb1e559e271092024bb2b46d10a66b909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Fri, 17 Jan 2025 18:15:51 +0100 Subject: [PATCH 30/50] docs(security): CDI request context with HTTP perms and proactive auth (cherry picked from commit a94d3cab805a8c8467efa4fee0b81cedf2bd6584) --- docs/src/main/asciidoc/cdi-reference.adoc | 1 + ...urity-authorize-web-endpoints-reference.adoc | 17 ++++++++++++++--- .../main/asciidoc/security-customization.adoc | 2 ++ .../security-proactive-authentication.adoc | 16 +++++++++++++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/src/main/asciidoc/cdi-reference.adoc b/docs/src/main/asciidoc/cdi-reference.adoc index f10a3a66eee15..cd714fcf13433 100644 --- a/docs/src/main/asciidoc/cdi-reference.adoc +++ b/docs/src/main/asciidoc/cdi-reference.adoc @@ -333,6 +333,7 @@ public class EagerAppBean { NOTE: Quarkus users are encouraged to always prefer the `@Observes StartupEvent` to `@Initialized(ApplicationScoped.class)` as explained in the xref:lifecycle.adoc[Application Initialization and Termination] guide. +[[request-context-lifecycle]] === Request Context Lifecycle The request context is also active: diff --git a/docs/src/main/asciidoc/security-authorize-web-endpoints-reference.adoc b/docs/src/main/asciidoc/security-authorize-web-endpoints-reference.adoc index 30fe4e03adb6a..dd33ede5af1b5 100644 --- a/docs/src/main/asciidoc/security-authorize-web-endpoints-reference.adoc +++ b/docs/src/main/asciidoc/security-authorize-web-endpoints-reference.adoc @@ -123,9 +123,9 @@ public class CustomNamedHttpSecPolicy implements HttpSecurityPolicy { public Uni checkPermission(RoutingContext event, Uni identity, AuthorizationRequestContext requestContext) { if (customRequestAuthorization(event)) { - return Uni.createFrom().item(CheckResult.PERMIT); + return CheckResult.permit(); } - return Uni.createFrom().item(CheckResult.DENY); + return CheckResult.deny(); } @Override @@ -182,6 +182,17 @@ You can also create global `HttpSecurityPolicy` invoked on every request. Just do not implement the `io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.name` method and leave the policy nameless. ==== +[[policy-active-cdi-request-context]] +=== Inject `@RequestScoped` beans into `HttpSecurityPolicy` + +`@RequestScoped` beans can only be injected when the xref:cdi-reference.adoc#request-context-lifecycle[CDI request context] is active. +The context can be activated by users, for example with the `@ActivateRequestContext`, however authorization happens before Quarkus prepares some `@RequestScoped` beans. +We recommend to let Quarkus activate and prepare CDI request context for you. +For example, consider a situation where you want to inject a bean from the Jakarta REST context, such as the `jakarta.ws.rs.core.UriInfo` bean. +In this case, you must apply the `HttpSecurityPolicy` to Jakarta REST endpoints. This can be achieved in one of the following ways: +* Use the `@AuthorizationPolicy` security annotation. +* Set the `quarkus.http.auth.permission.custom1.applies-to=jaxrs` configuration property. + === Matching on paths and methods Permission sets can also specify paths and methods as a comma-separated list. @@ -494,7 +505,7 @@ s| `@PermitAll` | Specifies that all security roles are allowed to invoke the sp s| `@RolesAllowed` | Specifies the list of security roles allowed to access methods in an application. s| `@Authenticated` | {project-name} provides the `io.quarkus.security.Authenticated` annotation that permits any authenticated user to access the resource. It's equivalent to `@RolesAllowed("**")`. s| `@PermissionsAllowed` | Specifies the list of permissions that are allowed to invoke the specified methods. -s| `@AuthorizationPolicy` | Specifies named `io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy` that should authorize access to the specified endpoints.HttpSecurityPolicy. +s| `@AuthorizationPolicy` | Specifies named `io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy` that should authorize access to the specified Jakarta REST endpoints. Named HttpSecurityPolicy can be used for general authorization checks as demonstrated by <>. |=== diff --git a/docs/src/main/asciidoc/security-customization.adoc b/docs/src/main/asciidoc/security-customization.adoc index b42ce570911ed..620e644fd5e2d 100644 --- a/docs/src/main/asciidoc/security-customization.adoc +++ b/docs/src/main/asciidoc/security-customization.adoc @@ -242,6 +242,8 @@ You can enforce the order by implementing a default `SecurityIdentityAugmentor#p By default, the request context is not activated when augmenting the security identity, this means that if you want to use for example Hibernate that mandates a request context, you will have a `jakarta.enterprise.context.ContextNotActiveException`. +IMPORTANT: Please also review the xref:security-proactive-authentication.adoc#cdi-request-context-activation[Activating the CDI request context] section of the "Proactive authentication" guide. + The solution is to activate the request context, the following example shows how to get the roles from an Hibernate with Panache `UserRoleEntity`. [source,java] diff --git a/docs/src/main/asciidoc/security-proactive-authentication.adoc b/docs/src/main/asciidoc/security-proactive-authentication.adoc index de4be3fb47cf9..424f3e5ff0721 100644 --- a/docs/src/main/asciidoc/security-proactive-authentication.adoc +++ b/docs/src/main/asciidoc/security-proactive-authentication.adoc @@ -18,6 +18,7 @@ Gain practical insights and strategies for various application scenarios. Proactive authentication is enabled in Quarkus by default. It ensures that all incoming requests with credentials are authenticated, even if the target page does not require authentication. As a result, requests with invalid credentials are rejected, even if the target page is public. +Requests without credentials are not rejected, because anonymous requests are allowed. You can turn off this default behavior if you want to authenticate only when the target page requires it. To turn off proactive authentication so that authentication occurs only when the target page requires it, modify the `application.properties` configuration file as follows: @@ -30,7 +31,7 @@ quarkus.http.auth.proactive=false If you turn off proactive authentication, the authentication process runs only when an identity is requested. An identity can be requested because of security rules that require the user to authenticate or because programmatic access to the current identity is required. -If proactive authentication is used, accessing `SecurityIdentity` is a blocking operation. +If proactive authentication is not used, accessing `SecurityIdentity` is a blocking operation. This is because authentication might have yet to happen, and accessing `SecurityIdentity` might require calls to external systems, such as databases, that might block the operation. For blocking applications, this is not an issue. However, if you have disabled authentication in a reactive application, this fails because you cannot do blocking operations on the I/O thread. @@ -96,6 +97,19 @@ public class HelloService { } ---- +[[cdi-request-context-activation]] +== Activating the CDI request context + +You may need to inject `@RequestScoped` beans during authentication and authorization. +A good example of this is accessing a database during a `SecurityIdentity` augmentation, +which is described in the xref:security-customization.adoc#security-identity-customization[Security Identity Customization] section of the "Security Tips and Tricks" guide. +If authentication or authorization fails with the `jakarta.enterprise.context.ContextNotActiveException`, disabling proactive authentication is most often the best solution. +Users can also activate xref:cdi-reference.adoc#request-context-lifecycle[CDI request context], for example, by using the `@ActivateRequestContext` annotation. +However, some CDI beans may not be ready for use. + +One exception to this solution is a situation when application endpoints are secured with the xref:security-authorize-web-endpoints-reference.adoc#authorization-using-configuration[Authorization using configuration]. +For more information, see the xref:security-authorize-web-endpoints-reference.adoc#policy-active-cdi-request-context[Inject RequestScoped beans into HttpSecurityPolicy] section of the "Authorization of Web endpoints" guide for more information. + [[customize-auth-exception-responses]] == Customize authentication exception responses From 78f38d2fc8d96de843aa02b2a6c7cf62d09ae6bd Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Fri, 17 Jan 2025 15:13:00 +1100 Subject: [PATCH 31/50] Don't allow requests into restarting application If the app is restarting then we should not short-circuit the hot reload handler/scan lock logic. This is not perfect, as there will always be a possible race, but makes it much less likely a request will hit a torn down app. (cherry picked from commit 4ba3a469181b17f9dcf3f8f39a2ae0df06b58e81) --- .../http/runtime/devmode/VertxHttpHotReplacementSetup.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/VertxHttpHotReplacementSetup.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/VertxHttpHotReplacementSetup.java index d717141311b0a..d2e2ca3d626af 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/VertxHttpHotReplacementSetup.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/devmode/VertxHttpHotReplacementSetup.java @@ -135,7 +135,9 @@ public Void call() { routingContext.request().resume(); return; } - if ((nextUpdate > System.currentTimeMillis() && !hotReplacementContext.isTest()) + if ((nextUpdate > System.currentTimeMillis() && + !hotReplacementContext.isTest() && + !DevConsoleManager.isDoingHttpInitiatedReload()) // if there is a live reload possibly going on we don't want to let a request through to restarting application, this is best effort, but it narrows the window a lot || routingContext.request().headers().contains(HEADER_NAME)) { if (hotReplacementContext.getDeploymentProblem() != null) { handleDeploymentProblem(routingContext, hotReplacementContext.getDeploymentProblem()); From d438959116397afc7dd7fbdaf696724a8a3d9198 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 17 Jan 2025 17:35:04 +0100 Subject: [PATCH 32/50] Redis Client: fix NPE when constructing XPendingSummary Certain fields that were deemed to be mandatory can in fact be `null`, as demonstrated in newly added tests. Also, this commit fixes translating `XTrimArgs` to the actual argument array in case `MAXLEN` is set to `0`. That is, in fact, a valid value, as demonstrated by the newly added tests and also by inspecting Redis source code [1]. [1] https://github.com/redis/redis/blob/7.2.4/src/t_stream.c#L941 (cherry picked from commit 84d57f6e44b7eb714e20a424a39fe8c9b5e5c9f8) --- .../datasource/stream/XPendingSummary.java | 6 +- .../redis/datasource/stream/XTrimArgs.java | 2 +- .../ReactiveStreamCommandsImpl.java | 10 +-- .../redis/datasource/StreamCommandsTest.java | 13 +++- .../TransactionalStreamCommandsTest.java | 69 +++++++++++++------ 5 files changed, 70 insertions(+), 30 deletions(-) diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/stream/XPendingSummary.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/stream/XPendingSummary.java index ee61127ca9751..815dac30b4cfb 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/stream/XPendingSummary.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/stream/XPendingSummary.java @@ -37,7 +37,7 @@ public long getPendingCount() { /** * Gets the lowest message id that was not yet acknowledged. * - * @return the lowest message id + * @return the lowest message id; may be {@code null} */ public String getLowestId() { return lowestId; @@ -46,7 +46,7 @@ public String getLowestId() { /** * Gets the highest message id that was not yet acknowledged. * - * @return the highest message id + * @return the highest message id; may be {@code null} */ public String getHighestId() { return highestId; @@ -56,7 +56,7 @@ public String getHighestId() { * Get the list of every consumer in the consumer group with at least one pending message, * and the number of pending messages it has. * - * @return the map composed of consumer -> number of message + * @return the map composed of consumer -> number of message; may be empty */ public Map getConsumers() { return consumers; diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/stream/XTrimArgs.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/stream/XTrimArgs.java index 3d33cc56a2e8e..cb96a3da49d7d 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/stream/XTrimArgs.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/stream/XTrimArgs.java @@ -66,7 +66,7 @@ public XTrimArgs limit(long limit) { public List toArgs() { List args = new ArrayList<>(); - if (maxlen > 0) { + if (maxlen >= 0) { if (minid != null) { throw new IllegalArgumentException("Cannot use `MAXLEN` and `MINID` together"); } diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveStreamCommandsImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveStreamCommandsImpl.java index ca566c31b7dde..4ccba2e69cea0 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveStreamCommandsImpl.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveStreamCommandsImpl.java @@ -370,12 +370,14 @@ protected XPendingSummary decodeAsXPendingSummary(Response r) { } var pending = r.get(0).toLong(); - var lowest = r.get(1).toString(); - var highest = r.get(2).toString(); + var lowest = r.get(1) != null ? r.get(1).toString() : null; + var highest = r.get(2) != null ? r.get(2).toString() : null; Map consumers = new HashMap<>(); - for (Response nested : r.get(3)) { - consumers.put(nested.get(0).toString(), nested.get(1).toLong()); + if (r.get(3) != null) { + for (Response nested : r.get(3)) { + consumers.put(nested.get(0).toString(), nested.get(1).toLong()); + } } return new XPendingSummary(pending, lowest, highest, consumers); diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StreamCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StreamCommandsTest.java index 5958c37f56d51..d491f69587c12 100644 --- a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StreamCommandsTest.java +++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/StreamCommandsTest.java @@ -648,11 +648,22 @@ void xGroupSetIdWithArgs() { @Test void xPendingSummaryTest() { Map payload = Map.of("sensor-id", 1234, "temperature", 19); + + stream.xadd(key, payload); + stream.xtrim(key, new XTrimArgs().maxlen(0)); + + stream.xgroupCreate(key, "my-group", "0-0"); + + XPendingSummary summaryEmpty = stream.xpending(key, "my-group"); + assertThat(summaryEmpty.getPendingCount()).isEqualTo(0); + assertThat(summaryEmpty.getHighestId()).isNull(); + assertThat(summaryEmpty.getLowestId()).isNull(); + assertThat(summaryEmpty.getConsumers()).isEmpty(); + for (int i = 0; i < 100; i++) { stream.xadd(key, payload); } - stream.xgroupCreate(key, "my-group", "0-0"); List> messages = stream.xreadgroup("my-group", "consumer-123", key, ">"); assertThat(messages).hasSize(100); diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/TransactionalStreamCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/TransactionalStreamCommandsTest.java index df4d62e61a01c..dec431322a6fd 100644 --- a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/TransactionalStreamCommandsTest.java +++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/TransactionalStreamCommandsTest.java @@ -18,6 +18,7 @@ import io.quarkus.redis.datasource.stream.XAddArgs; import io.quarkus.redis.datasource.stream.XPendingArgs; import io.quarkus.redis.datasource.stream.XPendingSummary; +import io.quarkus.redis.datasource.stream.XTrimArgs; import io.quarkus.redis.datasource.transactions.TransactionResult; import io.quarkus.redis.runtime.datasource.BlockingRedisDataSourceImpl; import io.quarkus.redis.runtime.datasource.ReactiveRedisDataSourceImpl; @@ -48,31 +49,45 @@ public void streamBlocking() { TransactionalStreamCommands stream = tx.stream(String.class); assertThat(stream.getDataSource()).isEqualTo(tx); + stream.xadd(key, payload); + stream.xtrim(key, new XTrimArgs().maxlen(0)); + + stream.xgroupCreate(key, "g1", "0"); + + stream.xpending(key, "g1"); + stream.xadd(key, payload); stream.xadd(key, new XAddArgs().nomkstream(), payload); stream.xread(key, "0"); // 3 -> 2 messages - stream.xgroupCreate(key, "g1", "0"); stream.xreadgroup("g1", "c1", key, ">"); stream.xpending(key, "g1"); stream.xpending(key, "g1", StreamRange.of("-", "+"), 10, new XPendingArgs().consumer("c1")); }); - assertThat(result.size()).isEqualTo(7); + assertThat(result.size()).isEqualTo(10); assertThat(result.discarded()).isFalse(); + assertThat((String) result.get(0)).isNotBlank(); - assertThat((String) result.get(1)).isNotBlank(); + assertThat((Long) result.get(1)).isEqualTo(1); - String id1 = result.get(0); - String id2 = result.get(1); + assertThat(result. get(3).getPendingCount()).isEqualTo(0); + assertThat(result. get(3).getHighestId()).isNull(); + assertThat(result. get(3).getLowestId()).isNull(); + assertThat(result. get(3).getConsumers()).isEmpty(); - assertThat((List>) result.get(2)).hasSize(2); - assertThat((List>) result.get(4)).hasSize(2); + String id1 = result.get(4); + String id2 = result.get(5); - assertThat(((XPendingSummary) result.get(5)).getPendingCount()).isEqualTo(2); - List list = result.get(6); + assertThat((List>) result.get(6)).hasSize(2); + assertThat((List>) result.get(7)).hasSize(2); - assertThat(((List) result.get(6))).hasSize(2); + assertThat((result. get(8)).getPendingCount()).isEqualTo(2); + assertThat((result. get(8)).getHighestId()).isNotNull(); + assertThat((result. get(8)).getLowestId()).isNotNull(); + assertThat((result. get(8)).getConsumers()).hasSize(1); + List list = result.get(9); + assertThat(list).hasSize(2); List ids = list.stream().map(PendingMessage::getMessageId).collect(Collectors.toList()); assertThat(ids).containsExactly(id1, id2); } @@ -84,29 +99,41 @@ public void streamReactive() { assertThat(stream.getDataSource()).isEqualTo(tx); return stream.xadd(key, payload) - .chain((x) -> stream.xadd(key, new XAddArgs().nomkstream(), payload)) - .chain(x -> stream.xread(key, "0")) + .chain(x -> stream.xtrim(key, new XTrimArgs().maxlen(0))) .chain(x -> stream.xgroupCreate(key, "g1", "0")) + .chain(x -> stream.xpending(key, "g1")) + .chain(x -> stream.xadd(key, payload)) + .chain(x -> stream.xadd(key, new XAddArgs().nomkstream(), payload)) + .chain(x -> stream.xread(key, "0")) .chain(x -> stream.xreadgroup("g1", "c1", key, ">")) .chain(x -> stream.xpending(key, "g1")) .chain(x -> stream.xpending(key, "g1", StreamRange.of("-", "+"), 10)); }).await().indefinitely(); - assertThat(result.size()).isEqualTo(7); + assertThat(result.size()).isEqualTo(10); assertThat(result.discarded()).isFalse(); + assertThat((String) result.get(0)).isNotBlank(); - assertThat((String) result.get(1)).isNotBlank(); + assertThat((Long) result.get(1)).isEqualTo(1); + + assertThat(result. get(3).getPendingCount()).isEqualTo(0); + assertThat(result. get(3).getHighestId()).isNull(); + assertThat(result. get(3).getLowestId()).isNull(); + assertThat(result. get(3).getConsumers()).isEmpty(); - String id1 = result.get(0); - String id2 = result.get(1); + String id1 = result.get(4); + String id2 = result.get(5); - assertThat((List>) result.get(2)).hasSize(2); - assertThat((List>) result.get(4)).hasSize(2); + assertThat((List>) result.get(6)).hasSize(2); + assertThat((List>) result.get(7)).hasSize(2); - assertThat(((XPendingSummary) result.get(5)).getPendingCount()).isEqualTo(2); - List list = result.get(6); + assertThat((result. get(8)).getPendingCount()).isEqualTo(2); + assertThat((result. get(8)).getHighestId()).isNotNull(); + assertThat((result. get(8)).getLowestId()).isNotNull(); + assertThat((result. get(8)).getConsumers()).hasSize(1); - assertThat(((List) result.get(6))).hasSize(2); + List list = result.get(9); + assertThat(list).hasSize(2); List ids = list.stream().map(PendingMessage::getMessageId).collect(Collectors.toList()); assertThat(ids).containsExactly(id1, id2); } From 740264a61c8319e6338c5228f70b04bab7673f95 Mon Sep 17 00:00:00 2001 From: Ales Justin Date: Sat, 18 Jan 2025 18:07:35 +0100 Subject: [PATCH 33/50] Add props change to LGTM re-load test (cherry picked from commit 3cc5e07025022d8276f50d37bc6ab3c7f4f89004) --- .../java/io/quarkus/observability/test/LgtmReloadTest.java | 7 ++++++- .../src/test/resources/application.properties | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmReloadTest.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmReloadTest.java index f0d11e46b007b..bbecad807b06e 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmReloadTest.java +++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmReloadTest.java @@ -20,7 +20,10 @@ public class LgtmReloadTest extends LgtmTestHelper { @RegisterExtension static final QuarkusDevModeTest test = new QuarkusDevModeTest() - .withApplicationRoot((jar) -> jar.addClasses(ReloadEndpoint.class, ConfigEndpoint.class)); + .withApplicationRoot( + jar -> jar.addClasses(ReloadEndpoint.class, ConfigEndpoint.class) + .addAsResource("application.properties", + "application.properties")); @Override protected String grafanaEndpoint() { @@ -32,5 +35,7 @@ public void testReload() { poke("/reload"); test.modifySourceFile(ReloadEndpoint.class, s -> s.replace("/reload", "/new")); poke("/new"); + test.modifyResourceFile("application.properties", s -> s.replace("timeout=PT1M", "timeout=PT2M")); + poke("/new"); } } diff --git a/integration-tests/observability-lgtm/src/test/resources/application.properties b/integration-tests/observability-lgtm/src/test/resources/application.properties index e1e468769a002..ed0590146060a 100644 --- a/integration-tests/observability-lgtm/src/test/resources/application.properties +++ b/integration-tests/observability-lgtm/src/test/resources/application.properties @@ -8,3 +8,5 @@ quarkus.log.category."io.quarkus.devservices".level=DEBUG quarkus.micrometer.export.otlp.enabled=true quarkus.micrometer.export.otlp.publish=true quarkus.micrometer.export.otlp.step=PT5S + +quarkus.observability.lgtm.timeout=PT1M \ No newline at end of file From bb7e3fff6e26e19300df1a54b8f23573030a48e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Pantale=C3=A3o?= <22629109+DeKoxD@users.noreply.github.com> Date: Sat, 18 Jan 2025 14:12:01 +0000 Subject: [PATCH 34/50] Add support for java.time.YearMonth as JAX-RS parameter Blatant copy of geoand's PR #34324, but adding YearMonth. (cherry picked from commit 2d685477286e6984f0de257e35f9b4a2e208d579) --- .../common/processor/EndpointIndexer.java | 3 +- .../processor/ResteasyReactiveDotNames.java | 2 + .../processor/ServerEndpointIndexer.java | 4 + .../converters/YearMonthParamConverter.java | 58 ++++++++++ .../vertx/test/simple/YearMonthParamTest.java | 104 ++++++++++++++++++ 5 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/YearMonthParamConverter.java create mode 100644 independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/YearMonthParamTest.java 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 c9e4a537b2139..15ccdcb94b4d8 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 @@ -80,6 +80,7 @@ import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.UNI; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.URI_INFO; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.YEAR; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.YEAR_MONTH; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.ZONED_DATE_TIME; import java.lang.reflect.Modifier; @@ -157,7 +158,7 @@ public abstract class EndpointIndexer SUPPORT_TEMPORAL_PARAMS = Set.of(INSTANT, LOCAL_DATE, LOCAL_TIME, LOCAL_DATE_TIME, - OFFSET_TIME, OFFSET_DATE_TIME, ZONED_DATE_TIME, YEAR); + OFFSET_TIME, OFFSET_DATE_TIME, ZONED_DATE_TIME, YEAR, YEAR_MONTH); protected static final Logger log = Logger.getLogger(EndpointIndexer.class); protected static final String[] EMPTY_STRING_ARRAY = new String[] {}; diff --git a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java index 15a4a10beed20..869a8b9a9a165 100644 --- a/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java +++ b/independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/ResteasyReactiveDotNames.java @@ -12,6 +12,7 @@ import java.time.OffsetDateTime; import java.time.OffsetTime; import java.time.Year; +import java.time.YearMonth; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Collection; @@ -203,6 +204,7 @@ public final class ResteasyReactiveDotNames { public static final DotName OFFSET_TIME = DotName.createSimple(OffsetTime.class.getName()); public static final DotName ZONED_DATE_TIME = DotName.createSimple(ZonedDateTime.class.getName()); public static final DotName YEAR = DotName.createSimple(Year.class.getName()); + public static final DotName YEAR_MONTH = DotName.createSimple(YearMonth.class.getName()); public static final DotName UNI = DotName.createSimple(Uni.class.getName()); public static final DotName MULTI = DotName.createSimple(Multi.class.getName()); 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 7ba93d0418f01..35566962ad19b 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 @@ -21,6 +21,7 @@ import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.SET; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.SORTED_SET; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.YEAR; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.YEAR_MONTH; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.ZONED_DATE_TIME; import java.io.File; @@ -83,6 +84,7 @@ import org.jboss.resteasy.reactive.server.core.parameters.converters.RuntimeResolvedConverter; import org.jboss.resteasy.reactive.server.core.parameters.converters.SetConverter; import org.jboss.resteasy.reactive.server.core.parameters.converters.SortedSetConverter; +import org.jboss.resteasy.reactive.server.core.parameters.converters.YearMonthParamConverter; import org.jboss.resteasy.reactive.server.core.parameters.converters.YearParamConverter; import org.jboss.resteasy.reactive.server.core.parameters.converters.ZonedDateTimeParamConverter; import org.jboss.resteasy.reactive.server.mapping.URITemplate; @@ -623,6 +625,8 @@ private ParameterConverterSupplier determineTemporalConverter(DotName paramType, return new ZonedDateTimeParamConverter.Supplier(format, dateTimeFormatterProviderClassName); } else if (YEAR.equals(paramType)) { return new YearParamConverter.Supplier(format, dateTimeFormatterProviderClassName); + } else if (YEAR_MONTH.equals(paramType)) { + return new YearMonthParamConverter.Supplier(format, dateTimeFormatterProviderClassName); } throw new RuntimeException( diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/YearMonthParamConverter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/YearMonthParamConverter.java new file mode 100644 index 0000000000000..5eaa1261c5a1f --- /dev/null +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/converters/YearMonthParamConverter.java @@ -0,0 +1,58 @@ +package org.jboss.resteasy.reactive.server.core.parameters.converters; + +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; + +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.SignStyle; + +public class YearMonthParamConverter extends TemporalParamConverter { + + // lifted from the JDK as PARSER is private... + private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() + .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral('-') + .appendValue(MONTH_OF_YEAR, 2) + .toFormatter(); + + // this can be called by generated code + public YearMonthParamConverter() { + super(PARSER); + } + + public YearMonthParamConverter(DateTimeFormatter formatter) { + super(formatter); + } + + @Override + protected YearMonth convert(String value) { + return YearMonth.parse(value); + } + + @Override + protected YearMonth convert(String value, DateTimeFormatter formatter) { + return YearMonth.parse(value, formatter); + } + + public static class Supplier extends TemporalSupplier { + + public Supplier() { + } + + public Supplier(String pattern, String dateTimeFormatterProviderClassName) { + super(pattern, dateTimeFormatterProviderClassName); + } + + @Override + protected YearMonthParamConverter createConverter(DateTimeFormatter dateTimeFormatter) { + return new YearMonthParamConverter(dateTimeFormatter); + } + + @Override + public String getClassName() { + return YearMonthParamConverter.class.getName(); + } + } +} diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/YearMonthParamTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/YearMonthParamTest.java new file mode 100644 index 0000000000000..bb39f3ff734f5 --- /dev/null +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/YearMonthParamTest.java @@ -0,0 +1,104 @@ +package org.jboss.resteasy.reactive.server.vertx.test.simple; + +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; + +import jakarta.ws.rs.CookieParam; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.QueryParam; + +import org.hamcrest.Matchers; +import org.jboss.resteasy.reactive.DateFormat; +import org.jboss.resteasy.reactive.server.vertx.test.framework.ResteasyReactiveUnitTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.restassured.RestAssured; + +public class YearMonthParamTest { + + @RegisterExtension + static ResteasyReactiveUnitTest test = new ResteasyReactiveUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(HelloResource.class, CustomDateTimeFormatterProvider.class)); + + @Test + public void yearMonthAsQueryParam() { + RestAssured.get("/hello?date=01984-12") + .then().statusCode(200).body(Matchers.equalTo("hello#1984-12")); + } + + @Test + public void yearMonthAsPathParam() { + RestAssured.get("/hello/1821-01") + .then().statusCode(200).body(Matchers.equalTo("hello@1821-01")); + } + + @Test + public void yearMonthAsFormParam() { + RestAssured.given().formParam("date", "995-06").post("/hello") + .then().statusCode(200).body(Matchers.equalTo("hello:0995-06")); + } + + @Test + public void yearMonthAsHeader() { + RestAssured.with().header("date", "1984-11") + .get("/hello/header") + .then().statusCode(200).body(Matchers.equalTo("hello=1984-11")); + } + + @Test + public void yearMonthAsCookie() { + RestAssured.with().cookie("date", "1984-10") + .get("/hello/cookie") + .then().statusCode(200).body(Matchers.equalTo("hello/1984-10")); + } + + @Path("hello") + public static class HelloResource { + + @GET + public String helloQuery(@QueryParam("date") @DateFormat(pattern = "yyyyy-MM") YearMonth date) { + return "hello#" + date; + } + + @GET + @Path("{date}") + public String helloPath(@PathParam("date") YearMonth date) { + return "hello@" + date; + } + + @POST + public String helloForm( + @FormParam("date") @DateFormat(dateTimeFormatterProvider = CustomDateTimeFormatterProvider.class) YearMonth date) { + return "hello:" + date; + } + + @GET + @Path("cookie") + public String helloCookie( + @CookieParam("date") YearMonth date) { + return "hello/" + date; + } + + @GET + @Path("header") + public String helloHeader( + @HeaderParam("date") YearMonth date) { + return "hello=" + date; + } + } + + public static class CustomDateTimeFormatterProvider implements DateFormat.DateTimeFormatterProvider { + @Override + public DateTimeFormatter get() { + return DateTimeFormatter.ofPattern("yyy-MM"); + } + } + +} From 2ff5ccefbccda5114899e7be7fda8a6949a6f816 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Fri, 17 Jan 2025 16:43:34 -0500 Subject: [PATCH 35/50] Observability LGTM dev service filling up logs with services starting (cherry picked from commit 6957299cf3625163e3f170fdfcbb05cda20f380c) --- .../common/JBossLoggingConsumer.java | 73 +++++++++++-------- .../testcontainers/GrafanaContainer.java | 18 +++-- .../testcontainers/LgtmContainer.java | 15 ++++ .../ObservabilityContainer.java | 9 ++- 4 files changed, 78 insertions(+), 37 deletions(-) diff --git a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/JBossLoggingConsumer.java b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/JBossLoggingConsumer.java index e578fd8106c5b..992916b3e619e 100644 --- a/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/JBossLoggingConsumer.java +++ b/extensions/devservices/common/src/main/java/io/quarkus/devservices/common/JBossLoggingConsumer.java @@ -2,6 +2,8 @@ import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; import org.jboss.logging.Logger; import org.jboss.logging.MDC; @@ -21,6 +23,8 @@ public class JBossLoggingConsumer extends BaseConsumer { private String prefix = ""; + private Predicate outputPredicate = f -> true; + public JBossLoggingConsumer(Logger logger) { this(logger, false); } @@ -50,39 +54,48 @@ public JBossLoggingConsumer withSeparateOutputStreams() { return this; } + public JBossLoggingConsumer withLoggingFilter(Predicate outputPredicate) { + Objects.requireNonNull(outputPredicate); + this.outputPredicate = outputPredicate; + return this; + } + @Override public void accept(OutputFrame outputFrame) { - final OutputFrame.OutputType outputType = outputFrame.getType(); - final String utf8String = outputFrame.getUtf8StringWithoutLineEnding(); - - final Map originalMdc = MDC.getMap(); - MDC.clear(); - MDC.getMap().putAll(mdc); - try { - switch (outputType) { - case END: - break; - case STDOUT: - if (separateOutputStreams) { - logger.infof("%s%s", prefix.isEmpty() ? "" : (prefix + ": "), utf8String); - } else { - logger.infof("%s%s: %s", prefix, outputType, utf8String); - } - break; - case STDERR: - if (separateOutputStreams) { - logger.errorf("%s%s", prefix.isEmpty() ? "" : (prefix + ": "), utf8String); - } else { - logger.infof("%s%s: %s", prefix, outputType, utf8String); - } - break; - default: - throw new IllegalArgumentException("Unexpected outputType " + outputType); - } - } finally { + if (this.outputPredicate.test(outputFrame)) { + final OutputFrame.OutputType outputType = outputFrame.getType(); + final String utf8String = outputFrame.getUtf8StringWithoutLineEnding(); + + final Map originalMdc = MDC.getMap(); MDC.clear(); - if (originalMdc != null) { - MDC.getMap().putAll(originalMdc); + MDC.getMap().putAll(mdc); + + try { + switch (outputType) { + case END: + break; + case STDOUT: + if (separateOutputStreams) { + logger.infof("%s%s", prefix.isEmpty() ? "" : (prefix + ": "), utf8String); + } else { + logger.infof("%s%s: %s", prefix, outputType, utf8String); + } + break; + case STDERR: + if (separateOutputStreams) { + logger.errorf("%s%s", prefix.isEmpty() ? "" : (prefix + ": "), utf8String); + } else { + logger.infof("%s%s: %s", prefix, outputType, utf8String); + } + break; + default: + throw new IllegalArgumentException("Unexpected outputType " + outputType); + } + } finally { + MDC.clear(); + if (originalMdc != null) { + MDC.getMap().putAll(originalMdc); + } } } } diff --git a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/GrafanaContainer.java b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/GrafanaContainer.java index 0218c6911b3cf..e7b101500af87 100644 --- a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/GrafanaContainer.java +++ b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/GrafanaContainer.java @@ -1,6 +1,7 @@ package io.quarkus.observability.testcontainers; -import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; import io.quarkus.observability.common.config.GrafanaConfig; @@ -26,10 +27,15 @@ public int getGrafanaPort() { } private WaitStrategy grafanaWaitStrategy() { - return new HttpWaitStrategy() - .forPath("/") - .forPort(config.grafanaPort()) - .forStatusCode(200) - .withStartupTimeout(config.timeout()); + return new WaitAllStrategy() + .withStartupTimeout(config.timeout()) + .withStrategy( + Wait.forHttp("/") + .forPort(config.grafanaPort()) + .forStatusCode(200) + .withStartupTimeout(config.timeout())) + .withStrategy( + Wait.forLogMessage(".*The OpenTelemetry collector and the Grafana LGTM stack are up and running.*", 1) + .withStartupTimeout(config.timeout())); } } diff --git a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java index 1f9fdc301c3a0..6541307bc292e 100644 --- a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java +++ b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java @@ -2,9 +2,11 @@ import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; +import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.utility.MountableFile; import io.quarkus.observability.common.ContainerConstants; @@ -95,6 +97,11 @@ protected String prefix() { return "LGTM"; } + @Override + protected Predicate getLoggingFilter() { + return new LgtmLoggingFilter(); + } + public String getOtlpProtocol() { return config.otlpProtocol(); } @@ -143,4 +150,12 @@ public String otlpProtocol() { return ContainerConstants.OTEL_HTTP_PROTOCOL; } } + + protected static class LgtmLoggingFilter implements Predicate { + @Override + public boolean test(OutputFrame outputFrame) { + final var line = outputFrame.getUtf8StringWithoutLineEnding(); + return !(line.startsWith("Waiting for") && line.endsWith("to start up...")); + } + } } diff --git a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/ObservabilityContainer.java b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/ObservabilityContainer.java index a3e6c6610890f..8714c8c55bfc3 100644 --- a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/ObservabilityContainer.java +++ b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/ObservabilityContainer.java @@ -7,6 +7,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Predicate; import org.jboss.logging.Logger; import org.testcontainers.containers.GenericContainer; @@ -37,8 +38,14 @@ public ObservabilityContainer(C config) { protected abstract String prefix(); + protected Predicate getLoggingFilter() { + return f -> true; + } + protected Consumer frameConsumer() { - return new JBossLoggingConsumer(log).withPrefix(prefix()); + return new JBossLoggingConsumer(log) + .withPrefix(prefix()) + .withLoggingFilter(getLoggingFilter()); } protected byte[] getResourceAsBytes(String resource) { From 99129287c2c4b45029c3a8b6cbf342a564ea747d Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 20 Jan 2025 09:40:56 +0200 Subject: [PATCH 36/50] Polish RecordBeanParamExtractor (cherry picked from commit da144feacb9ec1f94dbd60749b98b5357ef3a1bb) --- .../server/core/parameters/RecordBeanParamExtractor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/RecordBeanParamExtractor.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/RecordBeanParamExtractor.java index 8c2263e4e83ba..72a16cd0371a0 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/RecordBeanParamExtractor.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/RecordBeanParamExtractor.java @@ -9,7 +9,7 @@ public class RecordBeanParamExtractor implements ParameterExtractor { - private MethodHandle factoryMethod; + private final MethodHandle factoryMethod; public RecordBeanParamExtractor(Class target) { try { From abf815d557746d026ae75c57948ee66d09debde9 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 20 Jan 2025 09:59:19 +0200 Subject: [PATCH 37/50] Fix @BeanParam handling during hot reload Fixes: #45625 (cherry picked from commit 3a60175eb1e9910239cfa8b4a05b23ae2a50f47a) --- .../beanparam/BeanParamRecordDevModeTest.java | 42 +++++++++++++++++++ .../beanparam/FirstAndSecondResource.java | 20 +++++++++ .../parameters/RecordBeanParamExtractor.java | 6 +-- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/BeanParamRecordDevModeTest.java create mode 100644 extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/FirstAndSecondResource.java diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/BeanParamRecordDevModeTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/BeanParamRecordDevModeTest.java new file mode 100644 index 0000000000000..88d5ce887ba93 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/BeanParamRecordDevModeTest.java @@ -0,0 +1,42 @@ +package io.quarkus.resteasy.reactive.server.test.beanparam; + +import static io.restassured.RestAssured.when; +import static org.hamcrest.CoreMatchers.is; + +import java.util.function.Supplier; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class BeanParamRecordDevModeTest { + + @RegisterExtension + static QuarkusDevModeTest TEST = new QuarkusDevModeTest() + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class).addClasses(FirstAndSecondResource.class, + FirstAndSecondResource.Param.class); + } + }); + + @Test + void test() { + when().get("fs/foo/bar") + .then() + .statusCode(200) + .body(is("foo-bar")); + + TEST.modifySourceFile(FirstAndSecondResource.class, (orig) -> orig.replace("-", "#")); + + when().get("fs/foo/bar") + .then() + .statusCode(200) + .body(is("foo#bar")); + } + +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/FirstAndSecondResource.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/FirstAndSecondResource.java new file mode 100644 index 0000000000000..27e3fa187def0 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/FirstAndSecondResource.java @@ -0,0 +1,20 @@ +package io.quarkus.resteasy.reactive.server.test.beanparam; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.resteasy.reactive.RestPath; + +@Path("fs") +public class FirstAndSecondResource { + + @Path("{first}/{second}") + @GET + public String firstAndSecond(Param param) { + return param.first() + "-" + param.second(); + } + + public record Param(@RestPath String first, @RestPath String second) { + + } +} diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/RecordBeanParamExtractor.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/RecordBeanParamExtractor.java index 72a16cd0371a0..03e8f970ae9c0 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/RecordBeanParamExtractor.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/parameters/RecordBeanParamExtractor.java @@ -2,7 +2,6 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.injection.ResteasyReactiveInjectionContext; @@ -13,8 +12,8 @@ public class RecordBeanParamExtractor implements ParameterExtractor { public RecordBeanParamExtractor(Class target) { try { - factoryMethod = MethodHandles.lookup().findStatic(target, "__quarkus_rest_inject", - MethodType.methodType(target, ResteasyReactiveInjectionContext.class)); + factoryMethod = MethodHandles.lookup() + .unreflect(target.getMethod("__quarkus_rest_inject", ResteasyReactiveInjectionContext.class)); } catch (NoSuchMethodException | IllegalAccessException e) { throw new RuntimeException("Failed to find target generated factory method on record @BeanParam type", e); } @@ -27,7 +26,6 @@ public Object extractParameter(ResteasyReactiveRequestContext context) { } catch (RuntimeException e) { throw e; } catch (Throwable e) { - e.printStackTrace(); throw new RuntimeException("Failed to invoke generated factory method on record @BeanParam type", e); } } From 4910617f2974d17b442cfa7710a3fae1b64004d9 Mon Sep 17 00:00:00 2001 From: Romain QUINIO Date: Fri, 17 Jan 2025 16:50:04 +0100 Subject: [PATCH 38/50] fix: do not check categories without named handlers (cherry picked from commit 6f08f07477367ba14030fbb24de68b20bee762d9) --- .../runtime/logging/LoggingSetupRecorder.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java index 3d8fe47a07940..d6ea4794e6b20 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java @@ -261,7 +261,8 @@ public void close() throws SecurityException { namedHandlers.putAll(additionalNamedHandlersMap); - setUpCategoryLoggers(buildConfig, categoryDefaultMinLevels, categories, logContext, errorManager, namedHandlers); + setUpCategoryLoggers(buildConfig, categoryDefaultMinLevels, categories, logContext, errorManager, namedHandlers, + true); } for (RuntimeValue> additionalHandler : additionalHandlers) { @@ -344,7 +345,7 @@ public static void initializeBuildTimeLogging( emptyList(), emptyList(), emptyList(), emptyList(), errorManager, logCleanupFilter, emptyMap(), launchMode, dummy, false); - setUpCategoryLoggers(buildConfig, categoryDefaultMinLevels, categories, logContext, errorManager, namedHandlers); + setUpCategoryLoggers(buildConfig, categoryDefaultMinLevels, categories, logContext, errorManager, namedHandlers, false); addNamedHandlersToRootHandlers(config.handlers(), namedHandlers, handlers, errorManager); InitialConfigurator.DELAYED_HANDLER.setAutoFlush(false); @@ -482,7 +483,8 @@ public void run() { private static void addNamedHandlersToCategory( CategoryConfig categoryConfig, Map namedHandlers, Logger categoryLogger, - ErrorManager errorManager) { + ErrorManager errorManager, + boolean checkHandlerLinks) { for (String categoryNamedHandler : categoryConfig.handlers().get()) { Handler handler = namedHandlers.get(categoryNamedHandler); if (handler != null) { @@ -493,7 +495,7 @@ public void run() { categoryLogger.removeHandler(handler); } }); - } else { + } else if (checkHandlerLinks) { errorManager.error(String.format("Handler with name '%s' is linked to a category but not configured.", categoryNamedHandler), null, ErrorManager.GENERIC_FAILURE); } @@ -506,7 +508,8 @@ private static void setUpCategoryLoggers( final Map categories, final LogContext logContext, final ErrorManager errorManager, - final Map namedHandlers) { + final Map namedHandlers, + final boolean checkHandlerLinks) { for (Entry entry : categories.entrySet()) { String categoryName = entry.getKey(); @@ -532,7 +535,7 @@ private static void setUpCategoryLoggers( } categoryLogger.setUseParentHandlers(categoryConfig.useParentHandlers()); if (categoryConfig.handlers().isPresent()) { - addNamedHandlersToCategory(categoryConfig, namedHandlers, categoryLogger, errorManager); + addNamedHandlersToCategory(categoryConfig, namedHandlers, categoryLogger, errorManager, checkHandlerLinks); } } } From 50c0013a0c2c58fdd3f302695c7e58044901705f Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Mon, 20 Jan 2025 13:50:53 +0000 Subject: [PATCH 39/50] Avoid recursive interceptor lookups when the REST Client full name matches the simple name (cherry picked from commit de0e007d6c4b9d9495cf2cd0ce8a8bf312908651) --- .../AbstractRestClientConfigBuilder.java | 16 +++++++++---- .../config/RestClientConfigTest.java | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/AbstractRestClientConfigBuilder.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/AbstractRestClientConfigBuilder.java index 7ed5887381a76..41a74ca33ffb0 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/AbstractRestClientConfigBuilder.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/AbstractRestClientConfigBuilder.java @@ -94,7 +94,9 @@ public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) // FQN -> Quoted Config Key -> Quoted Simple Name -> Simple Name quarkusFallbacks.put(quotedFullName, quotedConfigKey); quarkusFallbacks.put(quotedConfigKey, restClient.getSimpleName()); - quarkusFallbacks.put(restClient.getSimpleName(), quotedSimpleName); + if (!quotedSimpleName.equals(quotedFullName)) { + quarkusFallbacks.put(restClient.getSimpleName(), quotedSimpleName); + } fullNameRelocates.add(quotedConfigKey); fullNameRelocates.add(restClient.getSimpleName()); fullNameRelocates.add(quotedSimpleName); @@ -103,7 +105,9 @@ public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) quarkusFallbacks.put(quotedFullName, configKey); quarkusFallbacks.put(configKey, quotedConfigKey); quarkusFallbacks.put(quotedConfigKey, restClient.getSimpleName()); - quarkusFallbacks.put(restClient.getSimpleName(), quotedSimpleName); + if (!quotedSimpleName.equals(quotedFullName)) { + quarkusFallbacks.put(restClient.getSimpleName(), quotedSimpleName); + } fullNameRelocates.add(configKey); fullNameRelocates.add(quotedConfigKey); fullNameRelocates.add(restClient.getSimpleName()); @@ -112,14 +116,18 @@ public SmallRyeConfigBuilder configBuilder(final SmallRyeConfigBuilder builder) } else { // FQN -> Quoted Simple Name -> Simple Name quarkusFallbacks.put(quotedFullName, restClient.getSimpleName()); - quarkusFallbacks.put(restClient.getSimpleName(), quotedSimpleName); + if (!quotedSimpleName.equals(quotedFullName)) { + quarkusFallbacks.put(restClient.getSimpleName(), quotedSimpleName); + } fullNameRelocates.add(restClient.getSimpleName()); fullNameRelocates.add(quotedSimpleName); } } else { // FQN -> Quoted Simple Name -> Simple Name quarkusFallbacks.put(quotedFullName, restClient.getSimpleName()); - quarkusFallbacks.put(restClient.getSimpleName(), quotedSimpleName); + if (!quotedSimpleName.equals(quotedFullName)) { + quarkusFallbacks.put(restClient.getSimpleName(), quotedSimpleName); + } fullNameRelocates.add(restClient.getSimpleName()); fullNameRelocates.add(quotedSimpleName); } diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/test/java/io/quarkus/restclient/config/RestClientConfigTest.java b/extensions/resteasy-classic/rest-client-config/runtime/src/test/java/io/quarkus/restclient/config/RestClientConfigTest.java index 7b73ed5ae3942..7e596d0a3fd20 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/test/java/io/quarkus/restclient/config/RestClientConfigTest.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/test/java/io/quarkus/restclient/config/RestClientConfigTest.java @@ -324,6 +324,29 @@ void buildTimeConfig() { assertFalse(buildTimeConfig.clients().get(ConfigKeyRestClient.class.getName()).removesTrailingSlash()); } + @Test + void defaultPackage() { + RegisteredRestClient registeredRestClient = new RegisteredRestClient("FullNameRestClient", "FullNameRestClient", null); + // application.properties in test/resources + SmallRyeConfig config = ConfigUtils.emptyConfigBuilder() + .withMapping(RestClientsConfig.class) + .withCustomizers(new SmallRyeConfigBuilderCustomizer() { + @Override + public void configBuilder(final SmallRyeConfigBuilder builder) { + new AbstractRestClientConfigBuilder() { + @Override + public List getRestClients() { + return List.of(registeredRestClient); + } + }.configBuilder(builder); + } + }) + .build(); + + RestClientsConfig restClientsConfig = config.getConfigMapping(RestClientsConfig.class); + assertEquals(1, restClientsConfig.clients().size()); + } + private void verifyConfig(RestClientConfig config) { assertTrue(config.url().isPresent()); assertThat(config.url().get()).isEqualTo("http://localhost:8080"); From b58dfd9b132073e50bac00bd310cf3829e239285 Mon Sep 17 00:00:00 2001 From: Rostislav Svoboda Date: Mon, 20 Jan 2025 16:07:21 +0100 Subject: [PATCH 40/50] Adjust Caffeine extension description to drop Java 8 mention https://github.com/ben-manes/caffeine says 'A high performance caching library for Java ' (cherry picked from commit f9553b7f97554398ee99dee30a2ee187c128aff5) --- extensions/caffeine/runtime/pom.xml | 2 +- ...m-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/caffeine/runtime/pom.xml b/extensions/caffeine/runtime/pom.xml index a3a36de42b086..bf68626565de0 100644 --- a/extensions/caffeine/runtime/pom.xml +++ b/extensions/caffeine/runtime/pom.xml @@ -11,7 +11,7 @@ quarkus-caffeine Quarkus - Caffeine - Runtime - A high performance caching library for Java 8+ + A high performance caching library for Java io.quarkus diff --git a/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-bom-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json b/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-bom-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json index 63ae43092b278..beb9c8445aa79 100644 --- a/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-bom-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json +++ b/independent-projects/tools/registry-client/src/test/resources/catalog-config/quarkus-bom-quarkus-platform-descriptor-999-SNAPSHOT-999-SNAPSHOT.json @@ -32,7 +32,7 @@ "origins" : [ "io.quarkus:quarkus-bom-quarkus-platform-descriptor:999-SNAPSHOT:json:999-SNAPSHOT" ] }, { "name" : "Caffeine", - "description" : "A high performance caching library for Java 8+", + "description" : "A high performance caching library for Java", "metadata" : { "keywords" : [ "cache" ], "categories" : [ "data" ], From 4e33efd61d4ef0b16a29ab8da9845e987a42daa8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:55:56 +0000 Subject: [PATCH 41/50] Bump resteasy-microprofile.version from 3.0.0.Final to 3.0.1.Final Bumps `resteasy-microprofile.version` from 3.0.0.Final to 3.0.1.Final. Updates `org.jboss.resteasy.microprofile:microprofile-rest-client` from 3.0.0.Final to 3.0.1.Final - [Release notes](https://github.com/resteasy/resteasy-microprofile/releases) - [Commits](https://github.com/resteasy/resteasy-microprofile/compare/3.0.0.Final...v3.0.1.Final) Updates `org.jboss.resteasy.microprofile:microprofile-config` from 3.0.0.Final to 3.0.1.Final - [Release notes](https://github.com/resteasy/resteasy-microprofile/releases) - [Commits](https://github.com/resteasy/resteasy-microprofile/compare/3.0.0.Final...v3.0.1.Final) --- updated-dependencies: - dependency-name: org.jboss.resteasy.microprofile:microprofile-rest-client dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jboss.resteasy.microprofile:microprofile-config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 1bafadd30c643709b857998a495ba27f94799df3) --- 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 8f0e37b009c2f..f3a0e6dba3412 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -26,7 +26,7 @@ 1.3.2 1 1.1.7 - 3.0.0.Final + 3.0.1.Final 3.2.0.Final 6.2.11.Final 2.8.0-alpha From 031da1a79fcb40ffb285e5b4c304ea04c37a0364 Mon Sep 17 00:00:00 2001 From: Ales Justin Date: Tue, 21 Jan 2025 07:10:45 +0100 Subject: [PATCH 42/50] Fix wait strategy usage, Grafana should not know about LGTM (cherry picked from commit 449b87c9dcca69f14d9d355da9bc5bae66aeb588) --- .../testcontainers/GrafanaContainer.java | 20 ++++++------------- .../testcontainers/LgtmContainer.java | 13 ++++++++++++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/GrafanaContainer.java b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/GrafanaContainer.java index e7b101500af87..aced0b756ca19 100644 --- a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/GrafanaContainer.java +++ b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/GrafanaContainer.java @@ -1,7 +1,6 @@ package io.quarkus.observability.testcontainers; import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.containers.wait.strategy.WaitAllStrategy; import org.testcontainers.containers.wait.strategy.WaitStrategy; import io.quarkus.observability.common.config.GrafanaConfig; @@ -9,7 +8,6 @@ @SuppressWarnings("resource") public abstract class GrafanaContainer, C extends GrafanaConfig> extends ObservabilityContainer { - protected static final String DATASOURCES_PATH = "/etc/grafana/provisioning/datasources/custom.yaml"; protected C config; @@ -19,23 +17,17 @@ public GrafanaContainer(C config) { withEnv("GF_SECURITY_ADMIN_USER", config.username()); withEnv("GF_SECURITY_ADMIN_PASSWORD", config.password()); addExposedPort(config.grafanaPort()); - waitingFor(grafanaWaitStrategy()); + waitingFor(waitStrategy()); } public int getGrafanaPort() { return getMappedPort(config.grafanaPort()); } - private WaitStrategy grafanaWaitStrategy() { - return new WaitAllStrategy() - .withStartupTimeout(config.timeout()) - .withStrategy( - Wait.forHttp("/") - .forPort(config.grafanaPort()) - .forStatusCode(200) - .withStartupTimeout(config.timeout())) - .withStrategy( - Wait.forLogMessage(".*The OpenTelemetry collector and the Grafana LGTM stack are up and running.*", 1) - .withStartupTimeout(config.timeout())); + protected WaitStrategy waitStrategy() { + return Wait.forHttp("/") + .forPort(config.grafanaPort()) + .forStatusCode(200) + .withStartupTimeout(config.timeout()); } } diff --git a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java index 6541307bc292e..bbf77c722d08a 100644 --- a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java +++ b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java @@ -7,6 +7,9 @@ import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; import org.testcontainers.containers.output.OutputFrame; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; +import org.testcontainers.containers.wait.strategy.WaitStrategy; import org.testcontainers.utility.MountableFile; import io.quarkus.observability.common.ContainerConstants; @@ -92,6 +95,16 @@ public LgtmContainer(LgtmConfig config) { } + @Override + protected WaitStrategy waitStrategy() { + return new WaitAllStrategy() + .withStartupTimeout(config.timeout()) + .withStrategy(super.waitStrategy()) + .withStrategy( + Wait.forLogMessage(".*The OpenTelemetry collector and the Grafana LGTM stack are up and running.*", 1) + .withStartupTimeout(config.timeout())); + } + @Override protected String prefix() { return "LGTM"; From 3fcba304b1a8128fd0909214ba6862203cb498a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 22:19:54 +0000 Subject: [PATCH 43/50] Bump grpc.version from 1.69.0 to 1.69.1 Bumps `grpc.version` from 1.69.0 to 1.69.1. Updates `io.grpc:grpc-bom` from 1.69.0 to 1.69.1 - [Release notes](https://github.com/grpc/grpc-java/releases) - [Commits](https://github.com/grpc/grpc-java/compare/v1.69.0...v1.69.1) Updates `io.grpc:protoc-gen-grpc-java` from 1.69.0 to 1.69.1 - [Release notes](https://github.com/grpc/grpc-java/releases) - [Commits](https://github.com/grpc/grpc-java/compare/v1.69.0...v1.69.1) --- updated-dependencies: - dependency-name: io.grpc:grpc-bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: io.grpc:protoc-gen-grpc-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit b130eecda24a199ea3cce0d0018082cb613b9744) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 05a9946f3aeea..89beca730c881 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ 7.2.2.Final - 1.69.0 + 1.69.1 1.2.2 3.25.5 ${protoc.version} From cf9a21936f417a304d018a68033b20f652115b6a Mon Sep 17 00:00:00 2001 From: Phillip Kruger Date: Tue, 21 Jan 2025 16:52:47 +1100 Subject: [PATCH 44/50] Enable new scripts config in Swagger UI Signed-off-by: Phillip Kruger (cherry picked from commit 20fe535e2649e30ec8ffd367cbd7ba77a33774d6) --- .../io/quarkus/swaggerui/deployment/SwaggerUiConfig.java | 6 ++++++ .../quarkus/swaggerui/deployment/SwaggerUiProcessor.java | 5 +++++ .../quarkus/swaggerui/deployment/SwaggerOptionsTest.java | 8 ++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiConfig.java b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiConfig.java index af2ab9d5e29de..f85bf85527f5b 100644 --- a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiConfig.java +++ b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiConfig.java @@ -256,6 +256,12 @@ public class SwaggerUiConfig { @ConfigItem Optional> plugins; + /** + * A list of external scripts (usually plugins) to use in Swagger UI. + */ + @ConfigItem + Optional> scripts; + /** * A list of presets to use in Swagger UI. */ diff --git a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java index d82f092babd90..ee4f25ef4d21c 100644 --- a/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java +++ b/extensions/swagger-ui/deployment/src/main/java/io/quarkus/swaggerui/deployment/SwaggerUiProcessor.java @@ -347,6 +347,11 @@ private byte[] generateIndexHtml(String openApiPath, String swaggerUiPath, Swagg options.put(Option.plugins, plugins); } + if (swaggerUiConfig.scripts.isPresent()) { + String scripts = String.join(",", swaggerUiConfig.scripts.get()); + options.put(Option.scripts, scripts); + } + if (swaggerUiConfig.presets.isPresent()) { String presets = swaggerUiConfig.presets.get().toString(); options.put(Option.presets, presets); diff --git a/extensions/swagger-ui/deployment/src/test/java/io/quarkus/swaggerui/deployment/SwaggerOptionsTest.java b/extensions/swagger-ui/deployment/src/test/java/io/quarkus/swaggerui/deployment/SwaggerOptionsTest.java index 0b450d11d3f59..b9c4698f17a56 100644 --- a/extensions/swagger-ui/deployment/src/test/java/io/quarkus/swaggerui/deployment/SwaggerOptionsTest.java +++ b/extensions/swagger-ui/deployment/src/test/java/io/quarkus/swaggerui/deployment/SwaggerOptionsTest.java @@ -22,7 +22,7 @@ public class SwaggerOptionsTest { @Test public void customOptions() { - RestAssured.when().get("/q/swagger-ui").then().statusCode(200) + RestAssured.when().get("/q/swagger-ui").then().log().all().and().statusCode(200) .body( containsString("Testing title"), containsString("/openapi"), @@ -33,7 +33,9 @@ public void customOptions() { containsString("validatorUrl: 'localhost'"), containsString("displayRequestDuration: true"), containsString("supportedSubmitMethods: ['get', 'post']"), - containsString("plugins: [Plugin1, Plugin2]")); + containsString("plugins: [Plugin1, Plugin2]"), + containsString("https://unpkg.com/swagger-ui-plugin-hierarchical-tags"), + containsString("/some/local/script.js")); } @@ -61,5 +63,7 @@ private static String getPropertyAsString() { PROPERTIES.put("quarkus.swagger-ui.display-request-duration", "true"); PROPERTIES.put("quarkus.swagger-ui.supported-submit-methods", "get,post"); PROPERTIES.put("quarkus.swagger-ui.plugins", "Plugin1,Plugin2"); + PROPERTIES.put("quarkus.swagger-ui.scripts", + "https://unpkg.com/swagger-ui-plugin-hierarchical-tags,/some/local/script.js"); } } From d382c3597c4ef88089c1029dc4eac03555d6a6eb Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 21 Jan 2025 12:07:09 +0200 Subject: [PATCH 45/50] Add HTTP response to HttpServerMetricsTagsContributor.Context This is useful for creating tags based on HTTP response headers for example Closes: #45717 (cherry picked from commit 9ac63d5259dd6de27ce615746b9c49969e5ecf80) --- .../main/asciidoc/telemetry-micrometer.adoc | 4 +++- .../HttpServerMetricsTagsContributor.java | 3 +++ .../binder/vertx/VertxHttpServerMetrics.java | 5 +++-- .../java/io/quarkus/ResponseHeaderTag.java | 20 +++++++++++++++++++ .../micrometer/prometheus/FruitResource.java | 8 +++++++- .../micrometer/prometheus/ExemplarTest.java | 2 +- .../PrometheusMetricsRegistryTest.java | 7 ++++--- 7 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 integration-tests/micrometer-prometheus/src/main/java/io/quarkus/ResponseHeaderTag.java diff --git a/docs/src/main/asciidoc/telemetry-micrometer.adoc b/docs/src/main/asciidoc/telemetry-micrometer.adoc index 2e4b60fb1095b..f9f9414ea0cb1 100644 --- a/docs/src/main/asciidoc/telemetry-micrometer.adoc +++ b/docs/src/main/asciidoc/telemetry-micrometer.adoc @@ -588,7 +588,9 @@ link:https://micrometer.io/docs/concepts[official documentation]. === Use `HttpServerMetricsTagsContributor` for server HTTP requests -By providing CDI beans that implement `io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor`, user code can contribute arbitrary tags based on the details of HTTP request +By providing CDI beans that implement `io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor`, user code can contribute arbitrary tags based on the details of HTTP request and responses. + +CAUTION: When creating tags using this interface, it's important to limit the cardinality of the values, otherwise there is a risk of severely degrading the metrics system's capacity. === Use `HttpClientMetricsTagsContributor` for client HTTP requests diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpServerMetricsTagsContributor.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpServerMetricsTagsContributor.java index 88651e171b3d8..0dec6727f65f9 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpServerMetricsTagsContributor.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpServerMetricsTagsContributor.java @@ -3,6 +3,7 @@ import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.config.MeterFilter; import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.spi.observability.HttpResponse; /** * Allows code to add additional Micrometer {@link Tags} to the metrics collected for completed HTTP server requests. @@ -20,5 +21,7 @@ public interface HttpServerMetricsTagsContributor { interface Context { HttpServerRequest request(); + + HttpResponse response(); } } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpServerMetrics.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpServerMetrics.java index 9b9cebe55007b..84667b71a2085 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpServerMetrics.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpServerMetrics.java @@ -203,7 +203,7 @@ public void responseEnd(HttpRequestMetric requestMetric, HttpResponse response, VertxMetricsTags.outcome(response), HttpCommonTags.status(response.statusCode())); if (!httpServerMetricsTagsContributors.isEmpty()) { - HttpServerMetricsTagsContributor.Context context = new DefaultContext(requestMetric.request()); + HttpServerMetricsTagsContributor.Context context = new DefaultContext(requestMetric.request(), response); for (int i = 0; i < httpServerMetricsTagsContributors.size(); i++) { try { Tags additionalTags = httpServerMetricsTagsContributors.get(i).contribute(context); @@ -256,6 +256,7 @@ public void disconnected(LongTaskTimer.Sample websocketMetric) { } } - private record DefaultContext(HttpServerRequest request) implements HttpServerMetricsTagsContributor.Context { + private record DefaultContext(HttpServerRequest request, + HttpResponse response) implements HttpServerMetricsTagsContributor.Context { } } diff --git a/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/ResponseHeaderTag.java b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/ResponseHeaderTag.java new file mode 100644 index 0000000000000..2339df1a30809 --- /dev/null +++ b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/ResponseHeaderTag.java @@ -0,0 +1,20 @@ +package io.quarkus; + +import jakarta.inject.Singleton; + +import io.micrometer.core.instrument.Tags; +import io.quarkus.micrometer.runtime.HttpServerMetricsTagsContributor; + +@Singleton +public class ResponseHeaderTag implements HttpServerMetricsTagsContributor { + + @Override + public Tags contribute(Context context) { + var headerValue = context.response().headers().get("foo-response"); + String value = "UNSET"; + if ((headerValue != null) && !headerValue.isEmpty()) { + value = headerValue; + } + return Tags.of("foo-response", value); + } +} diff --git a/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/FruitResource.java b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/FruitResource.java index 4227515888079..34c9fa5416b59 100644 --- a/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/FruitResource.java +++ b/integration-tests/micrometer-prometheus/src/main/java/io/quarkus/it/micrometer/prometheus/FruitResource.java @@ -6,6 +6,8 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; +import org.jboss.resteasy.reactive.RestResponse; + import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.smallrye.common.annotation.Blocking; @@ -26,9 +28,13 @@ public void trigger() { @GET @Path("all") - public void retrieveAll() { + public RestResponse retrieveAll() { PanacheQuery query = Fruit.find( "select name from Fruit"); List all = query.list(); + + return RestResponse.ResponseBuilder.noContent() + .header("foo-response", "value") + .build(); } } diff --git a/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/ExemplarTest.java b/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/ExemplarTest.java index 50e94d6220ae0..4bd040ac8725a 100644 --- a/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/ExemplarTest.java +++ b/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/ExemplarTest.java @@ -24,7 +24,7 @@ void testExemplar() { when().get("/example/prime/7919").then().statusCode(200); String metricMatch = "http_server_requests_seconds_count{dummy=\"value\",env=\"test\"," + - "env2=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\"," + + "env2=\"test\",foo=\"UNSET\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\"," + "registry=\"prometheus\",status=\"200\",uri=\"/example/prime/{number}\"} 2.0 # {span_id=\""; await().atMost(5, SECONDS).untilAsserted(() -> { diff --git a/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/PrometheusMetricsRegistryTest.java b/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/PrometheusMetricsRegistryTest.java index 1e530648096c2..5ee1d84b3927d 100644 --- a/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/PrometheusMetricsRegistryTest.java +++ b/integration-tests/micrometer-prometheus/src/test/java/io/quarkus/it/micrometer/prometheus/PrometheusMetricsRegistryTest.java @@ -134,14 +134,15 @@ void testPrometheusScrapeEndpointTextPlain() { .body(containsString("outcome=\"SUCCESS\"")) .body(containsString("dummy=\"value\"")) .body(containsString("foo=\"bar\"")) + .body(containsString("foo_response=\"value\"")) .body(containsString("uri=\"/message/match/{id}/{sub}\"")) .body(containsString("uri=\"/message/match/{other}\"")) .body(containsString( - "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\"")) + "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\"")) .body(containsString( - "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/root/{rootParam}/sub/{subParam}\"")) + "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/root/{rootParam}/sub/{subParam}\"")) // Verify Hibernate Metrics .body(containsString( @@ -233,7 +234,7 @@ void testPrometheusScrapeEndpointOpenMetrics() { .body(containsString("uri=\"/message/match/{other}\"")) .body(containsString( - "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\"")) + "http_server_requests_seconds_count{dummy=\"value\",env=\"test\",env2=\"test\",foo=\"UNSET\",foo_response=\"UNSET\",method=\"GET\",outcome=\"SUCCESS\",registry=\"prometheus\",status=\"200\",uri=\"/template/path/{value}\"")) // Verify Hibernate Metrics .body(containsString( From e0360ade8a51581de60ff5c4ce5abadafa620790 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 21 Jan 2025 13:02:53 +0200 Subject: [PATCH 46/50] Add HTTP response to HttpClientMetricsTagsContributor.Context (cherry picked from commit bd0b5e18e7132ff2052c554f29f5ed86dd523cba) --- .../micrometer/runtime/HttpClientMetricsTagsContributor.java | 3 +++ .../runtime/binder/vertx/VertxHttpClientMetrics.java | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpClientMetricsTagsContributor.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpClientMetricsTagsContributor.java index 54fc08a5ffe1b..c6c01ba80f54f 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpClientMetricsTagsContributor.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/HttpClientMetricsTagsContributor.java @@ -3,6 +3,7 @@ import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.config.MeterFilter; import io.vertx.core.spi.observability.HttpRequest; +import io.vertx.core.spi.observability.HttpResponse; /** * Allows code to add additional Micrometer {@link Tags} to the metrics collected for completed HTTP client requests. @@ -20,5 +21,7 @@ public interface HttpClientMetricsTagsContributor { interface Context { HttpRequest request(); + + HttpResponse response(); } } diff --git a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpClientMetrics.java b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpClientMetrics.java index 90db3fddf27f4..0d29a40c2c359 100644 --- a/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpClientMetrics.java +++ b/extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/vertx/VertxHttpClientMetrics.java @@ -172,7 +172,7 @@ public void responseEnd(RequestTracker tracker, long bytesRead) { .and(HttpCommonTags.status(tracker.response.statusCode())) .and(HttpCommonTags.outcome(tracker.response.statusCode())); if (!httpClientMetricsTagsContributors.isEmpty()) { - HttpClientMetricsTagsContributor.Context context = new DefaultContext(tracker.request); + HttpClientMetricsTagsContributor.Context context = new DefaultContext(tracker.request, tracker.response); for (int i = 0; i < httpClientMetricsTagsContributors.size(); i++) { try { Tags additionalTags = httpClientMetricsTagsContributors.get(i).contribute(context); @@ -254,6 +254,7 @@ public String getNormalizedUriPath(Map serverMatchPatterns, Lis } } - private record DefaultContext(HttpRequest request) implements HttpClientMetricsTagsContributor.Context { + private record DefaultContext(HttpRequest request, + HttpResponse response) implements HttpClientMetricsTagsContributor.Context { } } From 63463cb7719665ffd60ace990709da4a1d939b58 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 21 Jan 2025 17:11:09 +0100 Subject: [PATCH 47/50] Bump hibernate-reactive.version from 2.4.3.Final to 2.4.4.Final (cherry picked from commit 051d47d8a850cf29b702760dd155df39ddf951c5) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 89beca730c881..5d99f8dbdbfc2 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ 4.13.0 1.14.18 7.0.3.Final - 2.4.3.Final + 2.4.4.Final 8.0.2.Final 7.2.2.Final From d8f56cb6e90ce61627a5594d32f79673da737b4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:58:53 +0000 Subject: [PATCH 48/50] Bump hibernate-orm.version from 6.6.4.Final to 6.6.5.Final Bumps `hibernate-orm.version` from 6.6.4.Final to 6.6.5.Final. Updates `org.hibernate.orm:hibernate-core` from 6.6.4.Final to 6.6.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.4...6.6.5) Updates `org.hibernate.orm:hibernate-graalvm` from 6.6.4.Final to 6.6.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.4...6.6.5) Updates `org.hibernate.orm:hibernate-envers` from 6.6.4.Final to 6.6.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.4...6.6.5) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 6.6.4.Final to 6.6.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.4...6.6.5) Updates `org.hibernate:hibernate-jpamodelgen` from 6.6.4.Final to 6.6.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.4...6.6.5) Updates `org.hibernate.orm:hibernate-community-dialects` from 6.6.4.Final to 6.6.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.4...6.6.5) --- updated-dependencies: - dependency-name: org.hibernate.orm:hibernate-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-graalvm dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-envers dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate:hibernate-jpamodelgen dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-community-dialects dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 37d0453dbb3c0533d662d714bc2be157db3399fb) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5d99f8dbdbfc2..d961cc1731d24 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 0.8.12 7.0.1 5.5.0 - 6.6.4.Final + 6.6.5.Final 4.13.0 1.14.18 7.0.3.Final From b6bd029a5c093b86cf8137a80e5f5ca7097caf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 20 Dec 2024 16:25:21 +0100 Subject: [PATCH 49/50] Upgrade to ByteBuddy 1.15.11 (cherry picked from commit 46bb4cd7ed03f9ce0abd5cfbe2deaa571b158416) --- independent-projects/resteasy-reactive/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index f944ccdcc061f..668654b6d45c1 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -46,7 +46,7 @@ 4.1.0 3.2.3 - 1.14.11 + 1.15.11 5.10.5 3.9.9 3.27.2 diff --git a/pom.xml b/pom.xml index d961cc1731d24..a3b5f8fed4372 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ 5.5.0 6.6.5.Final 4.13.0 - 1.14.18 + 1.15.11 7.0.3.Final 2.4.4.Final 8.0.2.Final From 3a4cf412c7326d170f23c5099b8051ad85c1f9d5 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 22 Jan 2025 09:42:19 +0100 Subject: [PATCH 50/50] Remove quarkus-extension-processor from cache-runtime-spi It is not an extension so it shouldn't be treated as such. I have no idea why but it causes some issues when building with an empty repository so let's get rid of it given it's useless. --- extensions/cache/runtime-spi/pom.xml | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/extensions/cache/runtime-spi/pom.xml b/extensions/cache/runtime-spi/pom.xml index a0c57831b3890..e71a7105c1875 100644 --- a/extensions/cache/runtime-spi/pom.xml +++ b/extensions/cache/runtime-spi/pom.xml @@ -19,27 +19,4 @@ - - - - maven-compiler-plugin - - - default-compile - - - - io.quarkus - quarkus-extension-processor - ${project.version} - - - - - - - - - -