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();
 }