Support CompletionStage<Response>

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java
index 0372940..52d812f 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -85,6 +85,7 @@
     private final Type invocableResponseType;
     private final boolean canUseInvocableResponseType;
     private final boolean isCompletionStageResponseType;
+    private final boolean isCompletionStageResponseResponseType; // CompletionStage<Response>
     private final Type completionStageResponseType;
     private final ResourceMethodDispatcher dispatcher;
     private final Method resourceMethod;
@@ -313,6 +314,8 @@
                 && CompletionStage.class.isAssignableFrom((Class<?>) ((ParameterizedType) invocableResponseType).getRawType());
         this.completionStageResponseType =
                 isCompletionStageResponseType ? ((ParameterizedType) invocableResponseType).getActualTypeArguments()[0] : null;
+        this.isCompletionStageResponseResponseType = Class.class.isInstance(completionStageResponseType)
+                && Response.class.isAssignableFrom((Class<?>) completionStageResponseType);
     }
 
     private <T> void addNameBoundProviders(
@@ -465,7 +468,7 @@
             if (canUseInvocableResponseType
                     && response.hasEntity()
                     && !(response.getEntityType() instanceof ParameterizedType)) {
-                response.setEntityType(unwrapInvocableResponseType(context.request()));
+                response.setEntityType(unwrapInvocableResponseType(context.request(), response.getEntityType()));
             }
 
             return response;
@@ -484,10 +487,10 @@
         return jaxrsResponse;
     }
 
-    private Type unwrapInvocableResponseType(ContainerRequest request) {
+    private Type unwrapInvocableResponseType(ContainerRequest request, Type entityType) {
         if (isCompletionStageResponseType
                 && request.resolveProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.FALSE)) {
-            return completionStageResponseType;
+            return isCompletionStageResponseResponseType ? entityType : completionStageResponseType;
         }
         return invocableResponseType;
     }
diff --git a/tests/e2e-server/pom.xml b/tests/e2e-server/pom.xml
index 2650a3d..18cd641 100644
--- a/tests/e2e-server/pom.xml
+++ b/tests/e2e-server/pom.xml
@@ -102,6 +102,11 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-binding</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
             <artifactId>jersey-media-json-gson</artifactId>
             <scope>test</scope>
         </dependency>
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java
index cf7ade3..fb4eafc 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/CompletionStageTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,6 +18,7 @@
 
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.server.ServerRuntime;
 import org.glassfish.jersey.test.JerseyTest;
 import org.junit.Test;
 
@@ -64,7 +65,8 @@
 
     @Override
     protected Application configure() {
-        return new ResourceConfig(CompletionStageResource.class, DataBeanWriter.class);
+        return new ResourceConfig(CompletionStageResource.class, DataBeanWriter.class)
+                .property(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE);
     }
 
     @Test
@@ -105,6 +107,14 @@
     }
 
     @Test
+    public void testGetCompletedAsyncResponse() {
+        Response response = target("cs/completedAsyncResponse").request().get();
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(List.class).get(0), is(ENTITY));
+    }
+
+    @Test
     public void testGetException400Async() {
         Response response = target("cs/exception400Async").request().get();
 
@@ -214,6 +224,14 @@
         }
 
         @GET
+        @Path("/completedAsyncResponse")
+        public CompletionStage<Response> getCompletedAsyncResponse() {
+            CompletableFuture<Response> cs = new CompletableFuture<>();
+            delaySubmit(() -> cs.complete(Response.ok().entity(Collections.singletonList(ENTITY)).build()));
+            return cs;
+        }
+
+        @GET
         @Path("/exception400Async")
         public CompletionStage<String> getException400Async() {
             CompletableFuture<String> cs = new CompletableFuture<>();