JAX-RS exception handling and ExceptionMapper - java

I have implemented some custom exceptions like: NotFoundException, BadRequestException,
and for each of them I have implemented its own ExceptionMapper like NotFoundExceptionMapper, BadRequestExceptionMapper and also something like GenericExceptionMapper:
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
#Override
#Produces( {MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON} )
public Response toResponse(Throwable ex) {
ErrorResponseWrapper errorResponse = new ErrorResponseWrapper(ex.getMessage(), 500, "");
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorResponse).build();
}
}
Firstly the problem here is that GenericExceptionMapper doesn't work as I expected it doesn't catch generic exceptions thrown by JAX-RS as I think each Exception to be caught by such Mapper must be early explicitly thrown from the method like getResource() throws SomeException {
Moreover the problem is that in RestEasy I need to register exception mappers in web.xm. See below:
<!-- Custom exception handling provider -->
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>pl.salonea.jaxrs.exceptions.GenericExceptionMapper</param-value>
</context-param>
Here is another problem as I can not register (or I don't know how to register) each custom ExceptionMapper I have defined. I can only register SINGLE custom ExceptionMapper.
How to register all of them? i.e. NotFoundExceptionMapper, BadRequestExceptionMapper, etc.
So now each exception is mapped only by Generic Exception Mapper and that is the first problem.
Is this limitation of RESTEasy? in Jersey on some tutorial I haven't seen the need to register such ExceptionMappers but I also don't know whether there can be more than one exception Mapper.
Another solution (workaround) that came to my mind is to have single GenericExceptionMapper and in the method toResponse(Throwable ex) makes some checks like:
if(ex instanceof NotFoundException) { // send response type 1
if(ex instanceof BadRequestException) { // send response type 2
Thx for help

Related

Spring ExceptionHandler but for normal beans

I have been able to successfully use #ExceptionHandler annonated methodsorg.springframework.web.bind.annotation.ExceptionHandler in Controller Classes in my Spring projects to handle exceptions thrown by spring #RestController
Working example:
#Validated
#RestController
#RequestMapping(value = UrlsProperties.API_PATH, produces = MediaType.APPLICATION_JSON, consumes = { MediaType.APPLICATION_JSON })
#Api(value = "MyController", description = "MyController processing and forwarding controller")
public class MyController {
private static Logger log = LogManager.getLogger(MyController.class);
...
#JsonFormat
#ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseMessage handleMissingParams(MissingServletRequestParameterException ex) {
String name = ex.getParameterName();
log.error(name + " parameter is missing");
return new ResponseMessage(400, ex.getMessage());
}
}
I am trying to achieve the same way of exception handling but for a normal bean, [ not a controller ]. Simply adding an #ExceptionHanlder annotated method did not seem to catch the exceptions thrown by that bean's methods.
My question is how to handle exceptions thrown by a bean by writing a method inside this bean?
#ExceptionHandler annotation is not for general exception handling. It's used in controllers to convert an exception into a proper HTTP response. It won't work for normal beans, because only controllers return a response.
If any code (doesn't need to be in a bean) throws an exception and you don't handle it, it would eventually propagate up to your controller's exception handler and it would be converted to a response. That would be poor design though, as you should handle exceptions as early as you can.
What you can do is create exceptions that are meant to be propagated to your exception handlers. Your code catches an exception, then re-throws it wrapped into your own exception (such as IllegalRequestException). The handler then returns an error code and details to the caller.

Custom WebApplicationExceptions in jaxrs caught by ExceptionMapper show up in server log

I use the following Exceptionmapper to map WebApplicationExceptions in my jaxrs rest api to responses.
#Provider
public class ErrorHandler implements ExceptionMapper<WebApplicationException> {
#Override
public Response toResponse(WebApplicationException e) {
int status = e.getResponse().getStatus();
JsonObject errorResponse = Json.createObjectBuilder()
.add("status", status)
.add("message", e.getMessage())
.build();
return Response.status(status)
.entity(errorResponse)
.type(MediaType.APPLICATION_JSON)
.build();
}
}
This works fine and it does exactly what it should do, but when I throw custom errors, for example throw new NotFoundException("custom message"); the stacktrace shows up in my server log. Can anyone explain this? Or does anyone know of a solution?
TL;DR;
For some reason when I throw WebApplicationExceptions from my jax-rs code, my ExceptionMapper handles the error but still throws it so it shows up in the server log.
Any solutions?
I've found the origin of this problem and managed to solve it.
From the JAX-RS spec
When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception.
In my ExceptionMapper I used the WebApplicationException, so every error would be mapped. The problem is that WebApplicationException is not the nearest superclass of (e.g.) NotFoundException. There is a ClientErrorException superclass inbetween. When I changed my mapper to that class the problem was solved.
Since I only want to map client errors to Json responses this solution works fine for me.

How can I customize exceptions thrown when #Produces and #Consumes is violated

I'm working on i18n(Internationalization and localization) task of my
REST services. Now I want to pass the error message according to the
Accept-Language of the header to the Exceptions thrown when Accept or
Content-Type of the header's not matched with #Produces and
#Consumes.
I found a solution in ContainerRequestFilter, but if I
check the Accept and Content-Type of the header in that
ContainerRequestFilter and throw Exception when it's not matched the
MediaType I want, there will be no need to use #Produces and
#Consumes again in the Resource.
So my question is, is there a way to
customize the exceptions thrown when #Produces and #Consumes is
violated (I mean NotAcceptableException and NotSupportedException)?
Because I want to pass the error message in multiple languages to the
message in these Exceptions.
You may use ExceptionMapper which will catch exceptions thrown in your service and return formatted response. It may be customized to handle specific exceptions.
#Provider
#Singleton
public class ExceptionMapperProvider implements ExceptionMapper<Exception> {
#Override
public Response toResponse(final Exception exception){
return Response.status(HttpStatusCodes.STATUS_CODE_SERVER_ERROR).entity(new BasicResponse(InternalStatus.UNHANDLED_EXCEPTION, exception.getMessage())).type(MediaType.APPLICATION_JSON).build();
}
}

Rest Exceptions: Wrappers vs Error Object

Lets say we have a rest service defined as:
#GET
#Produces("application/json")
public Response getAllCategories(#QueryParam(value="startIndex") int startIndex, #QueryParam(value="size") int size)
{
logger.info("[SampleCategoryController][getAllCategories]");
List<YpCategory> categoryList = sampleCategoryService.getAllCategories(startIndex, size);
return Response.ok(categoryList).build();
}
and the service is defined as:
public class SampleCategoriesServiceImpl {
public List<YpCategory> getAllCategories(int startIndex, int size) {
...
//call some method that throws a runtime exception
...
}
}
And an Application Exception handler:
#Provider
#Component
public class ApplicationExceptionHandler implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable ex) {
String internalError = "There was a problem processing your request.";
return Response.serverError().entity(new ExceptionResponse(500, internalError)).build();
}
}
}
Exception response object: Let the exception bubble up to the ApplicationExceptionHandler and return the ExceptionResponse Object. This way seems cleaner because the service doesn't have to try to handle an exception that it can't really do anything with and the client will still receive a json response.
Response wrapper: The category object would extend some type of generic response wrapper object with information about error codes then I would always have to wrap the method that can throw a runtime exception in a try/catch block and set the error codes and message info in the catch block.
Is one of these ways preferred? Are there cons to using either one of these methods to handle errors?
I think you should use the ExceptionMapper in this case. It is cleaner to let exceptions be handled outside of your implementation.
Also your implementation should be as less possible aware of HTTP. The less your implementation knows about the other parts of your framework the more flexible it will become.
To give an example. Lets say that in the future there is support for a non-HTTP protocol and error messaging will go different then using HTTP status code. You can do the implementation at the level of ExceptionMapper without changing your implementation. Otherwise you have to refactor your application to be aware of the non-HTTP protocol.
Just to be clear, I don't say there is an other implementation available now. It is just a theory.

REST resource exception interceptor

I am trying to determine if it is possible to setup an interceptor like solution on a REST resource such that if an exception is thrown I can log the exception and change the response returned. I basically don't want to wrap all my REST resources with try/catch blocks. If a REST resource was managed I would just use an #Interceptor on all of my calls but since it is not managed that seems to be out of the question.
You can use an implementation javax.ws.rs.ext.ExceptionMapper. Let's suppose that your code might throw a YourFancyException from the resources. Then you can use the following mapper:
#Provider
public class YourFancyExceptionMapper
implements ExceptionMapper <YourFancyException> {
#Override
public Response toResponse(YourFancyException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(exception.getMessage()).build();
}
}
Don't forget to annotate the mapper with #Provider and to make your resources methods to throw YourFancyException.

Categories

Resources