[ 
https://issues.apache.org/jira/browse/CXF-4912?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13611657#comment-13611657
 ] 

Parwiz Rezai commented on CXF-4912:
-----------------------------------

i commented in a fixed bug that was complaining about run time exception 
mapping for client side.. so here is that one as well. 
https://issues.apache.org/jira/browse/CXF-4496

actually the bigger issue is that it always picks the first exception mapper 
based on method signature.
https://issues.apache.org/jira/browse/CXF-4912
what's happening right now is if you say have two different exception in your 
throws method
myMethod() throws Exception2, Exception1;
and mappers for both of those exceptions on server side and client side (maybe 
you are doing different
things.. so yes i know you can have one super class if they are both checked 
exceptions.. let's say 
you have a legacy exception and you don't have source for it.. and a new 
exception you created for something else)
now the client code is blind and picks the first mapper when a fault occurs so 
in this it will always
default to Exception2ResponeMapper on client side.. even if in the method you 
happen to throw Exception1
this is incorrect..
please take a look again as this is a big issue if you have multiple mappers 
and a method that
throws more than one exception in its signature (be it checked or runtime or 
multiple checked exceptions)... 
it will pick the first mapper for the first exception listed after throws 
keyword.. not 
actually match based on actual exception type and mappers available.
maybe allow the response to have a header of some sort that contains the 
exception name/class
and in the findExceptionMapper() use that header to know exactly what exception 
occurred and go
find a mapper based on that type instead of picking the first exception after 
the throws clause 
aka Class<?>[] exTypes = m.getExceptionTypes(); <-- the mapper is selected 
based on the order returned by this
                
> cxf rest client always picks the first ResponseExceptionMapper for a method 
> with different exception throws
> -----------------------------------------------------------------------------------------------------------
>
>                 Key: CXF-4912
>                 URL: https://issues.apache.org/jira/browse/CXF-4912
>             Project: CXF
>          Issue Type: Bug
>          Components: JAX-RS
>    Affects Versions: 2.7.3
>         Environment: apache-tomcat-6.0.33, jdk1.6.0_34, spring 3.2.1.RELEASE, 
> jackson 2.1.4
>            Reporter: Parwiz Rezai
>         Attachments: cxftest.zip
>
>
> 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:
> {code:borderStyle=solid}
> 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);
>     }
> }
> {code}
> {code:title=Exception1ResponseMapper.java|borderStyle=solid}
> 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());
>         }
>     }
> }
> {code}
> {code:title=Exception2ResponseMapper.java|borderStyle=solid}
> 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());
>         }
>     }
> }
> {code}
> so suppose one mapper does something different than the other one.
> now my service method:
> {code:borderStyle=solid}
> @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?");
>     }
> }
> {code}
> {code:title=creating client proxy|borderStyle=solid}
>     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
>     }
> {code}
> following is exception to response mappers on service side
> {code:title=Exception mapping on server side|borderStyle=solid}
> @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();
>     }
> }
> {code}
> {code:xml|title=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>
> {code}
> on the server side the right response mapper is invoked correctly based on 
> type of Exception thrown.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to