Uploaded image for project: 'CXF'
  1. CXF
  2. CXF-4912

cxf rest client always picks the first ResponseExceptionMapper for a method with different exception throws

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 2.7.3
    • Fix Version/s: 2.7.4, 2.6.7, 2.5.10, 3.0.0-milestone1
    • Component/s: JAX-RS
    • Labels:
      None
    • Environment:

      apache-tomcat-6.0.33, jdk1.6.0_34, spring 3.2.1.RELEASE, jackson 2.1.4

    • Estimated Complexity:
      Unknown

      Description

      The selection for response mapper on client side will always pick the first
      exception listed and invoke the mapper for that guy.. it will never
      invoke Exception1ResponseMapper even though our exception is of type
      Exception1..

      throws Exception2, Exception1 <-- this will always find a mapper for Exception2
      first and use that even if our actual eexception is Exception1 in this case.

      I think the code needs to lookup the mapper based on type as well
      instead of generic first mapper found for that service.

      I have two differnt exceptions:

      public class Exception1 extends Exception {
          public Exception1() { }
      
          public Exception1(String msg) {
              super(msg);
          }
      }
      
      public class Exception2 extends Exception {
          public Exception2() {}
      
          public Exception2(String msg) {
              super(msg);
          }
      }
      
      Exception1ResponseMapper.java
      import com.fasterxml.jackson.core.JsonParser;
      import com.fasterxml.jackson.databind.MappingJsonFactory;
      import java.io.InputStream;
      import javax.ws.rs.core.Response;
      import org.apache.cxf.jaxrs.client.ResponseExceptionMapper;
      
      public class Exception1ResponseMapper implements ResponseExceptionMapper<Exception1> {
          @Override
          public Exception1 fromResponse(Response response) {
              try {
      	    MappingJsonFactory factory = new MappingJsonFactory();
      	    JsonParser parser = factory.createJsonParser((InputStream) response.getEntity());
      	    Exception1 exception = parser.readValueAs(Exception1.class);
      	    return exception;
      	} catch (Exception ex) {
      	    return new Exception1("Could not deserialize server side exception: " + ex.getMessage());
              }
          }
      }
      
      Exception2ResponseMapper.java
      import com.fasterxml.jackson.core.JsonParser;
      import com.fasterxml.jackson.databind.MappingJsonFactory;
      import java.io.InputStream;
      import javax.ws.rs.core.Response;
      import org.apache.cxf.jaxrs.client.ResponseExceptionMapper;
      
      public class Exception2ResponseMapper implements ResponseExceptionMapper<Exception2> {
          @Override
          public Exception2 fromResponse(Response response) {
              try {
      	    MappingJsonFactory factory = new MappingJsonFactory();
      	    JsonParser parser = factory.createJsonParser((InputStream) response.getEntity());
      	    Exception2 exception = parser.readValueAs(Exception2.class);
                  // do some specific work for exception 2 only
      	    return exception;
      	} catch (Exception ex) {
      	    return new Exception2("Could not deserialize server side exception: " + ex.getMessage());
              }
          }
      }
      

      so suppose one mapper does something different than the other one.

      now my service method:

      @Path("/cool")
      @Consumes(MediaType.APPLICATION_JSON)
      @Produces(MediaType.APPLICATION_JSON)
      public interface MyService {
          @GET
          SomeCoolObject myMethod() throws Exception2, Exception1;
      }
      
      @Service("myService")
      public class MyServiceImpl implements MyService {
          public SomeCoolObject myMethod() throws Exception2, Exception1 {
              throw new Exception1("hey this exception will still go exception 2 mapper.. why?");
          }
      }
      
      creating client proxy
          List providers = new ArrayList<Object>();
          providers.add(new com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider());
          providers.add(new Exception1ResponseMapper());
          providers.add(new Exception2ResponseMapper());
          MyService serviceProxy = JAXRSClientFactory.create("http://localhost:8080/", MyService.class, providers);
      
          try {
              serviceProxy.myMethod();
          } catch(Exception e) {
           // should get back Exception1 but no go
          }
      
      

      following is exception to response mappers on service side

      Exception mapping on server side
      @Provider
      public class Exception1AsResponseMapper implements ExceptionMapper<Exception1> {
      
          @Override
          public Response toResponse(Exception1 exception) {
              return Response.ok(exception, MediaType.APPLICATION_JSON).status(Response.Status.BAD_REQUEST).build();
          }
      }
      
      @Provider
      public class Exception2AsResponseMapper implements ExceptionMapper<Exception2> {
      
          @Override
          public Response toResponse(Exception2 exception) {
              return Response.ok(exception, MediaType.APPLICATION_JSON).status(Response.Status.BAD_REQUEST).build();
          }
      }
      
      spring configs for cxf servlet
      <import resource="classpath:META-INF/cxf/cxf.xml" />
      <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
      <context:annotation-config/>
      	
      <context:component-scan base-package="com.test"/>
      
      <jaxrs:server id="services" address="/">
      		<jaxrs:serviceBeans>
      			<ref bean="myService"/>
      		</jaxrs:serviceBeans>
      
      		<jaxrs:providers>
      			<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>
      			<bean class="com.test.server.Exception1AsResponseMapper"/>
      			<bean class="com.test.server.Exception2AsResponseMapper"/>
      		</jaxrs:providers>
      		<jaxrs:extensionMappings>
      			<entry key="json" value="application/json" />
      		</jaxrs:extensionMappings>
      	</jaxrs:server>
      

      on the server side the right response mapper is invoked correctly based on type of Exception thrown.

        Attachments

        1. cxftest.zip
          10 kB
          Parwiz Rezai
        2. ClientProxyImpl.java.patch
          2 kB
          Parwiz Rezai

          Activity

            People

            • Assignee:
              sergey_beryozkin Sergey Beryozkin
              Reporter:
              parwiz Parwiz Rezai
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: