From b390d8e08297eb47a4438ec4e70160828cf48cac Mon Sep 17 00:00:00 2001 From: Ken Finnigan Date: Wed, 6 Mar 2019 11:12:41 +0100 Subject: [PATCH 1/2] Register return types on REST Interface methods for reflection Fixes #1254 --- .../SmallRyeRestClientProcessor.java | 20 ++++++++++ .../quarkus/example/rest/ClientResource.java | 38 +++++++++++++++++++ .../quarkus/example/rest/RestInterface.java | 13 +++++++ .../example/test/RestClientTestCase.java | 38 +++++++++++++++++++ 4 files changed, 109 insertions(+) diff --git a/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java b/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java index d3f97478254d2..6ef5f4ae624b1 100644 --- a/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java +++ b/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java @@ -18,7 +18,9 @@ import java.lang.reflect.Modifier; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import javax.ws.rs.client.ClientRequestFilter; import javax.ws.rs.client.ClientResponseFilter; @@ -32,6 +34,8 @@ import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; +import org.jboss.jandex.MethodInfo; +import org.jboss.jandex.Type; import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; import org.jboss.resteasy.client.jaxrs.internal.proxy.ProxyBuilderImpl; import org.jboss.resteasy.client.jaxrs.internal.proxy.ResteasyClientProxy; @@ -132,6 +136,7 @@ void processInterfaces(CombinedIndexBuildItem combinedIndexBuildItem, // According to the spec only rest client interfaces annotated with RegisterRestClient are registered as beans Map interfaces = new HashMap<>(); + Set returnTypes = new HashSet<>(); for (AnnotationInstance annotation : combinedIndexBuildItem.getIndex().getAnnotations(REGISTER_REST_CLIENT)) { AnnotationTarget target = annotation.target(); @@ -147,6 +152,16 @@ void processInterfaces(CombinedIndexBuildItem combinedIndexBuildItem, continue; } interfaces.put(theInfo.name(), theInfo); + + // Find Return types + for (MethodInfo method : theInfo.methods()) { + Type type = method.returnType(); + if (!type.name().toString().contains("java.lang")) { + if (!returnTypes.contains(type)) { + returnTypes.add(type); + } + } + } } if (interfaces.isEmpty()) { @@ -160,6 +175,11 @@ void processInterfaces(CombinedIndexBuildItem combinedIndexBuildItem, reflectiveClass.produce(new ReflectiveClassBuildItem(true, false, iName)); } + // Register Interface return types for reflection + for (Type returnType : returnTypes) { + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, returnType.toString())); + } + beanRegistrars.produce(new BeanRegistrarBuildItem(new BeanRegistrar() { @Override diff --git a/integration-tests/main/src/main/java/io/quarkus/example/rest/ClientResource.java b/integration-tests/main/src/main/java/io/quarkus/example/rest/ClientResource.java index 9860261e6a5fc..a45baa50a4311 100644 --- a/integration-tests/main/src/main/java/io/quarkus/example/rest/ClientResource.java +++ b/integration-tests/main/src/main/java/io/quarkus/example/rest/ClientResource.java @@ -17,10 +17,12 @@ package io.quarkus.example.rest; import java.net.URL; +import java.util.List; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.Produces; import org.eclipse.microprofile.rest.client.RestClientBuilder; import org.eclipse.microprofile.rest.client.inject.RestClient; @@ -47,4 +49,40 @@ public String cdi() throws Exception { return restInterface.get(); } + @GET + @Path("manual/jackson") + @Produces("application/json") + public TestResource.MyData getDataManual() throws Exception { + RestInterface iface = RestClientBuilder.newBuilder() + .baseUrl(new URL(System.getProperty("test.url"))) + .build(RestInterface.class); + System.out.println(iface.getData()); + return iface.getData(); + } + + @GET + @Path("cdi/jackson") + @Produces("application/json") + public TestResource.MyData getDataCdi() { + return restInterface.getData(); + } + + @GET + @Path("/manual/complex") + @Produces("application/json") + public List complexManual() throws Exception { + RestInterface iface = RestClientBuilder.newBuilder() + .baseUrl(new URL(System.getProperty("test.url"))) + .build(RestInterface.class); + System.out.println(iface.complex()); + return iface.complex(); + } + + @GET + @Path("/cdi/complex") + @Produces("application/json") + public List complexCdi() { + return restInterface.complex(); + } + } diff --git a/integration-tests/main/src/main/java/io/quarkus/example/rest/RestInterface.java b/integration-tests/main/src/main/java/io/quarkus/example/rest/RestInterface.java index 5e9c3edfd3a49..25a3fae1d8928 100644 --- a/integration-tests/main/src/main/java/io/quarkus/example/rest/RestInterface.java +++ b/integration-tests/main/src/main/java/io/quarkus/example/rest/RestInterface.java @@ -16,8 +16,11 @@ package io.quarkus.example.rest; +import java.util.List; + import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.Produces; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; @@ -27,4 +30,14 @@ public interface RestInterface { @GET String get(); + + @GET + @Path("/jackson") + @Produces("application/json") + TestResource.MyData getData(); + + @GET + @Path("/complex") + @Produces("application/json") + List complex(); } diff --git a/integration-tests/main/src/test/java/io/quarkus/example/test/RestClientTestCase.java b/integration-tests/main/src/test/java/io/quarkus/example/test/RestClientTestCase.java index 457cacdfd1716..430938d1f8e14 100644 --- a/integration-tests/main/src/test/java/io/quarkus/example/test/RestClientTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/example/test/RestClientTestCase.java @@ -19,11 +19,17 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import io.quarkus.example.rest.ComponentType; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; +import io.restassured.path.json.JsonPath; @QuarkusTest public class RestClientTestCase { @@ -40,6 +46,38 @@ public void testMicroprofileClientCDIIntegration() { .body(is("TEST")); } + @Test + void testMicroprofileClientData() { + JsonPath jsonPath = RestAssured.when().get("/client/manual/jackson").thenReturn().jsonPath(); + Assertions.assertEquals(jsonPath.getString("name"), "Stuart"); + Assertions.assertEquals(jsonPath.getString("value"), "A Value"); + } + + @Test + void testMicroprofileClientDataCdi() { + JsonPath jsonPath = RestAssured.when().get("/client/cdi/jackson").thenReturn().jsonPath(); + Assertions.assertEquals(jsonPath.getString("name"), "Stuart"); + Assertions.assertEquals(jsonPath.getString("value"), "A Value"); + } + + @Test + void testMicroprofileClientComplex() { + JsonPath jsonPath = RestAssured.when().get("/client/manual/complex").thenReturn().jsonPath(); + List> components = jsonPath.getList("$"); + Assertions.assertEquals(components.size(), 1); + Map map = components.get(0); + Assertions.assertEquals(map.get("value"), "component value"); + } + + @Test + void testMicroprofileClientComplexCdi() { + JsonPath jsonPath = RestAssured.when().get("/client/cdi/complex").thenReturn().jsonPath(); + List> components = jsonPath.getList("$"); + Assertions.assertEquals(components.size(), 1); + Map map = components.get(0); + Assertions.assertEquals(map.get("value"), "component value"); + } + /** * Disabled by default as it establishes external connections. *

From 9335b905edcd946f088986fc143f0619c55e5906 Mon Sep 17 00:00:00 2001 From: Ken Finnigan Date: Wed, 6 Mar 2019 11:21:45 +0100 Subject: [PATCH 2/2] Need to reflect the hierarchy of the return type --- .../restclient/deployment/SmallRyeRestClientProcessor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java b/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java index 6ef5f4ae624b1..0ce7c5be3657d 100644 --- a/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java +++ b/extensions/smallrye-rest-client/deployment/src/main/java/io/quarkus/smallrye/restclient/deployment/SmallRyeRestClientProcessor.java @@ -56,6 +56,7 @@ import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.SslNativeConfigBuildItem; import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.substrate.ReflectiveHierarchyBuildItem; import io.quarkus.deployment.builditem.substrate.SubstrateProxyDefinitionBuildItem; import io.quarkus.deployment.builditem.substrate.SubstrateResourceBuildItem; import io.quarkus.deployment.util.ServiceUtil; @@ -131,6 +132,7 @@ void processInterfaces(CombinedIndexBuildItem combinedIndexBuildItem, SslNativeConfigBuildItem sslNativeConfig, BuildProducer proxyDefinition, BuildProducer reflectiveClass, + BuildProducer reflectiveHierarchy, BuildProducer beanRegistrars, BuildProducer extensionSslNativeSupport) { @@ -177,7 +179,7 @@ void processInterfaces(CombinedIndexBuildItem combinedIndexBuildItem, // Register Interface return types for reflection for (Type returnType : returnTypes) { - reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, returnType.toString())); + reflectiveHierarchy.produce(new ReflectiveHierarchyBuildItem(returnType)); } beanRegistrars.produce(new BeanRegistrarBuildItem(new BeanRegistrar() {