From 7169f9db54fb89e70edaeebc257bfb3001c05d3f Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 1 Mar 2023 23:42:47 -0800 Subject: [PATCH 01/26] Interpret negative/zero body-limit as infinite Currently, if body-limit is set to -1 or zero, there will be either an IOOB exception, or an empty String. Instead, interpret body-limit <= 0 as 'no limit'. (cherry picked from commit c587e2dd2ccfed5c257741a10930c6df969b32ff) --- .../resteasy/reactive/client/logging/DefaultClientLogger.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/logging/DefaultClientLogger.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/logging/DefaultClientLogger.java index 2f5b9c632e98e..a7884ecb1e561 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/logging/DefaultClientLogger.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/logging/DefaultClientLogger.java @@ -57,6 +57,8 @@ public void logRequest(HttpClientRequest request, Buffer body, boolean omitBody) private String bodyToString(Buffer body) { if (body == null) { return ""; + } else if (bodySize <= 0) { + return body.toString(); } else { String bodyAsString = body.toString(); return bodyAsString.substring(0, Math.min(bodySize, bodyAsString.length())); From 76f5131aef91cbb44d7d0a05e93ebc860cb8e2d2 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Tue, 31 Jan 2023 19:58:51 +0000 Subject: [PATCH 02/26] Allow same origin CORS requests without 3rd party origins being configured (cherry picked from commit f4dde34949810cf5ad96c07107aa2d7ff1487aab) --- ...SameOriginWithoutOriginConfigTestCase.java | 36 +++++++++++++++++++ .../conf/cors-same-origin-only.properties | 1 + .../vertx/http/runtime/cors/CORSFilter.java | 11 +++--- 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/cors/CORSSameOriginWithoutOriginConfigTestCase.java create mode 100644 extensions/vertx-http/deployment/src/test/resources/conf/cors-same-origin-only.properties diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/cors/CORSSameOriginWithoutOriginConfigTestCase.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/cors/CORSSameOriginWithoutOriginConfigTestCase.java new file mode 100644 index 0000000000000..2d288d0ed45bb --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/cors/CORSSameOriginWithoutOriginConfigTestCase.java @@ -0,0 +1,36 @@ +package io.quarkus.vertx.http.cors; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.nullValue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +class CORSSameOriginWithoutOriginConfigTestCase { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(BeanRegisteringRoute.class) + .addAsResource("conf/cors-same-origin-only.properties", "application.properties")); + + @Test + void corsSameOriginRequest() { + String origin = "http://localhost:8081"; + given().header("Origin", origin) + .get("/test").then() + .statusCode(200) + .header("Access-Control-Allow-Origin", origin); + } + + @Test + void corsInvalidSameOriginRequest() { + String origin = "http://externalhost:8081"; + given().header("Origin", origin) + .get("/test").then() + .statusCode(403) + .header("Access-Control-Allow-Origin", nullValue()); + } +} diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/cors-same-origin-only.properties b/extensions/vertx-http/deployment/src/test/resources/conf/cors-same-origin-only.properties new file mode 100644 index 0000000000000..228ebaf416aed --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/resources/conf/cors-same-origin-only.properties @@ -0,0 +1 @@ +quarkus.http.cors=true diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSFilter.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSFilter.java index 30d1ad80ec87e..9722e703417e2 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSFilter.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/cors/CORSFilter.java @@ -187,10 +187,13 @@ public void handle(RoutingContext event) { boolean allowsOrigin = wildcardOrigin; if (!allowsOrigin) { - allowsOrigin = !corsConfig.origins.isEmpty() - && (corsConfig.origins.get().contains(origin) - || isOriginAllowedByRegex(allowedOriginsRegex, origin) - || isSameOrigin(request, origin)); + if (!corsConfig.origins.isEmpty()) { + allowsOrigin = corsConfig.origins.get().contains(origin) + || isOriginAllowedByRegex(allowedOriginsRegex, origin) + || isSameOrigin(request, origin); + } else { + allowsOrigin = isSameOrigin(request, origin); + } } if (allowsOrigin) { From f69ceedd5513f89ac80b4041395e75685c874dc6 Mon Sep 17 00:00:00 2001 From: Gerhard Flothow Date: Tue, 28 Feb 2023 12:05:54 -0600 Subject: [PATCH 03/26] fix: remove incorrect default value for keepAliveEnabled (cherry picked from commit a42565be62e8bb8d403b4195a2e7e1e835a1ecb0) --- .../java/io/quarkus/restclient/config/RestClientConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java index 7d85ffc3f58e6..1b290a754a214 100644 --- a/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java +++ b/extensions/resteasy-classic/rest-client-config/runtime/src/main/java/io/quarkus/restclient/config/RestClientConfig.java @@ -200,7 +200,7 @@ public class RestClientConfig { /** * If set to false disables the keep alive completely. */ - @ConfigItem(defaultValue = "true") + @ConfigItem public Optional keepAliveEnabled; /** From f26970d04ba0944d4262aa9fe8136aa0066d31f6 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Thu, 23 Feb 2023 20:14:40 +0000 Subject: [PATCH 04/26] Ignore required documentation for @ConfigMapping default methods (cherry picked from commit 92a780437bcc908abaa872bdb471a73281e63bd0) --- .../processor/ExtensionAnnotationProcessor.java | 10 ++++++---- .../extest/runtime/config/TestMappingBuildTime.java | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/ExtensionAnnotationProcessor.java b/core/processor/src/main/java/io/quarkus/annotation/processor/ExtensionAnnotationProcessor.java index 1ef4fb6b8e3b3..000b96d0fede8 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/ExtensionAnnotationProcessor.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/ExtensionAnnotationProcessor.java @@ -512,7 +512,7 @@ private void processCtorConfigItem(ExecutableElement ctor, Properties javadocPro final String docComment = getRequiredJavadoc(ctor); final StringBuilder buf = new StringBuilder(); appendParamTypes(ctor, buf); - javadocProps.put(className + Constants.DOT + buf.toString(), docComment); + javadocProps.put(className + Constants.DOT + buf, docComment); } private void processMethodConfigItem(ExecutableElement method, Properties javadocProps, String className) { @@ -520,12 +520,14 @@ private void processMethodConfigItem(ExecutableElement method, Properties javado final StringBuilder buf = new StringBuilder(); buf.append(method.getSimpleName().toString()); appendParamTypes(method, buf); - javadocProps.put(className + Constants.DOT + buf.toString(), docComment); + javadocProps.put(className + Constants.DOT + buf, docComment); } private void processMethodConfigMapping(ExecutableElement method, Properties javadocProps, String className) { - final String docComment = getRequiredJavadoc(method); - javadocProps.put(className + Constants.DOT + method.getSimpleName().toString(), docComment); + if (method.getModifiers().contains(Modifier.ABSTRACT)) { + final String docComment = getRequiredJavadoc(method); + javadocProps.put(className + Constants.DOT + method.getSimpleName().toString(), docComment); + } } private void processConfigGroup(RoundEnvironment roundEnv, TypeElement annotation) { diff --git a/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTime.java b/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTime.java index 2701764b02835..a256fb4843b74 100644 --- a/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTime.java +++ b/integration-tests/test-extension/extension/runtime/src/main/java/io/quarkus/extest/runtime/config/TestMappingBuildTime.java @@ -46,4 +46,8 @@ interface Group { */ String value(); } + + default void mustNotRequireDocs() { + + } } From 62da24845f3de3748aa899975347f76318c8299c Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 2 Mar 2023 14:44:29 +0100 Subject: [PATCH 05/26] Scheduler - detect scheduled methods of the same name on a class - fix #31547 (cherry picked from commit e1f819d54515a55097ca01b33b2d2a677d4e053f) --- .../ScheduledBusinessMethodItem.java | 4 +++ .../deployment/SchedulerProcessor.java | 12 +++++-- ...leScheduledMethodsWithTheSameNameTest.java | 36 +++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/MultipleScheduledMethodsWithTheSameNameTest.java diff --git a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/ScheduledBusinessMethodItem.java b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/ScheduledBusinessMethodItem.java index acce3bce521f4..b303fc18369e0 100644 --- a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/ScheduledBusinessMethodItem.java +++ b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/ScheduledBusinessMethodItem.java @@ -48,4 +48,8 @@ public boolean isNonBlocking() { return nonBlocking; } + public String getMethodDescription() { + return method.declaringClass().name() + "#" + method.name() + "()"; + } + } diff --git a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java index a5b314107ad2b..5751a086d5c46 100644 --- a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java +++ b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java @@ -11,9 +11,11 @@ import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Function; @@ -191,17 +193,23 @@ void validateScheduledBusinessMethods(SchedulerConfig config, List validationErrors) { List errors = new ArrayList<>(); Map encounteredIdentities = new HashMap<>(); + Set methodDescriptions = new HashSet<>(); for (ScheduledBusinessMethodItem scheduledMethod : scheduledMethods) { + if (!methodDescriptions.add(scheduledMethod.getMethodDescription())) { + errors.add(new IllegalStateException("Multiple @Scheduled methods of the same name declared on the same class: " + + scheduledMethod.getMethodDescription())); + continue; + } MethodInfo method = scheduledMethod.getMethod(); if (Modifier.isAbstract(method.flags())) { errors.add(new IllegalStateException("@Scheduled method must not be abstract: " - + method.declaringClass().name() + "#" + method.name() + "()")); + + scheduledMethod.getMethodDescription())); continue; } if (Modifier.isPrivate(method.flags())) { errors.add(new IllegalStateException("@Scheduled method must not be private: " - + method.declaringClass().name() + "#" + method.name() + "()")); + + scheduledMethod.getMethodDescription())); continue; } diff --git a/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/MultipleScheduledMethodsWithTheSameNameTest.java b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/MultipleScheduledMethodsWithTheSameNameTest.java new file mode 100644 index 0000000000000..2edadd011e600 --- /dev/null +++ b/extensions/scheduler/deployment/src/test/java/io/quarkus/scheduler/test/MultipleScheduledMethodsWithTheSameNameTest.java @@ -0,0 +1,36 @@ +package io.quarkus.scheduler.test; + +import javax.enterprise.inject.spi.DeploymentException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.scheduler.Scheduled; +import io.quarkus.scheduler.ScheduledExecution; +import io.quarkus.test.QuarkusUnitTest; + +public class MultipleScheduledMethodsWithTheSameNameTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .setExpectedException(DeploymentException.class) + .withApplicationRoot((jar) -> jar + .addClasses(BeanWithInvalidScheduledMethods.class)); + + @Test + public void test() throws InterruptedException { + } + + static class BeanWithInvalidScheduledMethods { + + @Scheduled(cron = "0/1 * * * * ?") + void foo() { + } + + @Scheduled(every = "10s") + void foo(ScheduledExecution execution) { + } + + } + +} From 81fbd3b9a8a9cac6ba101932947bed96dc149c82 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 2 Mar 2023 21:24:00 +0200 Subject: [PATCH 06/26] Prevent SSE writing from potentially causing accumulation of headers This could happen if a MessageBodyWriter writes the headers map (which does not make sense for streaming responses, but it's nevertheless allowed by the contract of the MessageBodyWriter) Fixes: #31559 (cherry picked from commit 4cb3797d8d124901db426dbe2a1ab12da5cdcac8) --- .../client/handlers/ClientSetResponseEntityRestHandler.java | 4 ++-- .../resteasy/reactive/client/impl/InboundSseEventImpl.java | 3 ++- .../org/jboss/resteasy/reactive/common/core/Serialisers.java | 2 -- .../java/org/jboss/resteasy/reactive/server/core/SseUtil.java | 3 ++- .../jboss/resteasy/reactive/server/core/StreamingUtil.java | 3 ++- .../server/core/multipart/MultipartMessageBodyWriter.java | 3 ++- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java index 0e8c53a0c25c6..67202ff3da724 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java @@ -5,7 +5,6 @@ import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.StatusType; @@ -17,6 +16,7 @@ import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; import org.jboss.resteasy.reactive.common.core.Serialisers; import org.jboss.resteasy.reactive.common.jaxrs.StatusTypeImpl; +import org.jboss.resteasy.reactive.common.util.QuarkusMultivaluedHashMap; import io.vertx.core.buffer.Buffer; @@ -88,7 +88,7 @@ private ByteArrayInputStream entityStreamOfAbortedResponseOf(RestClientRequestCo entity = Entity.entity(untypedEntity, mediaType); } // FIXME: pass headers? - Buffer buffer = context.writeEntity(entity, (MultivaluedMap) Serialisers.EMPTY_MULTI_MAP, + Buffer buffer = context.writeEntity(entity, new QuarkusMultivaluedHashMap<>(), Serialisers.NO_WRITER_INTERCEPTOR); return new ByteArrayInputStream(buffer.getBytes()); } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InboundSseEventImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InboundSseEventImpl.java index 947097eed0841..015834e102d4f 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InboundSseEventImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InboundSseEventImpl.java @@ -14,6 +14,7 @@ import org.jboss.resteasy.reactive.common.core.Serialisers; import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl; +import org.jboss.resteasy.reactive.common.util.QuarkusMultivaluedHashMap; public class InboundSseEventImpl implements InboundSseEvent { @@ -120,7 +121,7 @@ public T readData(GenericType type, MediaType mediaType) { InputStream in = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)); try { return (T) ClientSerialisers.invokeClientReader(null, type.getRawType(), type.getType(), - mediaType, null, null, Serialisers.EMPTY_MULTI_MAP, + mediaType, null, null, new QuarkusMultivaluedHashMap<>(), serialisers, in, Serialisers.NO_READER_INTERCEPTOR, configuration); } catch (IOException e) { throw new UncheckedIOException(e); diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/Serialisers.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/Serialisers.java index 1f5020bae6a4b..e50003b531963 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/Serialisers.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/Serialisers.java @@ -14,7 +14,6 @@ import javax.ws.rs.RuntimeType; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; @@ -31,7 +30,6 @@ public abstract class Serialisers { public static final Annotation[] NO_ANNOTATION = new Annotation[0]; public static final ReaderInterceptor[] NO_READER_INTERCEPTOR = new ReaderInterceptor[0]; - public static final MultivaluedMap EMPTY_MULTI_MAP = new QuarkusMultivaluedHashMap<>(); public static final WriterInterceptor[] NO_WRITER_INTERCEPTOR = new WriterInterceptor[0]; protected static final Map, Class> primitivesToWrappers = new HashMap<>(); // FIXME: spec says we should use generic type, but not sure how to pass that type from Jandex to reflection diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/SseUtil.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/SseUtil.java index 24de690d468c9..510bf092abbc5 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/SseUtil.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/SseUtil.java @@ -18,6 +18,7 @@ import org.jboss.resteasy.reactive.common.core.Serialisers; import org.jboss.resteasy.reactive.common.util.CommonSseUtil; +import org.jboss.resteasy.reactive.common.util.QuarkusMultivaluedHashMap; import org.jboss.resteasy.reactive.server.handlers.PublisherResponseHandler; import org.jboss.resteasy.reactive.server.jaxrs.OutboundSseEventImpl; import org.jboss.resteasy.reactive.server.spi.ServerHttpResponse; @@ -137,7 +138,7 @@ private static String serialiseDataToString(ResteasyReactiveRequestContext conte if (writer.isWriteable(entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType)) { // FIXME: spec doesn't really say what headers we should use here writer.writeTo(entity, entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType, - Serialisers.EMPTY_MULTI_MAP, baos); + new QuarkusMultivaluedHashMap<>(), baos); wrote = true; break; } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java index 8f31bd6bbbe4e..c91e8938e2512 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java @@ -14,6 +14,7 @@ import javax.ws.rs.ext.MessageBodyWriter; import org.jboss.resteasy.reactive.common.core.Serialisers; +import org.jboss.resteasy.reactive.common.util.QuarkusMultivaluedHashMap; import org.jboss.resteasy.reactive.server.StreamingOutputStream; import org.jboss.resteasy.reactive.server.handlers.PublisherResponseHandler; import org.jboss.resteasy.reactive.server.spi.ServerHttpResponse; @@ -65,7 +66,7 @@ private static byte[] serialiseEntity(ResteasyReactiveRequestContext context, Ob if (writer.isWriteable(entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType)) { // FIXME: spec doesn't really say what headers we should use here writer.writeTo(entity, entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType, - Serialisers.EMPTY_MULTI_MAP, baos); + new QuarkusMultivaluedHashMap<>(), baos); wrote = true; break; } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java index f1bfb0484a99e..29907911d6339 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java @@ -24,6 +24,7 @@ import org.jboss.resteasy.reactive.common.core.Serialisers; import org.jboss.resteasy.reactive.common.reflection.ReflectionBeanFactoryCreator; +import org.jboss.resteasy.reactive.common.util.QuarkusMultivaluedHashMap; import org.jboss.resteasy.reactive.multipart.FileDownload; import org.jboss.resteasy.reactive.server.NoopCloseAndFlushOutputStream; import org.jboss.resteasy.reactive.server.core.CurrentRequestManager; @@ -189,7 +190,7 @@ private void writeEntity(OutputStream os, Object entity, MediaType mediaType, Re try (NoopCloseAndFlushOutputStream writerOutput = new NoopCloseAndFlushOutputStream(os)) { // FIXME: spec doesn't really say what headers we should use here writer.writeTo(entity, entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType, - Serialisers.EMPTY_MULTI_MAP, writerOutput); + new QuarkusMultivaluedHashMap<>(), writerOutput); wrote = true; } From 2f7f6df8e80b4c0b0db91b8f16bb769f82703848 Mon Sep 17 00:00:00 2001 From: Geert Schuring Date: Thu, 2 Mar 2023 10:33:29 +0100 Subject: [PATCH 07/26] Append System.lineSeparator() to config error messages Suggestion to solve https://github.com/quarkusio/quarkus/issues/31536 (cherry picked from commit ac4354d430f9d7ba31fc4da42277e22dc4f724ab) --- .../src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java index 5b5331cd74f53..a94e21620999a 100644 --- a/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java +++ b/extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ConfigRecorder.java @@ -60,7 +60,7 @@ public void validateConfigProperties(Set properties) { ConfigProducerUtil.getValue(property.getName(), effectivePropertyType, property.getDefaultValue(), config); } catch (Exception e) { msg.append("Failed to load config value of type ").append(effectivePropertyType).append(" for: ") - .append(property.getName()); + .append(property.getName()).append(System.lineSeparator()); problems.add(property.getName()); suppressed.add(e); } From 618acc379c8bb2470fad5636e0d7b2fc7f2f205c Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Fri, 3 Mar 2023 12:18:02 +0200 Subject: [PATCH 08/26] Reinitialize sun.security.pkcs11.P11Util at runtime Fixes build error: ``` Error: Detected a java.lang.ref.Cleaner object in the image heap which uses a daemon thread that invokes cleaning actions, but threads running in the image generator are no longer running at image runtime. To see how this object got instantiated use --trace-object-instantiation=java.lang.ref.Cleaner. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=. Or you can write your own initialization methods and call them explicitly from your main entry point. Detailed message: Trace: Object was reached by trying to constant fold static field sun.security.pkcs11.P11Util.cleaner at sun.security.pkcs11.wrapper.PKCS11.(PKCS11.java:170) parsing method sun.security.pkcs11.wrapper.PKCS11.(PKCS11.java:154) reachable via the parsing context at static root method.(Unknown Source) ``` (cherry picked from commit 80340d00529d1bc77888c3c03af734e88a2d90c1) --- .../java/io/quarkus/security/deployment/SecurityProcessor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java index 1e5caf7772d78..cbcb2cedfd245 100644 --- a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java +++ b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java @@ -223,6 +223,8 @@ private static void prepareBouncyCastleProvider(BuildProducer Date: Tue, 7 Mar 2023 08:57:47 +0100 Subject: [PATCH 09/26] Fix iterator issue when executing a zrange with score on a missing key (cherry picked from commit 048c4a9dfc3658998d7acbe39e25c926078514bd) --- .../redis/runtime/datasource/AbstractSortedSetCommands.java | 3 +++ .../io/quarkus/redis/datasource/SortedSetCommandsTest.java | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/AbstractSortedSetCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/AbstractSortedSetCommands.java index d0e63dee9960c..b6bf0d93c508d 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/AbstractSortedSetCommands.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/AbstractSortedSetCommands.java @@ -734,6 +734,9 @@ protected String getScoreAsString(double score) { final List> decodeAsListOfScoredValues(Response response) { List> list = new ArrayList<>(); + if (!response.iterator().hasNext()) { + return Collections.emptyList(); + } if (response.iterator().next().type() == ResponseType.BULK) { // Redis 5 V current = null; diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/SortedSetCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/SortedSetCommandsTest.java index bb59e5f8acd19..b5f14cfad65de 100644 --- a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/SortedSetCommandsTest.java +++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/SortedSetCommandsTest.java @@ -468,6 +468,12 @@ void zrevrange() { .isEqualTo(List.of(Place.suze, Place.grignan, Place.crussol)); } + @Test + @RequiresRedis6OrHigher + void zrevrangeWithScoreEmpty() { + assertThat(ds.sortedSet(String.class).zrangeWithScores("top-products", 0, 2, new ZRangeArgs().rev())).isEmpty(); + } + @Test @RequiresRedis6OrHigher void zrevrangeWithScores() { From c839d0951bfafe87861405d789c5b6279e26bd2e Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 9 Mar 2023 09:59:55 +0200 Subject: [PATCH 10/26] Make request scoped beans work properly in ReaderInterceptors Fixes: #31692 (cherry picked from commit 067011ef0d9889d0d99ce8f9ff8ebe067c2d27ef) --- .../reactive/server/handlers/RequestDeserializeHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java index 701946a95ca5c..b2176079cfe9d 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java @@ -47,6 +47,7 @@ public RequestDeserializeHandler(Class type, Type genericType, List Date: Thu, 9 Mar 2023 11:37:55 +0200 Subject: [PATCH 11/26] Properly close metadata file in integration tests Fixes: #31713 (cherry picked from commit 4d22c5fce947a49913cf3e3c0c4f79f5243d2672) --- .../java/io/quarkus/test/junit/IntegrationTestUtil.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java index f3aa07dc40270..50b565e3b9f34 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java @@ -433,9 +433,10 @@ static Properties readQuarkusArtifactProperties(ExtensionContext context) { } throw new IllegalStateException(errorMessage); } - try { - Properties properties = new Properties(); - properties.load(new FileInputStream(artifactProperties.toFile())); + + Properties properties = new Properties(); + try (var fis = new FileInputStream(artifactProperties.toFile())) { + properties.load(fis); return properties; } catch (IOException e) { throw new UncheckedIOException( From 464ef1284b52bc9fcbb672ab9290cc45205bd6f7 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 9 Mar 2023 13:09:28 +0100 Subject: [PATCH 12/26] Register additional cache implementations for reflection Trying to find a good compromise for #30744 (cherry picked from commit 8746898ebbf3861c7ab4b300a2ac91ee0d2ba09f) --- .../caffeine/runtime/graal/CacheConstructorsFeature.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/caffeine/runtime/src/main/java/io/quarkus/caffeine/runtime/graal/CacheConstructorsFeature.java b/extensions/caffeine/runtime/src/main/java/io/quarkus/caffeine/runtime/graal/CacheConstructorsFeature.java index 8456a80860cf2..ac2bf2db5b498 100644 --- a/extensions/caffeine/runtime/src/main/java/io/quarkus/caffeine/runtime/graal/CacheConstructorsFeature.java +++ b/extensions/caffeine/runtime/src/main/java/io/quarkus/caffeine/runtime/graal/CacheConstructorsFeature.java @@ -80,6 +80,8 @@ public static String[] typesNeedingConstructorsRegistered() { "com.github.benmanes.caffeine.cache.PSMS", "com.github.benmanes.caffeine.cache.PSW", "com.github.benmanes.caffeine.cache.PSMW", + "com.github.benmanes.caffeine.cache.PSAMW", + "com.github.benmanes.caffeine.cache.PSAWMW", "com.github.benmanes.caffeine.cache.PSWMS", "com.github.benmanes.caffeine.cache.PSWMW", "com.github.benmanes.caffeine.cache.SILMS", @@ -88,6 +90,7 @@ public static String[] typesNeedingConstructorsRegistered() { "com.github.benmanes.caffeine.cache.SSLMS", "com.github.benmanes.caffeine.cache.SSMS", "com.github.benmanes.caffeine.cache.SSMSA", + "com.github.benmanes.caffeine.cache.SSMSAW", "com.github.benmanes.caffeine.cache.SSMSW", "com.github.benmanes.caffeine.cache.SSW", }; @@ -102,6 +105,7 @@ public static String[] typesNeedingConstructorsRegisteredWhenRecordingStats() { "com.github.benmanes.caffeine.cache.SSSMS", "com.github.benmanes.caffeine.cache.SSSMSA", "com.github.benmanes.caffeine.cache.SSSMSW", + "com.github.benmanes.caffeine.cache.SSSMSAW", "com.github.benmanes.caffeine.cache.SSSW" }; } From ee7f8678580e8f47331c08d5d5ebf1922bcde702 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Wed, 8 Mar 2023 12:48:42 +0000 Subject: [PATCH 13/26] Suppress config changed warning for quarkus.test.arg-line (cherry picked from commit 2690a1a924977175810452d3139a48348a34b6b6) --- .../io/quarkus/deployment/steps/ConfigGenerationBuildStep.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java index 080ac28efb2e2..2dd58393dc4d2 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java @@ -354,6 +354,7 @@ public void suppressNonRuntimeConfigChanged( suppressNonRuntimeConfigChanged.produce(new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.uuid")); suppressNonRuntimeConfigChanged.produce(new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.default-locale")); suppressNonRuntimeConfigChanged.produce(new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.locales")); + suppressNonRuntimeConfigChanged.produce(new SuppressNonRuntimeConfigChangedWarningBuildItem("quarkus.test.arg-line")); } /** From 23faec591dbf5074b859c3fded2bdf806798495b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Crocquesel?= <88554524+scrocquesel@users.noreply.github.com> Date: Wed, 8 Mar 2023 00:16:26 +0100 Subject: [PATCH 14/26] Remove all dev services for kubernetes dependencies from kubernetes-client-internal (cherry picked from commit 014ac6195e69a66866d796df34f4b6f992d0c615) --- .../kubernetes-client/deployment-internal/pom.xml | 10 ---------- extensions/kubernetes-client/deployment/pom.xml | 15 +++++++++++++++ .../deployment/NoQuarkusTestKubernetesClient.java | 0 3 files changed, 15 insertions(+), 10 deletions(-) rename extensions/kubernetes-client/{deployment-internal => deployment}/src/main/java/io/quarkus/kubernetes/client/deployment/NoQuarkusTestKubernetesClient.java (100%) diff --git a/extensions/kubernetes-client/deployment-internal/pom.xml b/extensions/kubernetes-client/deployment-internal/pom.xml index cdc98dd79a965..439a68d1f9472 100644 --- a/extensions/kubernetes-client/deployment-internal/pom.xml +++ b/extensions/kubernetes-client/deployment-internal/pom.xml @@ -38,16 +38,6 @@ io.quarkus quarkus-devservices-deployment - - com.dajudge.kindcontainer - kindcontainer - - - junit - junit - - - diff --git a/extensions/kubernetes-client/deployment/pom.xml b/extensions/kubernetes-client/deployment/pom.xml index 8e0de1efd6ad2..285cecb1f4da4 100644 --- a/extensions/kubernetes-client/deployment/pom.xml +++ b/extensions/kubernetes-client/deployment/pom.xml @@ -25,6 +25,21 @@ io.quarkus quarkus-jackson-deployment + + com.dajudge.kindcontainer + kindcontainer + + + junit + junit + + + + + io.quarkus + quarkus-junit5-internal + test + diff --git a/extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/NoQuarkusTestKubernetesClient.java b/extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/NoQuarkusTestKubernetesClient.java similarity index 100% rename from extensions/kubernetes-client/deployment-internal/src/main/java/io/quarkus/kubernetes/client/deployment/NoQuarkusTestKubernetesClient.java rename to extensions/kubernetes-client/deployment/src/main/java/io/quarkus/kubernetes/client/deployment/NoQuarkusTestKubernetesClient.java From 9ea0e710be951790a2249f3572877d57e3b696e8 Mon Sep 17 00:00:00 2001 From: Christian Berger Date: Fri, 10 Mar 2023 17:16:58 +0100 Subject: [PATCH 15/26] feat: capability added for quarkus-rest-client-reactive-jackson (cherry picked from commit 728efc59bb15c8f05546a73c876dc19f62f1ff8e) --- .../src/main/java/io/quarkus/deployment/Capability.java | 1 + .../rest-client-reactive-jackson/runtime/pom.xml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java index 776b85fcb21cf..5f8d5a41a4e0f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java @@ -42,6 +42,7 @@ public interface Capability { String REST = QUARKUS_PREFIX + "rest"; String REST_CLIENT = REST + ".client"; String REST_CLIENT_REACTIVE = REST_CLIENT + ".reactive"; + String REST_CLIENT_REACTIVE_JACKSON = REST_CLIENT_REACTIVE + ".jackson"; String REST_JACKSON = REST + ".jackson"; String REST_JSONB = REST + ".jsonb"; diff --git a/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml index b075ab7f76f30..cf12a23456091 100644 --- a/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml +++ b/extensions/resteasy-reactive/rest-client-reactive-jackson/runtime/pom.xml @@ -32,6 +32,11 @@ io.quarkus quarkus-extension-maven-plugin + + + io.quarkus.rest.client.reactive.jackson + + maven-compiler-plugin From df2b576e12a67db25f4bf37a7aed93837c88efae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 10 Mar 2023 14:45:08 +0100 Subject: [PATCH 16/26] Remove duplicate handling of oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource We already handle that in OracleMetadataOverrides (cherry picked from commit 505fc5492f42e57d4c494a60d70222fc14546f82) --- .../quarkus/jdbc/oracle/deployment/OracleNativeImage.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleNativeImage.java b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleNativeImage.java index 8778e677ca104..51053a2fb0765 100644 --- a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleNativeImage.java +++ b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleNativeImage.java @@ -3,7 +3,6 @@ import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; -import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; /** * @author Sanne Grinovero @@ -35,10 +34,4 @@ void reflection(BuildProducer reflectiveClass) { reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, "com.sun.jndi.rmi.registry.RegistryContextFactory")); } - @BuildStep - void runtimeInitialization(BuildProducer runtimeInitializedClass) { - runtimeInitializedClass - .produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource")); - } - } From 202b7334ebede567a89e714d253bee7f2bd6b4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 10 Mar 2023 14:48:24 +0100 Subject: [PATCH 17/26] Reinitialize oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource at runtime (cherry picked from commit 77062838587b25c6c8a18dddda6e26f2995348b7) --- .../oracle/deployment/OracleMetadataOverrides.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleMetadataOverrides.java b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleMetadataOverrides.java index fbabaecceb2d5..892ef190c539f 100644 --- a/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleMetadataOverrides.java +++ b/extensions/jdbc/jdbc-oracle/deployment/src/main/java/io/quarkus/jdbc/oracle/deployment/OracleMetadataOverrides.java @@ -9,6 +9,7 @@ import io.quarkus.deployment.builditem.nativeimage.NativeImageAllowIncompleteClasspathBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.RuntimeReinitializedClassBuildItem; import io.quarkus.maven.dependency.ArtifactKey; /** @@ -66,7 +67,8 @@ void build(BuildProducer reflectiveClass) { } @BuildStep - void runtimeInitializeDriver(BuildProducer runtimeInitialized) { + void runtimeInitializeDriver(BuildProducer runtimeInitialized, + BuildProducer runtimeReinitialized) { //These re-implement all the "--initialize-at-build-time" arguments found in the native-image.properties : // Override: the original metadata marks the drivers as "runtime initialized" but this makes it incompatible with @@ -93,10 +95,14 @@ void runtimeInitializeDriver(BuildProducer run runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TcpMultiplexer")); runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TcpMultiplexer")); runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.net.nt.TcpMultiplexer$LazyHolder")); - runtimeInitialized - .produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource")); - runtimeInitialized.produce(new RuntimeInitializedClassBuildItem( + + // Needs to be REinitialized to avoid problems when also using the Elasticsearch Java client + // See https://github.com/quarkusio/quarkus/issues/31624#issuecomment-1457706253 + runtimeReinitialized.produce(new RuntimeReinitializedClassBuildItem( + "oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource")); + runtimeReinitialized.produce(new RuntimeReinitializedClassBuildItem( "oracle.jdbc.driver.BlockSource$ThreadedCachingBlockSource$BlockReleaser")); + runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.xa.client.OracleXADataSource")); runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.replay.OracleXADataSourceImpl")); runtimeInitialized.produce(new RuntimeInitializedClassBuildItem("oracle.jdbc.datasource.OracleXAConnection")); From 12be197f3ef82fd70a73c6f964ff8ea2dd26bdcc Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Wed, 15 Mar 2023 13:41:00 +0100 Subject: [PATCH 18/26] Qute type-safe fragments - fix validation for loop metadata and globals - currently loop section metadata and global variables result in a build failure (cherry picked from commit 8b3918f757637a80de5e073251f9ce0e0e2d5067) --- .../qute/deployment/QuteProcessor.java | 58 +++++++++++-------- .../fragment/CheckedTemplateFragmentTest.java | 12 +++- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index 7af0eb4bcea36..721d56e9f8350 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -647,6 +647,7 @@ public void beforeParsing(ParserHelper parserHelper) { @BuildStep void validateCheckedFragments(List validations, List expressionMatches, + List templateGlobals, BeanArchiveIndexBuildItem beanArchiveIndex, BuildProducer validationErrors) { @@ -657,6 +658,8 @@ void validateCheckedFragments(List validatio Map assignableCache = new HashMap<>(); String[] hintPrefixes = { LoopSectionHelper.Factory.HINT_PREFIX, WhenSectionHelper.Factory.HINT_PREFIX, SetSectionHelper.Factory.HINT_PREFIX }; + Set globals = templateGlobals.stream().map(TemplateGlobalBuildItem::getName) + .collect(Collectors.toUnmodifiableSet()); for (CheckedFragmentValidationBuildItem validation : validations) { Map paramNamesToTypes = new HashMap<>(); @@ -672,33 +675,40 @@ void validateCheckedFragments(List validatio } for (Expression expression : validation.fragmentExpressions) { - // Note that we ignore literals and expressions with no type info and expressions with a hint referencing an expression from inside the fragment - if (expression.isLiteral()) { + // Note that we ignore: + // - literals, + // - globals, + // - expressions with no type info, + // - loop metadata; e.g. |java.lang.Integer| + // - expressions with a hint referencing an expression from inside the fragment + if (expression.isLiteral() || globals.contains(expression.getParts().get(0).getName())) { continue; } - if (expression.hasTypeInfo()) { - Info info = TypeInfos.create(expression, index, null).get(0); - if (info.isTypeInfo()) { - // |org.acme.Foo|.name - paramNamesToTypes.put(expression.getParts().get(0).getName(), info.asTypeInfo().resolvedType); - } else if (info.hasHints()) { - // foo.name - hintLoop: for (String helperHint : info.asHintInfo().hints) { - for (String prefix : hintPrefixes) { - if (helperHint.startsWith(prefix)) { - int generatedId = parseHintId(helperHint, prefix); - Expression localExpression = findExpression(generatedId, validation.fragmentExpressions); - if (localExpression == null) { - Match match = matchResults.getMatch(generatedId); - if (match == null) { - throw new IllegalStateException( - "Match result not found for expression [" + expression.toOriginalString() - + "] in: " - + validation.templateId); - } - paramNamesToTypes.put(expression.getParts().get(0).getName(), match.type); - break hintLoop; + String typeInfo = expression.getParts().get(0).getTypeInfo(); + if (typeInfo == null || (typeInfo != null && typeInfo.endsWith(LoopSectionHelper.Factory.HINT_METADATA))) { + continue; + } + Info info = TypeInfos.create(expression, index, null).get(0); + if (info.isTypeInfo()) { + // |org.acme.Foo|.name + paramNamesToTypes.put(expression.getParts().get(0).getName(), info.asTypeInfo().resolvedType); + } else if (info.hasHints()) { + // foo.name + hintLoop: for (String helperHint : info.asHintInfo().hints) { + for (String prefix : hintPrefixes) { + if (helperHint.startsWith(prefix)) { + int generatedId = parseHintId(helperHint, prefix); + Expression localExpression = findExpression(generatedId, validation.fragmentExpressions); + if (localExpression == null) { + Match match = matchResults.getMatch(generatedId); + if (match == null) { + throw new IllegalStateException( + "Match result not found for expression [" + expression.toOriginalString() + + "] in: " + + validation.templateId); } + paramNamesToTypes.put(expression.getParts().get(0).getName(), match.type); + break hintLoop; } } } diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/fragment/CheckedTemplateFragmentTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/fragment/CheckedTemplateFragmentTest.java index 6f7f7ba7af2cb..c5a7cb3502be3 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/fragment/CheckedTemplateFragmentTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/fragment/CheckedTemplateFragmentTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.qute.CheckedTemplate; +import io.quarkus.qute.TemplateGlobal; import io.quarkus.qute.TemplateInstance; import io.quarkus.test.QuarkusUnitTest; @@ -20,21 +21,30 @@ public class CheckedTemplateFragmentTest { .addClasses(Templates.class, Item.class) .addAsResource(new StringAsset( "{#each items}{#fragment id='item'}{it.name}{#if it.name.length > 5} is a long name{/if}{/fragment}{/each}"), - "templates/CheckedTemplateFragmentTest/items.html")); + "templates/CheckedTemplateFragmentTest/items.html") + .addAsResource(new StringAsset( + "{#fragment id=foo}{#for i in bar}{i_count}. <{i}>{#if i_hasNext}, {/if}{/for}{/fragment}"), + "templates/CheckedTemplateFragmentTest/foos.html")); @Test public void testFragment() { assertEquals("Foo", Templates.items(null).getFragment("item").data("it", new Item("Foo")).render()); assertEquals("Foo", Templates.items$item(new Item("Foo")).render()); assertEquals("FooAndBar is a long name", Templates.items$item(new Item("FooAndBar")).render()); + assertEquals("1. <1>, 2. <2>, 3. <3>, 4. <4>, 5. <5>", Templates.foos$foo().render()); } @CheckedTemplate public static class Templates { + @TemplateGlobal + static int bar = 5; + static native TemplateInstance items(List items); static native TemplateInstance items$item(Item it); + + static native TemplateInstance foos$foo(); } } From 0566557bce3634e05d8b4fd51de889eadba93ea7 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 15 Mar 2023 13:49:04 +0200 Subject: [PATCH 19/26] Fix incorrect generic type passed to MessageBodyWriter#writeTo Fixes: #31818 (cherry picked from commit 5221dac7d21fcaa8fde174b2013f76bf63e66cae) --- .../server/core/ServerSerialisers.java | 19 +++++++++++-------- .../simple/SimpleQuarkusRestResource.java | 6 ++++++ .../simple/SimpleQuarkusRestTestCase.java | 2 ++ .../server/vertx/test/simple/TestWriter.java | 6 +++++- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ServerSerialisers.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ServerSerialisers.java index 2a2377cc3e021..705d96fcef9d7 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ServerSerialisers.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ServerSerialisers.java @@ -198,16 +198,19 @@ public static boolean invokeWriter(ResteasyReactiveRequestContext context, Objec WriterInterceptor[] writerInterceptors = context.getWriterInterceptors(); boolean outputStreamSet = context.getOutputStream() != null; context.serverResponse().setPreCommitListener(HEADER_FUNCTION); + + RuntimeResource target = context.getTarget(); + Type genericType; + if (context.hasGenericReturnType()) { // make sure that when a Response with a GenericEntity was returned, we use it + genericType = context.getGenericReturnType(); + } else { + genericType = target == null ? null : target.getReturnType(); + } + try { if (writer instanceof ServerMessageBodyWriter && writerInterceptors == null && !outputStreamSet) { ServerMessageBodyWriter quarkusRestWriter = (ServerMessageBodyWriter) writer; - RuntimeResource target = context.getTarget(); - Type genericType; - if (context.hasGenericReturnType()) { // make sure that when a Response with a GenericEntity was returned, we use it - genericType = context.getGenericReturnType(); - } else { - genericType = target == null ? null : target.getReturnType(); - } + Class entityClass = entity.getClass(); if (quarkusRestWriter.isWriteable( entityClass, @@ -230,7 +233,7 @@ public static boolean invokeWriter(ResteasyReactiveRequestContext context, Objec context.setResponseContentType(mediaType); } if (writerInterceptors == null) { - writer.writeTo(entity, entity.getClass(), context.getGenericReturnType(), + writer.writeTo(entity, entity.getClass(), genericType, context.getAllAnnotations(), context.getResponseMediaType(), response.getHeaders(), context.getOrCreateOutputStream()); context.getOrCreateOutputStream().close(); diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/SimpleQuarkusRestResource.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/SimpleQuarkusRestResource.java index 5570f23f853ee..86104b92e38aa 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/SimpleQuarkusRestResource.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/SimpleQuarkusRestResource.java @@ -233,6 +233,12 @@ public TestClass writer() { return new TestClass(); } + @GET + @Path("uni-writer") + public Uni uniWriter() { + return Uni.createFrom().item(new TestClass()); + } + @GET @Path("fast-writer") @Produces("text/plain") diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/SimpleQuarkusRestTestCase.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/SimpleQuarkusRestTestCase.java index 1324e5ca35a5a..1a3c0266a27ae 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/SimpleQuarkusRestTestCase.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/SimpleQuarkusRestTestCase.java @@ -206,6 +206,8 @@ public void testWriter() { .then().body(Matchers.equalTo("OK")); RestAssured.get("/simple/writer") .then().body(Matchers.equalTo("WRITER")); + RestAssured.get("/simple/uni-writer") + .then().body(Matchers.equalTo("WRITER")); RestAssured.get("/simple/fast-writer") .then().body(Matchers.equalTo("OK")); diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/TestWriter.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/TestWriter.java index 18f8a2194846f..673f8dcdefd92 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/TestWriter.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/simple/TestWriter.java @@ -26,7 +26,11 @@ public boolean isWriteable(Class type, Type genericType, Annotation[] annotat public void writeTo(TestClass t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { - entityStream.write("WRITER".getBytes(StandardCharsets.UTF_8)); + if (genericType.getTypeName().equals(TestClass.class.getName())) { + entityStream.write("WRITER".getBytes(StandardCharsets.UTF_8)); + } else { + entityStream.write("INCORRECT GENERIC TYPE".getBytes(StandardCharsets.UTF_8)); + } } } From 6bc9ece6b6530fe50b99bb2f86b09b9a013259c8 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 15 Mar 2023 13:54:45 +0200 Subject: [PATCH 20/26] Add logging to RequestDeserializeHandler This makes it easier to diagnose cases returned HTTP 415 (cherry picked from commit 304d30ec1c4ea4aac8d984b3d9b44439fab6f789) --- .../reactive/server/handlers/RequestDeserializeHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java index b2176079cfe9d..2bfc2b7e1dbf5 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RequestDeserializeHandler.java @@ -54,6 +54,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti try { effectiveRequestType = MediaType.valueOf((String) requestType); } catch (Exception e) { + log.debugv("Incorrect media type", e); throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST).build()); } @@ -70,6 +71,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti } List> readers = serialisers.findReaders(null, type, effectiveRequestType, RuntimeType.SERVER); if (readers.isEmpty()) { + log.debugv("No matching MessageBodyReader found for type {0} and media type {1}", type, effectiveRequestType); throw new NotSupportedException(); } for (MessageBodyReader reader : readers) { @@ -102,6 +104,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti return; } } + log.debugv("No matching MessageBodyReader found for type {0} and media type {1}", type, effectiveRequestType); throw new NotSupportedException("No supported MessageBodyReader found"); } From 09f077b3623f927d4bff243c8cd037a443465308 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Wed, 1 Mar 2023 09:37:24 +0100 Subject: [PATCH 21/26] Filter out RESTEasy related warning in ProviderConfigInjectionWarningsTest We sometimes have RESTEasy complaining about an Apache HTTP Client not being closed. (cherry picked from commit ea361493010b5c9472027399dc0fce0eab1d3ffc) --- .../test/config/ProviderConfigInjectionWarningsTest.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/config/ProviderConfigInjectionWarningsTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/config/ProviderConfigInjectionWarningsTest.java index 0eb3bdd0d6bd0..3a9cc5435455b 100644 --- a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/config/ProviderConfigInjectionWarningsTest.java +++ b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/config/ProviderConfigInjectionWarningsTest.java @@ -36,8 +36,13 @@ public class ProviderConfigInjectionWarningsTest { static final QuarkusUnitTest TEST = new QuarkusUnitTest() .setLogRecordPredicate(record -> record.getLevel().intValue() >= Level.WARNING.intValue()) .assertLogRecords(logRecords -> { - assertEquals(4, logRecords.size()); - Set messages = logRecords.stream().map(LogRecord::getMessage).collect(Collectors.toSet()); + Set messages = logRecords.stream() + .map(LogRecord::getMessage) + // filter out noise coming from RESTEasy + .filter(m -> !m.contains("RESTEASY004687")) + .collect(Collectors.toSet()); + + assertEquals(4, messages.size()); assertTrue(messages.contains( "Directly injecting a org.eclipse.microprofile.config.Config into a javax.ws.rs.ext.Provider may lead to unexpected results. To ensure proper results, please change the type of the field to javax.enterprise.inject.Instance. Offending field is 'config' of class 'io.quarkus.resteasy.test.config.ProviderConfigInjectionWarningsTest$FooProvider'")); assertTrue(messages.contains( From 5fcaebc0f9f28da50cf415c464dd703567ffad63 Mon Sep 17 00:00:00 2001 From: Holly Cummins Date: Thu, 16 Mar 2023 09:13:47 +0000 Subject: [PATCH 22/26] Supply missing extension metadata for reactive keycloak client (cherry picked from commit 6110d38e631c9b2e759697a6e77cf74fbb6cde53) --- .../java/resources/META-INF/quarkus-extension.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 extensions/keycloak-admin-client-reactive/runtime/src/main/java/resources/META-INF/quarkus-extension.yaml diff --git a/extensions/keycloak-admin-client-reactive/runtime/src/main/java/resources/META-INF/quarkus-extension.yaml b/extensions/keycloak-admin-client-reactive/runtime/src/main/java/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000000000..92570a014faf2 --- /dev/null +++ b/extensions/keycloak-admin-client-reactive/runtime/src/main/java/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,14 @@ +--- +artifact: ${project.groupId}:${project.artifactId}:${project.version} +name: "Reactive Keycloak Admin Client" +metadata: + keywords: + - "keycloak" + - "keycloak-admin-client" + - "admin" + - "openid-connect" + - "reactive" + categories: + - "security" + - "reactive" + status: "stable" \ No newline at end of file From aa5b1639171625fd4f5eaa8894ac29a6de7f3ce7 Mon Sep 17 00:00:00 2001 From: Jose Date: Thu, 16 Mar 2023 09:57:46 +0100 Subject: [PATCH 23/26] Fix truststore REST Client config when password is not set Relates https://github.com/quarkusio/quarkus/pull/27925#issuecomment-1471446028 (cherry picked from commit 4502d6a78d994d2502b839649bf2408568f5a451) --- .../resteasy/reactive/client/impl/ClientBuilderImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java index 9d323b012a410..062c9ecd26185 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientBuilderImpl.java @@ -173,9 +173,6 @@ public ClientBuilder clientLogger(ClientLogger clientLogger) { @Override public ClientImpl build() { - Buffer keyStore = asBuffer(this.keyStore, keystorePassword); - Buffer trustStore = asBuffer(this.trustStore, this.trustStorePassword); - HttpClientOptions options = Optional.ofNullable(configuration.getFromContext(HttpClientOptions.class)) .orElseGet(HttpClientOptions::new); @@ -185,6 +182,9 @@ public ClientImpl build() { options.setVerifyHost(false); } + char[] effectiveTrustStorePassword = trustStorePassword == null ? EMPTY_CHAR_ARARAY : trustStorePassword; + Buffer keyStore = asBuffer(this.keyStore, keystorePassword); + Buffer trustStore = asBuffer(this.trustStore, effectiveTrustStorePassword); if (keyStore != null || trustStore != null) { options = options.setSsl(true); if (keyStore != null) { @@ -196,7 +196,7 @@ public ClientImpl build() { if (trustStore != null) { JksOptions jks = new JksOptions(); jks.setValue(trustStore); - jks.setPassword(trustStorePassword == null ? "" : new String(trustStorePassword)); + jks.setPassword(new String(effectiveTrustStorePassword)); options.setTrustStoreOptions(jks); } } From e89ccd2ae10416f3cd7c8be8a9f97f5e87f6c9c6 Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Thu, 16 Mar 2023 23:21:23 +0100 Subject: [PATCH 24/26] Add more lenient Liquibase ZipPathHandler to work around includeAll not working in prod mode Workaround until https://github.com/liquibase/liquibase/issues/3524 is fixed (cherry picked from commit c33b9b75e710fcfde0d7578f75a23042793dcce3) --- .../LiquibaseLenientZipPathHandler.java | 24 ++++++++++++ .../services/liquibase.resource.PathHandler | 1 + integration-tests/liquibase/pom.xml | 32 ++++++++++++++++ .../liquibase/LiquibaseFunctionalityPMT.java | 38 +++++++++++++++++++ .../liquibase/LiquibaseFunctionalityTest.java | 6 ++- 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/LiquibaseLenientZipPathHandler.java create mode 100644 extensions/liquibase/runtime/src/main/resources/META-INF/services/liquibase.resource.PathHandler create mode 100644 integration-tests/liquibase/src/test/java/io/quarkus/it/liquibase/LiquibaseFunctionalityPMT.java diff --git a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/LiquibaseLenientZipPathHandler.java b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/LiquibaseLenientZipPathHandler.java new file mode 100644 index 0000000000000..143dd5e291ba8 --- /dev/null +++ b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/LiquibaseLenientZipPathHandler.java @@ -0,0 +1,24 @@ +package io.quarkus.liquibase; + +import java.io.FileNotFoundException; + +import liquibase.resource.ResourceAccessor; +import liquibase.resource.ZipPathHandler; + +// https://github.com/liquibase/liquibase/issues/3524#issuecomment-1465282155 +public class LiquibaseLenientZipPathHandler extends ZipPathHandler { + + @Override + public int getPriority(String root) { + if (root != null && root.startsWith("jar:") && root.contains("!/")) { + return PRIORITY_SPECIALIZED; + } + return PRIORITY_NOT_APPLICABLE; + } + + @Override + public ResourceAccessor getResourceAccessor(String root) throws FileNotFoundException { + int idx = root.indexOf("!/"); + return super.getResourceAccessor(idx > 0 ? root.substring(0, idx) : root); + } +} \ No newline at end of file diff --git a/extensions/liquibase/runtime/src/main/resources/META-INF/services/liquibase.resource.PathHandler b/extensions/liquibase/runtime/src/main/resources/META-INF/services/liquibase.resource.PathHandler new file mode 100644 index 0000000000000..3ab18217b94b6 --- /dev/null +++ b/extensions/liquibase/runtime/src/main/resources/META-INF/services/liquibase.resource.PathHandler @@ -0,0 +1 @@ +io.quarkus.liquibase.LiquibaseLenientZipPathHandler diff --git a/integration-tests/liquibase/pom.xml b/integration-tests/liquibase/pom.xml index b799d8b7f1078..16f35371b55b6 100644 --- a/integration-tests/liquibase/pom.xml +++ b/integration-tests/liquibase/pom.xml @@ -12,6 +12,12 @@ quarkus-integration-test-liquibase Quarkus - Integration Tests - Liquibase Module that contains Liquibase related tests + + + false + ${skipTests} + + io.quarkus @@ -35,6 +41,11 @@ quarkus-junit5 test + + io.quarkus + quarkus-junit5-internal + test + io.quarkus quarkus-test-h2 @@ -126,6 +137,27 @@ + + maven-surefire-plugin + + + + prod-mode + test + + test + + + **/*PMT.java + ${skipPMTests} + + + + diff --git a/integration-tests/liquibase/src/test/java/io/quarkus/it/liquibase/LiquibaseFunctionalityPMT.java b/integration-tests/liquibase/src/test/java/io/quarkus/it/liquibase/LiquibaseFunctionalityPMT.java new file mode 100644 index 0000000000000..1bcb6cb604d16 --- /dev/null +++ b/integration-tests/liquibase/src/test/java/io/quarkus/it/liquibase/LiquibaseFunctionalityPMT.java @@ -0,0 +1,38 @@ +package io.quarkus.it.liquibase; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.LogFile; +import io.quarkus.test.QuarkusProdModeTest; + +public class LiquibaseFunctionalityPMT { + + @RegisterExtension + static final QuarkusProdModeTest config = new QuarkusProdModeTest() + .withApplicationRoot(jar -> jar + .addClasses(LiquibaseApp.class, LiquibaseFunctionalityResource.class) + .addAsResource("db") + .addAsResource("application.properties")) + .setApplicationName("liquibase-prodmode-test") + .setLogFileName("liquibase-prodmode-test.log") + .setRun(true); + + @LogFile + private Path logfile; + + @Test + public void test() { + LiquibaseFunctionalityTest.doTestLiquibaseQuarkusFunctionality(true); + } + + @AfterEach + void dumpLog() throws IOException { + System.out.println(Files.readString(logfile)); + } +} diff --git a/integration-tests/liquibase/src/test/java/io/quarkus/it/liquibase/LiquibaseFunctionalityTest.java b/integration-tests/liquibase/src/test/java/io/quarkus/it/liquibase/LiquibaseFunctionalityTest.java index a5079d99a5cec..52246e1254d69 100644 --- a/integration-tests/liquibase/src/test/java/io/quarkus/it/liquibase/LiquibaseFunctionalityTest.java +++ b/integration-tests/liquibase/src/test/java/io/quarkus/it/liquibase/LiquibaseFunctionalityTest.java @@ -15,12 +15,16 @@ public class LiquibaseFunctionalityTest { @Test @DisplayName("Migrates a schema correctly using integrated instance") public void testLiquibaseQuarkusFunctionality() { + doTestLiquibaseQuarkusFunctionality(isIncludeAllExpectedToWork()); + } + + static void doTestLiquibaseQuarkusFunctionality(boolean isIncludeAllExpectedToWork) { when() .get("/liquibase/update") .then() .body(is( "create-tables-1,test-1,create-view-inline,create-view-file-abs,create-view-file-rel," - + (isIncludeAllExpectedToWork() ? "includeAll-1,includeAll-2," : "") + + (isIncludeAllExpectedToWork ? "includeAll-1,includeAll-2," : "") + "json-create-tables-1,json-test-1," + "sql-create-tables-1,sql-test-1," + "yaml-create-tables-1,yaml-test-1," From 764f8d627020a87147957613c8a394d81a01ba41 Mon Sep 17 00:00:00 2001 From: Jose Date: Fri, 17 Mar 2023 10:11:31 +0100 Subject: [PATCH 25/26] Support of generic collections in RESTEasy Reactive server and client There were some issues when supporting List, Set and SortedSet: - In the server, List, Set and SortedSet types without the element type was not working - In the client, it was unsopported as it was always trying to cast the header to string (and hence failing). Fix https://github.com/quarkusio/quarkus/issues/31866 (cherry picked from commit 41aba276f020d510c9a2a0435ad9ad332ebf0a97) --- .../JaxrsClientReactiveProcessor.java | 6 +-- .../simple/SimpleQuarkusRestResource.java | 44 +++++++++++++++ .../simple/SimpleQuarkusRestTestCase.java | 30 +++++++++++ .../client/reactive/headers/HeaderTest.java | 54 +++++++++++++++++++ .../MicroProfileRestClientRequestFilter.java | 5 +- .../common/processor/EndpointIndexer.java | 17 +++++- 6 files changed, 151 insertions(+), 5 deletions(-) diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index db778f22b64cb..f58d6841b0dca 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -941,7 +941,7 @@ A more full example of generated client (with sub-resource) can is at the bottom MethodDescriptor handleHeaderDescriptor = MethodDescriptor.ofMethod(name, method.getName() + "$$" + methodIndex + "$$handleHeader$$" + paramIdx, Invocation.Builder.class, - Invocation.Builder.class, param.type); + Invocation.Builder.class, param.declaredType); MethodCreator handleHeaderMethod = classContext.classCreator.getMethodCreator( handleHeaderDescriptor).setModifiers(Modifier.PRIVATE); @@ -1358,7 +1358,7 @@ private void handleSubResourceMethod(List subMethod.getName() + "$$" + subMethodIndex + "$$handleHeader$$param" + inheritedParamIndex + "$" + subParamField.paramIndex, Invocation.Builder.class, - Invocation.Builder.class, param.type); + Invocation.Builder.class, param.declaredType); MethodCreator handleHeaderMethod = subContext.classCreator.getMethodCreator( handleHeaderDescriptor).setModifiers(Modifier.PRIVATE); @@ -1464,7 +1464,7 @@ private void handleSubResourceMethod(List MethodDescriptor handleHeaderDescriptor = MethodDescriptor.ofMethod(subName, subMethod.getName() + "$$" + subMethodIndex + "$$handleHeader$$" + paramIdx, Invocation.Builder.class, - Invocation.Builder.class, param.type); + Invocation.Builder.class, param.declaredType); MethodCreator handleHeaderMethod = subContext.classCreator.getMethodCreator( handleHeaderDescriptor).setModifiers(Modifier.PRIVATE); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestResource.java index 895a728bbd143..d186fce0e127a 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestResource.java @@ -2,9 +2,13 @@ import java.math.BigDecimal; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.Set; +import java.util.SortedSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.json.JsonArray; @@ -143,6 +147,42 @@ public Response filters(@Context HttpHeaders headers, @RestHeader("filter-reques return Response.ok().header("filter-request", header).build(); } + @GET + @Path("header-param-list") + public Object headerParamWithList(@HeaderParam("header") List list) { + return collectionToString(list); + } + + @GET + @Path("rest-header-list") + public Object restHeaderWithList(@RestHeader List header) { + return collectionToString(header); + } + + @GET + @Path("header-param-set") + public Object headerParamWithSet(@HeaderParam("header") Set list) { + return collectionToString(list); + } + + @GET + @Path("rest-header-set") + public Object restHeaderWithSet(@RestHeader Set header) { + return collectionToString(header); + } + + @GET + @Path("header-param-sorted-set") + public Object headerParamWithSortedSet(@HeaderParam("header") SortedSet list) { + return collectionToString(list); + } + + @GET + @Path("rest-header-sorted-set") + public String restHeaderWithSortedSet(@RestHeader SortedSet header) { + return collectionToString(header); + } + @GET @Path("feature-filters") public Response featureFilters(@Context HttpHeaders headers) { @@ -380,4 +420,8 @@ public String simplifiedResourceInfo(@Context SimpleResourceInfo simplifiedResou public String bigDecimalConverter(BigDecimal val) { return val.toString(); } + + private String collectionToString(Collection list) { + return (String) list.stream().map(Object::toString).collect(Collectors.joining(", ")); + } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java index ecadb812402d4..0281067a255c7 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/simple/SimpleQuarkusRestTestCase.java @@ -17,6 +17,8 @@ import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; @@ -313,6 +315,34 @@ public void testHeaderParamInCtor() { .then().body(Matchers.is(emptyString())); } + @ParameterizedTest + @ValueSource(strings = { + "rest-header-list", + "rest-header-set", + "rest-header-sorted-set" + }) + public void testRestHeaderUsingCollection(String path) { + RestAssured.with().header("header", "a", "b") + .get("/simple/" + path) + .then() + .statusCode(HttpStatus.SC_OK) + .body(Matchers.equalTo("a, b")); + } + + @ParameterizedTest + @ValueSource(strings = { + "header-param-list", + "header-param-set", + "header-param-sorted-set" + }) + public void testHeaderParamUsingCollection(String path) { + RestAssured.with().header("header", "a", "b") + .get("/simple/" + path) + .then() + .statusCode(HttpStatus.SC_OK) + .body(Matchers.equalTo("a, b")); + } + @Test public void testFormMap() { RestAssured diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/HeaderTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/HeaderTest.java index 22d902c8e8406..24c585cf67b8c 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/HeaderTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/HeaderTest.java @@ -3,6 +3,13 @@ import static org.assertj.core.api.Assertions.assertThat; import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; import javax.ws.rs.GET; @@ -40,6 +47,15 @@ void testNullHeaders() { assertThat(client.cookieSub("bar", null).send(null, "bar4", "dummy")).isEqualTo("bar:null:null:bar4:X-My-Header/dummy"); } + @Test + void testHeadersWithCollections() { + String expected = "a, b, c, d"; + Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class); + assertThat(client.headersList(List.of("a", "b"), List.of("c", "d"))).isEqualTo(expected); + assertThat(client.headersSet(Set.of("a", "b"), Set.of("c", "d"))).isEqualTo(expected); + assertThat(client.headersSet(new TreeSet(List.of("a", "b")), new TreeSet(List.of("c", "d")))).isEqualTo(expected); + } + @Path("/") @ApplicationScoped public static class Resource { @@ -51,12 +67,50 @@ public String returnHeaders(@HeaderParam("foo") String header, @HeaderParam("foo return header + ":" + header2 + ":" + header3 + ":" + header4 + ":" + myHeaderName + "/" + headers.getHeaderString("X-My-Header"); } + + @GET + @Path("/headers-list") + public String headersList(@HeaderParam("foo") List foo, @RestHeader List header) { + return joiningCollections(foo, header); + } + + @GET + @Path("/headers-set") + public String headersSet(@HeaderParam("foo") Set foo, @RestHeader Set header) { + return joiningCollections(foo, header); + } + + @GET + @Path("/headers-sorted-set") + public String headersSortedSet(@HeaderParam("foo") SortedSet foo, @RestHeader SortedSet header) { + return joiningCollections(foo, header); + } + + private String joiningCollections(Collection... collections) { + List allHeaders = new ArrayList<>(); + for (Collection collection : collections) { + collection.forEach(v -> allHeaders.add((String) v)); + } + return allHeaders.stream().collect(Collectors.joining(", ")); + } } public interface Client { @Path("/") SubClient cookieSub(@HeaderParam("foo") String cookie, @HeaderParam("foo2") String cookie2); + + @GET + @Path("/headers-list") + String headersList(@HeaderParam("foo") List foo, @RestHeader List header); + + @GET + @Path("/headers-set") + String headersSet(@HeaderParam("foo") Set foo, @RestHeader Set header); + + @GET + @Path("/headers-sorted-set") + String headersSortedSet(@HeaderParam("foo") SortedSet foo, @RestHeader SortedSet header); } public interface SubClient { diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/MicroProfileRestClientRequestFilter.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/MicroProfileRestClientRequestFilter.java index 89982a4c8caaa..7506bccc7cfc7 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/MicroProfileRestClientRequestFilter.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/MicroProfileRestClientRequestFilter.java @@ -1,6 +1,7 @@ package io.quarkus.rest.client.reactive.runtime; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -81,11 +82,13 @@ public void filter(ResteasyReactiveClientRequestContext requestContext) { } } - private static List castToListOfStrings(List values) { + private static List castToListOfStrings(Collection values) { List result = new ArrayList<>(); for (Object value : values) { if (value instanceof String) { result.add((String) value); + } else if (value instanceof Collection) { + result.addAll(castToListOfStrings((Collection) value)); } else { result.add(String.valueOf(value)); } 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 b279df0c8784b..cb56e0d49c651 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 @@ -1362,13 +1362,28 @@ && isParameterContainerType(paramType.asClassType())) { elementType = paramType.name().toString(); handleTemporalParam(builder, paramType.name(), anns, currentMethodInfo); typeHandled = true; - } else if (paramType.name().equals(LIST) && (type == ParameterType.QUERY)) { // RESTEasy Classic handles the non-generic List type + } else if (paramType.name().equals(LIST) && (type == ParameterType.QUERY + || type == ParameterType.HEADER)) { // RESTEasy Classic handles the non-generic List type elementType = String.class.getName(); typeHandled = true; builder.setSingle(false); if (convertible) { handleListParam(existingConverters, errorLocation, hasRuntimeConverters, builder, elementType); } + } else if (paramType.name().equals(SET) && type == ParameterType.HEADER) { // RESTEasy Classic handles the non-generic Set type + elementType = String.class.getName(); + typeHandled = true; + builder.setSingle(false); + if (convertible) { + handleSetParam(existingConverters, errorLocation, hasRuntimeConverters, builder, elementType); + } + } else if (paramType.name().equals(SORTED_SET) && type == ParameterType.HEADER) { // RESTEasy Classic handles the non-generic SortedSet type + elementType = String.class.getName(); + typeHandled = true; + builder.setSingle(false); + if (convertible) { + handleSortedSetParam(existingConverters, errorLocation, hasRuntimeConverters, builder, elementType); + } } else if (paramType.kind() == Kind.ARRAY) { ArrayType at = paramType.asArrayType(); typeHandled = true; From a22f5db906bc309fc57e00f5b1fd00d6fadc458d Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sat, 18 Mar 2023 17:08:16 +0100 Subject: [PATCH 26/26] New home for Narayana LRA coordinator Docker images (cherry picked from commit 5c15e71d971e03316acf438900169ba735151ff9) --- docs/src/main/asciidoc/lra.adoc | 2 +- .../quickstart/lra/LRAParticipantTestResourceLifecycle.java | 2 +- .../src/main/java/io/quarkus/tck/lra/LRACoordinatorManager.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/lra.adoc b/docs/src/main/asciidoc/lra.adoc index fb584080fe4e5..0f3fcf9402d0a 100644 --- a/docs/src/main/asciidoc/lra.adoc +++ b/docs/src/main/asciidoc/lra.adoc @@ -96,7 +96,7 @@ quarkus.lra.coordinator-url=http://localhost:8080/lra-coordinator ---- For a Narayana coordinator the path component of the url is normally `lra-coordinator`. -Coordinators can be obtained from `https://hub.docker.com/r/jbosstm/lra-coordinator` +Coordinators can be obtained from https://quay.io/repository/jbosstm/lra-coordinator or you can build your own coordinator using a maven pom that includes the appropriate dependencies. A Quarkus quickstart will be provided to show how to do this, or you can take a look at one of the https://github.com/jbosstm/quickstart/tree/master/rts/lra-examples/lra-coordinator[Narayana quickstarts]. diff --git a/integration-tests/narayana-lra/src/test/java/org/acme/quickstart/lra/LRAParticipantTestResourceLifecycle.java b/integration-tests/narayana-lra/src/test/java/org/acme/quickstart/lra/LRAParticipantTestResourceLifecycle.java index afa58143853d8..c0fc6107985c7 100644 --- a/integration-tests/narayana-lra/src/test/java/org/acme/quickstart/lra/LRAParticipantTestResourceLifecycle.java +++ b/integration-tests/narayana-lra/src/test/java/org/acme/quickstart/lra/LRAParticipantTestResourceLifecycle.java @@ -22,7 +22,7 @@ public static String getCoordinatorEndpoint() { @Override public Map start() { - registry = new GenericContainer<>("jbosstm/lra-coordinator:5.12.0.Final") + registry = new GenericContainer<>("quay.io/jbosstm/lra-coordinator:latest") .withExposedPorts(8080) .withEnv("QUARKUS_PROFILE", "prod"); registry.start(); diff --git a/tcks/microprofile-lra/src/main/java/io/quarkus/tck/lra/LRACoordinatorManager.java b/tcks/microprofile-lra/src/main/java/io/quarkus/tck/lra/LRACoordinatorManager.java index 8721d08e41755..03d62f97fdd9b 100644 --- a/tcks/microprofile-lra/src/main/java/io/quarkus/tck/lra/LRACoordinatorManager.java +++ b/tcks/microprofile-lra/src/main/java/io/quarkus/tck/lra/LRACoordinatorManager.java @@ -19,7 +19,7 @@ public class LRACoordinatorManager { public void beforeClass( @Observes(precedence = DEFAULT_PRECEDENCE) org.jboss.arquillian.test.spi.event.suite.BeforeSuite event) { LOGGER.debug("Starting LRA coordinator on port " + coordinatorPort); - coordinatorContainer = new GenericContainer<>(DockerImageName.parse("jbosstm/lra-coordinator:latest")) + coordinatorContainer = new GenericContainer<>(DockerImageName.parse("quay.io/jbosstm/lra-coordinator:latest")) // lra-coordinator is a Quarkus service .withEnv("QUARKUS_HTTP_PORT", String.valueOf(coordinatorPort)) // need to run with host network because coordinator calls the TCK services from the container