CXF-8179: JSON Jackson does not handle resource returning CompletionStage<X>
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
index 30ff11d..209d7c1 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/ClientProxyImpl.java
@@ -1011,10 +1011,8 @@
}
Class<?> returnType = getReturnType(method, outMessage);
- Type genericType =
- InjectionUtils.processGenericTypeIfNeeded(serviceCls,
- returnType,
- method.getGenericReturnType());
+ Type genericType = getGenericReturnType(serviceCls, method, returnType);
+
returnType = InjectionUtils.updateParamClassToTypeIfNeeded(returnType, genericType);
return readBody(r,
outMessage,
@@ -1026,6 +1024,10 @@
}
}
+ protected Type getGenericReturnType(Class<?> serviceCls, Method method, Class<?> returnType) {
+ return InjectionUtils.processGenericTypeIfNeeded(serviceCls, returnType, method.getGenericReturnType());
+ }
+
protected Class<?> getReturnType(Method method, Message outMessage) {
return method.getReturnType();
}
diff --git a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MicroProfileClientProxyImpl.java b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MicroProfileClientProxyImpl.java
index 98150db..e27e1ef 100644
--- a/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MicroProfileClientProxyImpl.java
+++ b/rt/rs/microprofile-client/src/main/java/org/apache/cxf/microprofile/client/proxy/MicroProfileClientProxyImpl.java
@@ -20,6 +20,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Arrays;
@@ -204,6 +205,26 @@
}
return returnType;
}
+
+ @Override
+ protected Type getGenericReturnType(Class<?> serviceCls, Method method, Class<?> returnType) {
+ final Type genericReturnType = super.getGenericReturnType(serviceCls, method, returnType);
+
+ if (genericReturnType instanceof ParameterizedType) {
+ final ParameterizedType pt = (ParameterizedType)genericReturnType;
+ if (CompletionStage.class.isAssignableFrom(InjectionUtils.getRawType(pt))) {
+ final Type[] actualTypeArguments = pt.getActualTypeArguments();
+ if (actualTypeArguments.length > 0 && actualTypeArguments[0] instanceof ParameterizedType) {
+ return InjectionUtils.processGenericTypeIfNeeded(serviceCls, returnType,
+ (ParameterizedType)actualTypeArguments[0]);
+ } else {
+ return returnType;
+ }
+ }
+ }
+
+ return genericReturnType;
+ }
@Override
protected Message createMessage(Object body,
diff --git a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/AsyncMethodTest.java b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/AsyncMethodTest.java
index 3ddb396..d7efb10 100644
--- a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/AsyncMethodTest.java
+++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/AsyncMethodTest.java
@@ -19,17 +19,22 @@
package org.apache.cxf.systest.microprofile.rest.client;
import java.net.URI;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
import javax.ws.rs.core.Response;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import org.apache.cxf.jaxrs.provider.jsrjsonp.JsrJsonpProvider;
import org.apache.cxf.systest.microprofile.rest.client.mock.AsyncClientWithCompletionStage;
import org.apache.cxf.systest.microprofile.rest.client.mock.AsyncInvocationInterceptorFactoryTestImpl;
import org.apache.cxf.systest.microprofile.rest.client.mock.AsyncInvocationInterceptorFactoryTestImpl2;
@@ -48,7 +53,10 @@
//CHECKSTYLE:OFF
import static com.github.tomakehurst.wiremock.client.WireMock.*;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
//CHECKSTYLE:ON
public class AsyncMethodTest {
@@ -142,6 +150,72 @@
}
}
+ @Test
+ public void testInvokesGetOperationWithRegisteredProvidersAsyncCompletionStage() throws Exception {
+ wireMockRule.stubFor(get(urlEqualTo("/echo/test2"))
+ .willReturn(aResponse()
+ .withBody("{\"name\": \"test\"}")));
+
+ AsyncClientWithCompletionStage api = RestClientBuilder.newBuilder()
+ .register(TestClientRequestFilter.class)
+ .register(TestClientResponseFilter.class)
+ .register(TestMessageBodyReader.class, 3)
+ .register(TestMessageBodyWriter.class)
+ .register(TestParamConverterProvider.class)
+ .register(TestReaderInterceptor.class)
+ .register(TestWriterInterceptor.class)
+ .register(JsrJsonpProvider.class)
+ .baseUri(getBaseUri())
+ .build(AsyncClientWithCompletionStage.class);
+
+ CompletionStage<JsonStructure> cs = api.get();
+
+ // should need <1 second, but 20s timeout in case something goes wrong
+ JsonStructure response = cs.toCompletableFuture().get(20, TimeUnit.SECONDS);
+ assertThat(response, instanceOf(JsonObject.class));
+
+ final JsonObject jsonObject = (JsonObject)response;
+ assertThat(jsonObject.get("name"), instanceOf(JsonString.class));
+ assertThat(((JsonString)jsonObject.get("name")).getString(), equalTo("test"));
+
+ assertEquals(TestClientResponseFilter.getAndResetValue(), 1);
+ assertEquals(TestClientRequestFilter.getAndResetValue(), 1);
+ assertEquals(TestReaderInterceptor.getAndResetValue(), 1);
+ }
+
+ @Test
+ public void testInvokesGetAllOperationWithRegisteredProvidersAsyncCompletionStage() throws Exception {
+ wireMockRule.stubFor(get(urlEqualTo("/echo/test3"))
+ .willReturn(aResponse()
+ .withBody("[{\"name\": \"test\"}]")));
+
+ AsyncClientWithCompletionStage api = RestClientBuilder.newBuilder()
+ .register(TestClientRequestFilter.class)
+ .register(TestClientResponseFilter.class)
+ .register(TestMessageBodyReader.class, 3)
+ .register(TestMessageBodyWriter.class)
+ .register(TestParamConverterProvider.class)
+ .register(TestReaderInterceptor.class)
+ .register(TestWriterInterceptor.class)
+ .register(JsrJsonpProvider.class)
+ .baseUri(getBaseUri())
+ .build(AsyncClientWithCompletionStage.class);
+
+ CompletionStage<Collection<JsonObject>> cs = api.getAll();
+
+ // should need <1 second, but 20s timeout in case something goes wrong
+ Collection<JsonObject> response = cs.toCompletableFuture().get(20, TimeUnit.SECONDS);
+ assertEquals(1, response.size());
+
+ final JsonObject jsonObject = response.iterator().next();
+ assertThat(jsonObject.get("name"), instanceOf(JsonString.class));
+ assertThat(((JsonString)jsonObject.get("name")).getString(), equalTo("test"));
+
+ assertEquals(TestClientResponseFilter.getAndResetValue(), 1);
+ assertEquals(TestClientRequestFilter.getAndResetValue(), 1);
+ assertEquals(TestReaderInterceptor.getAndResetValue(), 1);
+ }
+
private URI getBaseUri() {
return URI.create("http://localhost:" + wireMockRule.port() + "/echo");
}
diff --git a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncClientWithCompletionStage.java b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncClientWithCompletionStage.java
index d30a2d0..d5b57ed 100644
--- a/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncClientWithCompletionStage.java
+++ b/systests/microprofile/client/async/src/test/java/org/apache/cxf/systest/microprofile/rest/client/mock/AsyncClientWithCompletionStage.java
@@ -19,10 +19,15 @@
package org.apache.cxf.systest.microprofile.rest.client.mock;
+import java.util.Collection;
import java.util.concurrent.CompletionStage;
+import javax.json.JsonObject;
+import javax.json.JsonStructure;
+import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
public interface AsyncClientWithCompletionStage {
@@ -30,4 +35,14 @@
@PUT
@Path("/test")
CompletionStage<Response> put(String text);
+
+ @GET
+ @Path("/test2")
+ @Produces("application/json")
+ CompletionStage<JsonStructure> get();
+
+ @GET
+ @Path("/test3")
+ @Produces("application/json")
+ CompletionStage<Collection<JsonObject>> getAll();
}