Hi

ResponseExceptionMapper is a CXF specific provider for facilitating the mapping of HTTP errors to *proxy* API specific exceptions.

For example, if you have BookInterface#getBook() throws BookNotFounfException, then when you invoke

bookInterface.getBook() and the server returns 404, then you may want to map it to BookNotFoundException as opposed to the client runtime returning JAX-RS 2.0 NotFoundException.

If you prefer to work with the standard JAX-RS 2.0 client API then the right way to handle your case is as follows:

try {
   // do the call
} catch (WebApplicationException ex) {
    // if 412
    ex.getResponse().readEntity(RequestError.class)
}

HTH, SErgey

On 15/06/17 21:12, RAYAPROLU, SANJEEV wrote:
Hello,

Problem Statement: Unable to implement custom client side exception handler 
using Client (javax.ws.rs.client.Client) API, and @Provider class implementing 
the ResponseExceptionMapper interface.
Questions: 1. Does Client API not support custom client side providers for 
exception handling? 2. Any literature I looked up for this problem statement 
uses JAXRSClientFactory implementation; I'm yet to find any using Client API 
for this scenario. Would I have to switch my implementation? 3. What is the 
difference between Client API and JAXRSClientFactory implementations?

I am working on a cxf Client API implementation in Java, and noticed that for 
most http status codes above 300 cxf wraps the Response in either a 
WebApplicationException or ProcessingException (depending upon the response 
status code). The server in my case has a customized response body indicating 
the actual reason for an http status code !200, like below (for response code = 
412):

{
"requestError": {
   "serviceException": {
        "messageId": "SVC4120",
        "text": "Invalid Request: Invalid Coupon Code."
     }
   }
}

Unfortunately the WebApplicationException itself does not render this. Instead the only 
message captured in the exception directly is a generic "412 Precondition 
Failed". I can do something similar to below exception block from code snippet 
(includes Client API code snippet):

protected RESPOBJ invoke(String endPointUrl) throws CustomException {
        Object reqPOJO = prepareRequest();
        try {
                if(client == null) {
                        ClientBuilder builder = ClientBuilder.newBuilder();
                        //register custom JAX-RS components
                        builder.register(new CustomMapper());
                }
                WebTarget target = client.target(endPointUrl);
                //do this when queryParams exist
                if(!getUriParams().isEmpty()) {
                        for(Map.Entry<String, String> queryParams : 
getUriParams().entrySet()) {
                                target = 
target.queryParam(queryParams.getKey(), queryParams.getValue());
                        }
                }
                Invocation.Builder builder = target.request();
                //create headers here
                MultivaluedMap<String, Object> headers = new 
MultivaluedHashMap<>();
                if(isBasicAuthRequired()) {
                        headers.add(AUTH_HEADER_PARAM, 
getBasicAuthentication());
                }
                headers.add(CONTENT_TYPE, getMediaType().toString());
                builder.headers(headers);
                builder.accept(getMediaType().toString());
                //GET or POST
                if(HttpMethodType.GET.equals(getHttpMethod())) {
                        return builder.get(RESPOBJ.class);
                }
                return builder.post(Entity.entity(reqPOJO, getMediaType()), 
RESPOBJ.class);
        }
        catch (Exception ex) {
                if(ex instanceof ResponseProcessingException) {
                        ResponseProcessingException e = 
(ResponseProcessingException) ex;
                        logger.error("Unmarshalling failed: [" + 
e.getResponse().readEntity(String.class) + "]");
                }
                else if(ex instanceof WebApplicationException) {
                        WebApplicationException e = (WebApplicationException) 
ex;
                        logger.error("Error Response: 
["+e.getResponse().readEntity(String.class) + "]");
                }
                throw new CustomException(ex);
        }
}
Although, I am looking to implement something cleaner, preferably using a custom 
Exception handler that implements ResponseExceptionMapper<> interface. From 
literature I noticed the only implementations of ResponseExceptionMapper for custom 
client side exception handling are using JAXRSClientFactory. My current 
implementation however uses the Client API (code snippet below). From a design aspect 
I will modify this to have a separate CustomExceptionMapper class that would be the 
Provider only for Exception cases, but I do not see why this Custom class is 
registered as a Provider (works for 200 status codes as MBR, and the MBW works 
always) but does not work for exception cases.

Update: While debugging and observing changes between a 200 vs >300 status code 
(412 in my case), I noticed that for 200 case 
JAXRSUtils.readFromMessageBodyReader() method gets invoked, which for the 1st time 
retrieves the Custom Provider. The code never gets here for status codes shown 
below in code snippet which should be the reason for not finding the CustomMapper. 
So is there any difference in how I must register my CustomExceptionMapper? Or 
does the Client API simply not support this functionality?

// for failure case the method above returns null (status > 300), whereas for 
success 200 case it executes method in last line and gets the provider.
// AbstractClient class that invokes the doReadEntity() method which in turn 
invokes and finds the Provider in JAXRSUtils.readFromMessageBodyReader() method 
code

protected <T> T readBody(Response r, Message outMessage, Class<T> cls,
                                                 Type type, Annotation[] anns) {
        
        if (cls == Response.class) {
                return cls.cast(r);
        }
        
        int status = r.getStatus();

        //this is invoked for failure case
        if ((status < 200 || status == 204) && r.getLength() <= 0 || status >= 
300) {
                return null;
        }
        //this for 200 status code
        return ((ResponseImpl)r).doReadEntity(cls, type, anns);
}

//My custom provider code
@Provider
@Consumes
@Produces(MediaType.APPLICATION_JSON)
public class CustomMapper implements MessageBodyReader<CustomResponse>, 
MessageBodyWriter<CustomRequest>, ResponseExceptionMapper<CustomException> {
                 private Gson gson = new 
GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

                 @Override
                 public boolean isReadable(Class<?> type, Type genericType, 
Annotation[] annotations, MediaType mediaType) {
                                 return 
type.isAssignableFrom(CustomResponse.class);
                 }

                 @Override
                 public CustomResponse readFrom(Class<CustomResponse> type, 
Type genericType, Annotation[] annotations,
                                                 MediaType mediaType, 
MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws 
IOException, WebApplicationException {
                                 CustomResponse respObj = new CustomResponse();
                                 //json to pojo code
                                 return respObj;
                 }

                 @Override
                 public long getSize(CustomRequest reqObj, Class<?> type, Type 
genericType, Annotation[] annotations, MediaType mediaType) {
                                 return -1;
                 }

                 @Override
                 public boolean isWriteable(Class<?> type, Type genericType, 
Annotation[] annotations, MediaType mediaType) {
                                 return 
type.isAssignableFrom(CustomRequest.class);
                 }

                 @Override
                 public void writeTo(CustomRequest reqObj, Class<?> type, Type 
genericType, Annotation[] annotations, MediaType mediaType,
                                                 MultivaluedMap<String, Object> 
httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
                                 
entityStream.write(gson.toJson(reqObj).getBytes());
                 }

                 @Override
                 public CustomException fromResponse(Response 
exceptionResponse) {
                                 //Response obj to my CustomException code
                                 return (CustomException);
                 }
}


Question: I'm trying to figure out what is done wrong here, and if Client API 
does not support custom client side exception handling for any reason? What is 
the difference between Client API and JAXRSClientFactory implementations? I 
also am looking into possibly using ClientResponseFilter (haven't researched 
this fully yet).

Any help appreciated. Thanks.

Sanjeev

Reply via email to