You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2020/04/28 00:13:10 UTC
[cxf] branch master updated: CXF-8261: Exceptions being thrown in a
ClientResponseFilter are ignored (#662)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push:
new 9584a56 CXF-8261: Exceptions being thrown in a ClientResponseFilter are ignored (#662)
9584a56 is described below
commit 9584a56a6289d8e1899a33cb6c604073d7851bbc
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Mon Apr 27 20:13:01 2020 -0400
CXF-8261: Exceptions being thrown in a ClientResponseFilter are ignored (#662)
---
.../apache/cxf/jaxrs/client/AbstractClient.java | 5 +-
.../spec/ClientResponseFilterInterceptor.java | 27 ++--
.../client/spec/ClientResponseFilterTest.java | 168 +++++++++++++++++++++
3 files changed, 190 insertions(+), 10 deletions(-)
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
index 80eb2cc..c65945c 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
@@ -48,6 +48,7 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.ResponseProcessingException;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.Form;
@@ -615,7 +616,9 @@ public abstract class AbstractClient implements Client {
Exchange exchange = outMessage.getExchange();
Integer responseCode = getResponseCode(exchange);
- if (responseCode == null
+ if (actualEx instanceof ResponseProcessingException) {
+ throw (ResponseProcessingException)actualEx;
+ } else if (responseCode == null
|| responseCode < 300 && !(actualEx instanceof IOException)
|| actualEx instanceof IOException && exchange.get("client.redirect.exception") != null) {
if (actualEx instanceof ProcessingException) {
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseFilterInterceptor.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseFilterInterceptor.java
index 0db80e3..901525c 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseFilterInterceptor.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseFilterInterceptor.java
@@ -23,10 +23,10 @@ import java.io.InputStream;
import java.util.List;
import java.util.Map;
-import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.ResponseProcessingException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
@@ -54,18 +54,27 @@ public class ClientResponseFilterInterceptor extends AbstractInDatabindingInterc
List<ProviderInfo<ClientResponseFilter>> filters = pf.getClientResponseFilters();
if (!filters.isEmpty()) {
- ClientRequestContext reqContext = new ClientRequestContextImpl(inMessage.getExchange().getOutMessage(),
- true);
-
- ClientResponseContext respContext =
- new ClientResponseContextImpl((ResponseImpl)getResponse(inMessage),
- inMessage);
+ final ClientRequestContext reqContext = new ClientRequestContextImpl(
+ inMessage.getExchange().getOutMessage(), true);
+ final ResponseImpl response = (ResponseImpl)getResponse(inMessage);
+ final ClientResponseContext respContext = new ClientResponseContextImpl(response, inMessage);
for (ProviderInfo<ClientResponseFilter> filter : filters) {
InjectionUtils.injectContexts(filter.getProvider(), filter, inMessage);
try {
filter.getProvider().filter(reqContext, respContext);
- } catch (IOException ex) {
- throw new ProcessingException(ex);
+ } catch (RuntimeException | IOException ex) {
+ // Complete the IN chain, if we won't set it, the AbstractClient::preProcessResult
+ // would be stuck waiting for the IN chain completion.
+ if (!inMessage.getExchange().isOneWay()) {
+ synchronized (inMessage.getExchange()) {
+ inMessage.getExchange().put("IN_CHAIN_COMPLETE", Boolean.TRUE);
+ }
+ }
+
+ // When a provider method throws an exception, the JAX-RS client runtime will map
+ // it to an instance of ResponseProcessingException if thrown while processing
+ // a response (4.5.2 Client Runtime).
+ throw new ResponseProcessingException(response, ex);
}
}
}
diff --git a/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spec/ClientResponseFilterTest.java b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spec/ClientResponseFilterTest.java
new file mode 100644
index 0000000..9056212
--- /dev/null
+++ b/rt/rs/client/src/test/java/org/apache/cxf/jaxrs/client/spec/ClientResponseFilterTest.java
@@ -0,0 +1,168 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.jaxrs.client.spec;
+
+import java.io.IOException;
+
+import javax.annotation.Priority;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
+import javax.ws.rs.client.ResponseProcessingException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.transport.local.LocalTransportFactory;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class ClientResponseFilterTest {
+ private static final String ADDRESS = "local://client";
+ private Server server;
+
+ @Path("/")
+ public static class TestEndpoint {
+ @GET
+ public String get() {
+ return "hello rabbit";
+ }
+
+ @PUT
+ public Response update() {
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ @DELETE
+ public void delete() {
+ }
+ }
+
+ @Priority(2)
+ public static class AddHeaderClientResponseFilter implements ClientResponseFilter {
+ @Override
+ public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
+ throws IOException {
+ responseContext.getHeaders().add("X-Done", "true");
+ }
+ }
+
+ @Priority(1)
+ public static class FaultyClientResponseFilter implements ClientResponseFilter {
+ @Override
+ public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
+ throws IOException {
+ throw new IOException("Exception from client response filter");
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ final JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+ sf.setResourceClasses(TestEndpoint.class);
+ sf.setResourceProvider(TestEndpoint.class, new SingletonResourceProvider(new TestEndpoint(), false));
+ sf.setTransportId(LocalTransportFactory.TRANSPORT_ID);
+ sf.setAddress(ADDRESS);
+ server = sf.create();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ server.stop();
+ server.destroy();
+ }
+
+ @Test(expected = ResponseProcessingException.class)
+ public void testExceptionInClientResponseFilter() {
+ try (Response response = ClientBuilder.newClient()
+ .register(FaultyClientResponseFilter.class)
+ .target(ADDRESS)
+ .request()
+ .get()) {
+ assertEquals(200, response.getStatus());
+ assertEquals("hello rabbit", response.readEntity(String.class));
+ }
+ }
+
+ @Test(expected = ResponseProcessingException.class)
+ public void testExceptionInClientResponseFilterWhenNotFound() {
+ try (Response response = ClientBuilder.newClient()
+ .register(FaultyClientResponseFilter.class)
+ .target(ADDRESS)
+ .request()
+ .put(null)) {
+ fail("Should not be invoked");
+ }
+ }
+
+ @Test
+ public void testClientResponseFilterWhenNotFound() {
+ try (Response response = ClientBuilder.newClient()
+ .register(AddHeaderClientResponseFilter.class)
+ .target(ADDRESS)
+ .request()
+ .put(null)) {
+ assertEquals(404, response.getStatus());
+ assertEquals("true", response.getHeaderString("X-Done"));
+ }
+ }
+
+ @Test
+ public void testClientResponseFilter() {
+ try (Response response = ClientBuilder.newClient()
+ .register(AddHeaderClientResponseFilter.class)
+ .target(ADDRESS)
+ .request()
+ .get()) {
+ assertEquals(200, response.getStatus());
+ assertEquals("true", response.getHeaderString("X-Done"));
+ }
+ }
+
+ @Test
+ public void testExceptionWhenMultipleClientResponseFilters() {
+ try (Response response = ClientBuilder.newClient()
+ .register(AddHeaderClientResponseFilter.class)
+ .register(FaultyClientResponseFilter.class)
+ .target(ADDRESS)
+ .request()
+ .put(null)) {
+ fail("Should not be invoked");
+ } catch (ResponseProcessingException ex) {
+ // Seems to be an issue here, CXF creates the response context only once
+ // for all client response filters, the changes performed upstream the chain
+ // are not visible to the downstream filters.
+ assertEquals(null, ex.getResponse().getHeaderString("X-Done"));
+ } catch (Throwable ex) {
+ fail("Should be handled by ResponseProcessingException block");
+ }
+ }
+}
\ No newline at end of file