From cad1e374cf25af743a71608c1166cb1272b939f0 Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 8 Jun 2022 12:02:37 +0200 Subject: [PATCH] Resteasy Reactive: Fix native mode when using Files When having the following resource: ```java @POST @Consumes(MediaType.APPLICATION_OCTET_STREAM) public String update(File file) { // .. } ``` This works fine when running on JVM, but not in Native where fails with a NoSuchMethodException exception. To fix this issue, I've registered all the resource classes (the ones annotated with `@ApplicationPath` and `@Path`) for reflection usage. In addition to this change: - I removed some unused methods and fields from the resteasy reactive processor - I added some coverage of the getting started resteasy reactive client (I also reproduced this issue in this test). Fix https://github.com/quarkusio/quarkus/issues/25973 --- .../QuarkusServerEndpointIndexer.java | 30 ----- .../deployment/ResteasyReactiveProcessor.java | 20 +-- .../rest-client-reactive/pom.xml | 26 ++++ .../invoker.jvm.properties | 1 + .../invoker.native.properties | 1 + .../src/it/getting-started-from-guide/pom.xml | 115 ++++++++++++++++++ .../java/org/acme/rest/client/Extension.java | 10 ++ .../acme/rest/client/ExtensionsResource.java | 32 +++++ .../acme/rest/client/ExtensionsService.java | 24 ++++ .../client/ExtensionsServiceResource.java | 35 ++++++ .../src/main/resources/application.properties | 1 + .../rest/client/ExtensionsResourceIT.java | 35 ++++++ 12 files changed, 290 insertions(+), 40 deletions(-) create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/invoker.jvm.properties create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/invoker.native.properties create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/pom.xml create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/Extension.java create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsResource.java create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsService.java create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsServiceResource.java create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/resources/application.properties create mode 100644 integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/test/java/org/acme/rest/client/ExtensionsResourceIT.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java index 16b6355c47190..2ad8daa6fb5eb 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/QuarkusServerEndpointIndexer.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Map; -import java.util.function.Predicate; import javax.ws.rs.core.MediaType; @@ -23,9 +22,7 @@ import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.annotations.BuildProducer; -import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; -import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.resteasy.reactive.common.deployment.JsonDefaultProducersHandler; import io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder; @@ -36,22 +33,15 @@ public class QuarkusServerEndpointIndexer private final Capabilities capabilities; private final BuildProducer generatedClassBuildItemBuildProducer; - private final BuildProducer bytecodeTransformerBuildProducer; - private final BuildProducer reflectiveClassProducer; private final DefaultProducesHandler defaultProducesHandler; private final JsonDefaultProducersHandler jsonDefaultProducersHandler; private final ResteasyReactiveRecorder resteasyReactiveRecorder; - private final Predicate applicationClassPredicate; - QuarkusServerEndpointIndexer(Builder builder) { super(builder); this.capabilities = builder.capabilities; this.generatedClassBuildItemBuildProducer = builder.generatedClassBuildItemBuildProducer; - this.bytecodeTransformerBuildProducer = builder.bytecodeTransformerBuildProducer; - this.reflectiveClassProducer = builder.reflectiveClassProducer; this.defaultProducesHandler = builder.defaultProducesHandler; - this.applicationClassPredicate = builder.applicationClassPredicate; this.resteasyReactiveRecorder = builder.resteasyReactiveRecorder; this.jsonDefaultProducersHandler = new JsonDefaultProducersHandler(); } @@ -109,11 +99,8 @@ public static final class Builder extends AbstractBuilder { private final Capabilities capabilities; private BuildProducer generatedClassBuildItemBuildProducer; - private BuildProducer bytecodeTransformerBuildProducer; - private BuildProducer reflectiveClassProducer; private ResteasyReactiveRecorder resteasyReactiveRecorder; private DefaultProducesHandler defaultProducesHandler = DefaultProducesHandler.Noop.INSTANCE; - public Predicate applicationClassPredicate; public Builder(Capabilities capabilities) { this.capabilities = capabilities; @@ -124,29 +111,12 @@ public QuarkusServerEndpointIndexer build() { return new QuarkusServerEndpointIndexer(this); } - public Builder setBytecodeTransformerBuildProducer( - BuildProducer bytecodeTransformerBuildProducer) { - this.bytecodeTransformerBuildProducer = bytecodeTransformerBuildProducer; - return this; - } - public Builder setGeneratedClassBuildItemBuildProducer( BuildProducer generatedClassBuildItemBuildProducer) { this.generatedClassBuildItemBuildProducer = generatedClassBuildItemBuildProducer; return this; } - public Builder setReflectiveClassProducer( - BuildProducer reflectiveClassProducer) { - this.reflectiveClassProducer = reflectiveClassProducer; - return this; - } - - public Builder setApplicationClassPredicate(Predicate applicationClassPredicate) { - this.applicationClassPredicate = applicationClassPredicate; - return this; - } - public Builder setResteasyReactiveRecorder(ResteasyReactiveRecorder resteasyReactiveRecorder) { this.resteasyReactiveRecorder = resteasyReactiveRecorder; return this; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index be9da31998cac..dac431c402d63 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -347,7 +347,6 @@ public void setupEndpoints(ApplicationIndexBuildItem applicationIndexBuildItem, Optional resourceScanningResultBuildItem, BuildProducer generatedClassBuildItemBuildProducer, BuildProducer bytecodeTransformerBuildItemBuildProducer, - BuildProducer reflectiveClassBuildItemBuildProducer, ResteasyReactiveRecorder recorder, List serverDefaultProducesHandlers, Optional classLevelExceptionMappers, @@ -424,8 +423,6 @@ public void setupEndpoints(ApplicationIndexBuildItem applicationIndexBuildItem, .setEndpointInvokerFactory( new QuarkusInvokerFactory(generatedClassBuildItemBuildProducer, recorder)) .setGeneratedClassBuildItemBuildProducer(generatedClassBuildItemBuildProducer) - .setBytecodeTransformerBuildProducer(bytecodeTransformerBuildItemBuildProducer) - .setReflectiveClassProducer(reflectiveClassBuildItemBuildProducer) .setExistingConverters(existingConverters) .setScannedResourcePaths(scannedResourcePaths) .setConfig(createRestReactiveConfig(config)) @@ -517,7 +514,6 @@ private boolean hasAnnotation(MethodInfo method, short paramPosition, DotName an } }) .setResteasyReactiveRecorder(recorder) - .setApplicationClassPredicate(applicationClassPredicate) .setTargetJavaVersion(new TargetJavaVersion() { private final Status result; @@ -558,9 +554,9 @@ public Status isJava19OrHigher() { } serverEndpointIndexerBuilder.setMultipartReturnTypeIndexerExtension(new QuarkusMultipartReturnTypeHandler( - generatedClassBuildItemBuildProducer, applicationClassPredicate, reflectiveClassBuildItemBuildProducer)); + generatedClassBuildItemBuildProducer, applicationClassPredicate, reflectiveClass)); serverEndpointIndexerBuilder.setMultipartParameterIndexerExtension(new QuarkusMultipartParamHandler( - generatedClassBuildItemBuildProducer, applicationClassPredicate, reflectiveClassBuildItemBuildProducer, + generatedClassBuildItemBuildProducer, applicationClassPredicate, reflectiveClass, bytecodeTransformerBuildItemBuildProducer)); serverEndpointIndexer = serverEndpointIndexerBuilder.build(); @@ -568,13 +564,17 @@ public Status isJava19OrHigher() { for (ClassInfo i : scannedResources.values()) { Optional endpoints = serverEndpointIndexer.createEndpoints(i, true); if (endpoints.isPresent()) { + ResourceClass resourceClass = endpoints.get(); if (singletonClasses.contains(i.name().toString())) { - endpoints.get().setFactory(new SingletonBeanFactory<>(i.name().toString())); + resourceClass.setFactory(new SingletonBeanFactory<>(i.name().toString())); } - resourceClasses.add(endpoints.get()); - for (ResourceMethod rm : endpoints.get().getMethods()) { - addResourceMethodByPath(allMethods, endpoints.get().getPath(), i, rm); + resourceClasses.add(resourceClass); + for (ResourceMethod rm : resourceClass.getMethods()) { + addResourceMethodByPath(allMethods, resourceClass.getPath(), i, rm); } + + // Ensure native support of resource endpoints: + reflectiveClass.produce(new ReflectiveClassBuildItem(false, true, false, resourceClass.getClassName())); } } diff --git a/integration-tests/rest-client-reactive/pom.xml b/integration-tests/rest-client-reactive/pom.xml index ddcc61b1e1193..6e7939a35b331 100644 --- a/integration-tests/rest-client-reactive/pom.xml +++ b/integration-tests/rest-client-reactive/pom.xml @@ -11,6 +11,10 @@ quarkus-integration-test-rest-client-reactive Quarkus - Integration Tests - REST Client Reactive + + invoker.jvm.properties + + @@ -160,6 +164,27 @@ + + maven-invoker-plugin + + + integration-tests + + run + verify + + + + + ${project.build.directory}/it + true + setup + verify + true + true + ${integration.tests.invoker.properties} + + @@ -173,6 +198,7 @@ native + invoker.native.properties diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/invoker.jvm.properties b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/invoker.jvm.properties new file mode 100644 index 0000000000000..1e1d6c337cb30 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/invoker.jvm.properties @@ -0,0 +1 @@ +invoker.goals=clean verify diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/invoker.native.properties b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/invoker.native.properties new file mode 100644 index 0000000000000..5bd1b9387c9c1 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/invoker.native.properties @@ -0,0 +1 @@ +invoker.goals=clean verify -Dnative diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/pom.xml b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/pom.xml new file mode 100644 index 0000000000000..30447b60e57d6 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + org.acme + getting-started-from-guide + 0.1-SNAPSHOT + + UTF-8 + 3.0.0-M5 + 11 + UTF-8 + 11 + + + + + io.quarkus + quarkus-bom + @project.version@ + pom + import + + + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-rest-client-reactive-jackson + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + io.quarkus + quarkus-maven-plugin + @project.version@ + + + + build + + + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native + + + native + + + + native + + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + + diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/Extension.java b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/Extension.java new file mode 100644 index 0000000000000..67bf4ceee7936 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/Extension.java @@ -0,0 +1,10 @@ +package org.acme.rest.client; + +import java.util.List; + +public class Extension { + public String id; + public String name; + public String shortName; + public List keywords; +} \ No newline at end of file diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsResource.java b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsResource.java new file mode 100644 index 0000000000000..210d4140b046f --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsResource.java @@ -0,0 +1,32 @@ +package org.acme.rest.client; + +import io.smallrye.common.annotation.Blocking; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.POST; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Set; + +@Path("/extension") +public class ExtensionsResource { + + @RestClient + ExtensionsService extensionsService; + + @GET + @Path("/id/{id}") + @Blocking + public Set id(String id) { + return extensionsService.getById(id); + } + + @POST + @Path("/init") + public void initialize() throws IOException { + extensionsService.registerFile(Files.createTempFile("any", ".txt").toFile()); + } +} \ No newline at end of file diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsService.java b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsService.java new file mode 100644 index 0000000000000..f9439e4f6d76d --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsService.java @@ -0,0 +1,24 @@ +package org.acme.rest.client; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.POST; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import java.io.File; +import java.util.Set; + +@Path("/extensions") +@RegisterRestClient +public interface ExtensionsService { + + @GET + Set getById(@QueryParam("id") String id); + + @POST + @Consumes(MediaType.APPLICATION_OCTET_STREAM) + String registerFile(File file); +} \ No newline at end of file diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsServiceResource.java b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsServiceResource.java new file mode 100644 index 0000000000000..cbd831f38d478 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/java/org/acme/rest/client/ExtensionsServiceResource.java @@ -0,0 +1,35 @@ +package org.acme.rest.client; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.POST; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +@Path("/extensions") +public class ExtensionsServiceResource { + + @GET + public Set getById(@QueryParam("id") String id) { + if ("io.quarkus:quarkus-rest-client-reactive".equals(id)) { + Extension extension = new Extension(); + extension.id = id; + extension.name = "REST Client Reactive"; + extension.keywords = List.of("rest-client", "reactive"); + return Set.of(extension); + } + + return Collections.emptySet(); + } + + @POST + @Consumes(MediaType.APPLICATION_OCTET_STREAM) + public String registerFile(File file) { + return "done"; + } +} \ No newline at end of file diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/resources/application.properties b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/resources/application.properties new file mode 100644 index 0000000000000..247efd6315538 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/main/resources/application.properties @@ -0,0 +1 @@ +quarkus.rest-client."org.acme.rest.client.ExtensionsService".url=http://localhost:8081 \ No newline at end of file diff --git a/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/test/java/org/acme/rest/client/ExtensionsResourceIT.java b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/test/java/org/acme/rest/client/ExtensionsResourceIT.java new file mode 100644 index 0000000000000..9580a689fccd7 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/it/getting-started-from-guide/src/test/java/org/acme/rest/client/ExtensionsResourceIT.java @@ -0,0 +1,35 @@ +package org.acme.rest.client; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.greaterThan; + +@QuarkusIntegrationTest +public class ExtensionsResourceIT { + + @Test + public void testExtensionsIdEndpoint() { + given() + .when().get("/extension/id/io.quarkus:quarkus-rest-client-reactive") + .then() + .statusCode(200) + .body("$.size()", is(1), + "[0].id", is("io.quarkus:quarkus-rest-client-reactive"), + "[0].name", is("REST Client Reactive"), + "[0].keywords.size()", greaterThan(1), + "[0].keywords", hasItem("rest-client")); + } + + @Test + public void testPostEndpointWithFile() { + given() + .when().post("/extension/init") + .then() + .statusCode(204); + } +} \ No newline at end of file