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 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);
}
}
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.
@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; if Client API does
not support custom client side exception handling for any reason? What is the
difference between Client API and JAXRSClientFactory implementations?
Any help appreciated. Thanks.
Sanjeev