From b2ffdafd3534c8b2bf0856c16a2b0974fbe3555b Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Thu, 22 Apr 2021 13:36:31 +0300 Subject: [PATCH] Execute ClientResponseFilter as part of the abort chain in JAX-RS Client This doesn't seem to be specified by the spec or properly tested in the TCK (I actually had to disable a single test that threw a NPE), but it does seem to be what RESTEasy does Relates to: #16702 --- .../test/ClientResponseFilterTestCase.java | 77 +++++++++++++++++++ .../client/impl/AsyncInvokerImpl.java | 9 +-- .../reactive/client/impl/ClientImpl.java | 7 +- .../reactive/client/impl/HandlerChain.java | 18 ++++- .../client/impl/InvocationBuilderImpl.java | 10 +-- .../reactive/client/impl/WebTargetImpl.java | 9 +-- tcks/resteasy-reactive/pom.xml | 2 +- 7 files changed, 104 insertions(+), 28 deletions(-) create mode 100644 extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/test/java/io/quarkus/jaxrs/client/reactive/deployment/test/ClientResponseFilterTestCase.java diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/test/java/io/quarkus/jaxrs/client/reactive/deployment/test/ClientResponseFilterTestCase.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/test/java/io/quarkus/jaxrs/client/reactive/deployment/test/ClientResponseFilterTestCase.java new file mode 100644 index 0000000000000..e212f0c9a0801 --- /dev/null +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/test/java/io/quarkus/jaxrs/client/reactive/deployment/test/ClientResponseFilterTestCase.java @@ -0,0 +1,77 @@ +package io.quarkus.jaxrs.client.reactive.deployment.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URL; + +import javax.enterprise.event.Observes; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; +import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientResponseFilter; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; +import io.vertx.core.Handler; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +public class ClientResponseFilterTestCase { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(Endpoint.class)); + + @TestHTTPResource + URL url; + + private Client client; + + @BeforeEach + public void before() { + client = ClientBuilder.newClient().register(TestClientResponseFilter.class); + } + + @AfterEach + public void after() { + client.close(); + } + + @Test + public void test() { + Response response = client.target(url.toExternalForm() + "/hello").request().get(); + assertEquals(200, response.getStatus()); + } + + public static class Endpoint { + + public void setup(@Observes Router router) { + router.route("/hello").handler(new Handler() { + @Override + public void handle(RoutingContext event) { + event.response().setStatusCode(500).end(); + } + }); + } + } + + @Provider + public static class TestClientResponseFilter implements ResteasyReactiveClientResponseFilter { + + @Override + public void filter(ResteasyReactiveClientRequestContext requestContext, ClientResponseContext responseContext) { + responseContext.setStatus(200); + } + } +} diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/AsyncInvokerImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/AsyncInvokerImpl.java index 103b350d28821..e555bbb0afa88 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/AsyncInvokerImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/AsyncInvokerImpl.java @@ -16,7 +16,6 @@ import javax.ws.rs.client.InvocationCallback; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.Response; -import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl; import org.jboss.resteasy.reactive.common.util.types.Types; import org.jboss.resteasy.reactive.spi.ThreadSetupAction; @@ -32,12 +31,11 @@ public class AsyncInvokerImpl implements AsyncInvoker, CompletionStageRxInvoker final Map properties; final ClientImpl restClient; final HandlerChain handlerChain; - final ClientRestHandler[] abortHandlerChain; final ThreadSetupAction requestContext; public AsyncInvokerImpl(ClientImpl restClient, HttpClient httpClient, URI uri, RequestSpec requestSpec, ConfigurationImpl configuration, - Map properties, HandlerChain handlerChain, ClientRestHandler[] abortHandlerChain, + Map properties, HandlerChain handlerChain, ThreadSetupAction requestContext) { this.restClient = restClient; this.httpClient = httpClient; @@ -46,7 +44,6 @@ public AsyncInvokerImpl(ClientImpl restClient, HttpClient httpClient, URI uri, R this.configuration = configuration; this.properties = new HashMap<>(properties); this.handlerChain = handlerChain; - this.abortHandlerChain = abortHandlerChain; this.requestContext = requestContext; } @@ -258,8 +255,8 @@ RestClientRequestContext performRequestInternal(String httpMethodName, Entity boolean registerBodyHandler) { RestClientRequestContext restClientRequestContext = new RestClientRequestContext(restClient, httpClient, httpMethodName, uri, requestSpec.configuration, requestSpec.headers, - entity, responseType, registerBodyHandler, properties, handlerChain.toRestHandler(configuration), - abortHandlerChain, requestContext); + entity, responseType, registerBodyHandler, properties, handlerChain.createHandlerChain(configuration), + handlerChain.createAbortHandlerChain(configuration), requestContext); restClientRequestContext.run(); return restClientRequestContext; } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java index 68f31081ede2e..1a4c9958de98c 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java @@ -57,9 +57,7 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Link; import javax.ws.rs.core.UriBuilder; -import org.jboss.resteasy.reactive.client.handlers.ClientErrorHandler; import org.jboss.resteasy.reactive.client.spi.ClientContext; -import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl; import org.jboss.resteasy.reactive.common.jaxrs.MultiQueryParamMode; import org.jboss.resteasy.reactive.common.jaxrs.UriBuilderImpl; @@ -76,7 +74,6 @@ public class ClientImpl implements Client { final SSLContext sslContext; private boolean isClosed; final HandlerChain handlerChain; - final ClientRestHandler[] abortHandlerChain; final Vertx vertx; private final MultiQueryParamMode multiQueryParamMode; @@ -117,7 +114,6 @@ public Vertx get() { options.setMaxRedirects((Integer) maxRedirects); } this.httpClient = this.vertx.createHttpClient(options); - abortHandlerChain = new ClientRestHandler[] { new ClientErrorHandler() }; handlerChain = new HandlerChain(followRedirects); } @@ -162,8 +158,7 @@ public WebTarget target(UriBuilder uriBuilder) { if (uriBuilder instanceof UriBuilderImpl && multiQueryParamMode != null) { ((UriBuilderImpl) uriBuilder).multiQueryParamMode(multiQueryParamMode); } - return new WebTargetImpl(this, httpClient, uriBuilder, new ConfigurationImpl(configuration), handlerChain, - abortHandlerChain, null); + return new WebTargetImpl(this, httpClient, uriBuilder, new ConfigurationImpl(configuration), handlerChain, null); } @Override diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/HandlerChain.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/HandlerChain.java index 6999067e62c23..67beeba86fbe4 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/HandlerChain.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/HandlerChain.java @@ -4,6 +4,7 @@ import java.util.List; import javax.ws.rs.client.ClientRequestFilter; import javax.ws.rs.client.ClientResponseFilter; +import org.jboss.resteasy.reactive.client.handlers.ClientErrorHandler; import org.jboss.resteasy.reactive.client.handlers.ClientRequestFilterRestHandler; import org.jboss.resteasy.reactive.client.handlers.ClientResponseCompleteRestHandler; import org.jboss.resteasy.reactive.client.handlers.ClientResponseFilterRestHandler; @@ -19,14 +20,16 @@ class HandlerChain { private final ClientRestHandler clientSendHandler; private final ClientRestHandler clientSetResponseEntityRestHandler; private final ClientRestHandler clientResponseCompleteRestHandler; + private final ClientRestHandler clientErrorHandler; public HandlerChain(boolean followRedirects) { this.clientSendHandler = new ClientSendRequestHandler(followRedirects); this.clientSetResponseEntityRestHandler = new ClientSetResponseEntityRestHandler(); this.clientResponseCompleteRestHandler = new ClientResponseCompleteRestHandler(); + this.clientErrorHandler = new ClientErrorHandler(); } - ClientRestHandler[] toRestHandler(ConfigurationImpl configuration) { + ClientRestHandler[] createHandlerChain(ConfigurationImpl configuration) { List requestFilters = configuration.getRequestFilters(); List responseFilters = configuration.getResponseFilters(); if (requestFilters.isEmpty() && responseFilters.isEmpty()) { @@ -45,4 +48,17 @@ ClientRestHandler[] toRestHandler(ConfigurationImpl configuration) { result.add(clientResponseCompleteRestHandler); return result.toArray(EMPTY_REST_HANDLERS); } + + ClientRestHandler[] createAbortHandlerChain(ConfigurationImpl configuration) { + List responseFilters = configuration.getResponseFilters(); + if (responseFilters.isEmpty()) { + return new ClientRestHandler[] { clientErrorHandler }; + } + List result = new ArrayList<>(1 + responseFilters.size()); + for (int i = 0; i < responseFilters.size(); i++) { + result.add(new ClientResponseFilterRestHandler(responseFilters.get(i))); + } + result.add(clientErrorHandler); + return result.toArray(EMPTY_REST_HANDLERS); + } } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java index d83d1fa94ed5f..1b849fb60f05a 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java @@ -25,7 +25,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; -import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl; import org.jboss.resteasy.reactive.spi.ThreadSetupAction; @@ -41,14 +40,12 @@ public class InvocationBuilderImpl implements Invocation.Builder { final ConfigurationImpl configuration; final ClientImpl restClient; final HandlerChain handlerChain; - final ClientRestHandler[] abortHandlerChain; final ThreadSetupAction requestContext; final long readTimeoutMs; public InvocationBuilderImpl(URI uri, ClientImpl restClient, HttpClient httpClient, WebTargetImpl target, - ConfigurationImpl configuration, HandlerChain handlerChain, - ClientRestHandler[] abortHandlerChain, ThreadSetupAction requestContext) { + ConfigurationImpl configuration, HandlerChain handlerChain, ThreadSetupAction requestContext) { this.uri = uri; this.restClient = restClient; this.httpClient = httpClient; @@ -56,7 +53,6 @@ public InvocationBuilderImpl(URI uri, ClientImpl restClient, HttpClient httpClie this.requestSpec = new RequestSpec(configuration); this.configuration = configuration; this.handlerChain = handlerChain; - this.abortHandlerChain = abortHandlerChain; this.requestContext = requestContext; Object readTimeoutMs = configuration.getProperty(READ_TIMEOUT); if (readTimeoutMs == null) { @@ -99,7 +95,7 @@ public Invocation buildPut(Entity entity) { @Override public AsyncInvokerImpl async() { return new AsyncInvokerImpl(restClient, httpClient, uri, requestSpec, configuration, - properties, handlerChain, abortHandlerChain, requestContext); + properties, handlerChain, requestContext); } @Override @@ -171,7 +167,7 @@ public Invocation.Builder property(String name, Object value) { @Override public CompletionStageRxInvoker rx() { return new AsyncInvokerImpl(restClient, httpClient, uri, requestSpec, configuration, - properties, handlerChain, abortHandlerChain, requestContext); + properties, handlerChain, requestContext); } @Override diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java index 5e157b881553c..117196067a389 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/WebTargetImpl.java @@ -10,7 +10,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.UriBuilder; -import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; import org.jboss.resteasy.reactive.common.core.Serialisers; import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl; import org.jboss.resteasy.reactive.common.jaxrs.UriBuilderImpl; @@ -24,20 +23,17 @@ public class WebTargetImpl implements WebTarget { private boolean chunked = false; private final ClientImpl restClient; final HandlerChain handlerChain; - final ClientRestHandler[] abortHandlerChain; final ThreadSetupAction requestContext; public WebTargetImpl(ClientImpl restClient, HttpClient client, UriBuilder uriBuilder, ConfigurationImpl configuration, HandlerChain handlerChain, - ClientRestHandler[] abortHandlerChain, ThreadSetupAction requestContext) { this.restClient = restClient; this.client = client; this.uriBuilder = uriBuilder; this.configuration = configuration; this.handlerChain = handlerChain; - this.abortHandlerChain = abortHandlerChain; this.requestContext = requestContext; } @@ -264,7 +260,7 @@ public WebTargetImpl queryParamNoTemplate(String name, Object... values) throws protected WebTargetImpl newInstance(HttpClient client, UriBuilder uriBuilder, ConfigurationImpl configuration) { - return new WebTargetImpl(restClient, client, uriBuilder, configuration, handlerChain, abortHandlerChain, + return new WebTargetImpl(restClient, client, uriBuilder, configuration, handlerChain, requestContext); } @@ -300,8 +296,7 @@ private void abortIfClosed() { protected InvocationBuilderImpl createQuarkusRestInvocationBuilder(HttpClient client, UriBuilder uri, ConfigurationImpl configuration) { - return new InvocationBuilderImpl(uri.build(), restClient, client, this, configuration, handlerChain, - abortHandlerChain, requestContext); + return new InvocationBuilderImpl(uri.build(), restClient, client, this, configuration, handlerChain, requestContext); } @Override diff --git a/tcks/resteasy-reactive/pom.xml b/tcks/resteasy-reactive/pom.xml index 349e112122ed4..edbb53cf606d0 100644 --- a/tcks/resteasy-reactive/pom.xml +++ b/tcks/resteasy-reactive/pom.xml @@ -16,7 +16,7 @@ - 531734d28f1fcb39d369c1d2fae0a0f90a4ab9dc + 6bc5d1bacccaf45a7db968b57a262b8e84e4621b ${skipTests} ${exec.skip}