Bind Exception Handling to #ControllerAdvice class only - java

I have a controller class with REST methods that can throw various exceptions. I have decided to handle these exceptions in a separate class using the #ControllerAdvice and #ExceptionHandler for my handler methods.
However, I have the problem, that my REST methods use an annotation from another library. This library catches an exception that my REST method throws as well.
Now that I am handling the exceptions globally and not via try/catch in the REST method directly, my exception is always caught by the other library and not by my own handler method. Apparently, that other method in the library I am using wins due to the annotation.
How can I bind the exception handling to my own class to prevent it from being caught by anyone else?
My REST method:
#SomeLibraryAnnotation
#PostMapping(path = "/add", consumes = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity< ? > addItem(#RequestHeader HttpHeaders headers, #RequestBody MyDTO myDTO)
throws UnsupportedOperationException {
doSomethingWith(myDTO);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
My Exception Handler class:
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(UnsupportedOperationException.class)
public ResponseEntity<?> handleUnsupportedOperationException(UnsupportedOperationException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
}
}
As the library method also catches the UnsupportedOperationException, it wins due to the #SomeLibraryAnnotation and the exception is never handled in my handler class.

You might try using #Order or #Priority in the MyExceptionHandler class, as discussed in Setting Precedence of Multiple #ControllerAdvice #ExceptionHandlers.
That would give Spring an opportunity to use your class instead of the one specified by the #SomeLibraryAnnotation. However, without knowing how Spring interprets that other annotation at context initialization, that's just a guess.

Did you tried to write #ExceptionHandler inside your controller? Like:
#RestController
#RequestMapping("/path")
public class TheController {
#ExceptionHandler(UnsupportedOperationException.class)
public ResponseEntity<?> handleUnsupportedOperationException(UnsupportedOperationException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
}
}
Maybe that would pickup and exception with higher priority. It's hard to answer not knowing what #SomeLibraryAnnotation is...

Those are simply Java Language rules, i.e. exception is no longer unhandled, as it was handled (caught in a catch block) by your other library. What you can do it is to re-throw (maybe conditionally) another exception in your library which caught original exception and see if #ExceptionHandler will handle it. It might not because #ExceptionHandler is handling exceptions thrown in Controller classes.
Second approach would be to throw exception which is only handled in #ExceptionHandler and then re-throw it be handled in other library.
In other words you need to choose where to handled first originally thrown exception.
Third approach would be use AOP interceptor #AfterThrowing or #Around and then execute whatever logic you want within.
Essence : There is no way to handle exception in two places at one time. Does it make sense?

Related

Spring boot exception handling for specific annotated methods

Our spring boot controllers have methods called by ajax as well as the standard methods rendering CRUD templates. We'd like to be able to annotate all our ajax methods with a single annotation so that regardless of what type of exception is thrown, we can return a response the ui can handle.
I've been looking at the ControllerAdvice and ExceptionHandler annotations, but I don't think either can be used the way we intend. ControllerAdvice can only cover entire controllers, so any exception handling would also cover the non-ajax methods. Similarly, the ExceptionHandler annotation would handle exceptions from both types of methods.
The current idea is to split the two types of methods, ajax and CRUD, into separate controllers. Is there another way to do this?
I don't think this is that much easy/straightforward in Spring (or at least to my current spring knowledge).
We'd like to be able to annotate all our ajax methods with a single
annotation
Apart from separating ajaxMethods into another controller, I can think of another way of doing this.
1.Declare your own exception class like myAjaxException.
2.Don't handle this exception in controller, just throw it when you encounter any exception inside your ajax methods.
try{
//....
}catch(Exception ex){
throw new myAjaxException("this is myAjaxException blah blah blah");
}
3.Intercept myAjaxException by implementing HandlerExceptionResolver as below.
Any Spring bean declared in the DispatcherServlet’s application
context that implements HandlerExceptionResolver will be used to
intercept and process any exception raised in the MVC system and not
handled by a Controller.
public class AjaxExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
if (ex instanceof myAjaxException) {
//return response to UI
}
}

ExceptionMapper for WebApplicationExceptions thrown with entity?

In our (legacy) codebase, we're throwing WebApplicationExceptions in different ways.
In an attempt to make some order in how we're handling exceptions - I wanted to create an ExceptionMapper for these WAEs (and others).
I realized, however, that Jersey's ExceptionMapper only maps WAE which weren't thrown with an entity.
For example:
throw new WebApplicationException(Response.status(500).build());
This exception is caught by the ExceptionMapper.
throw new WebApplicationException(Response.status(500).entity(WsResourceUtils.createWSResourceRestError(500, "bla")).build());
This exception is NOT caught by the ExceptionMapper.
Both are thrown from the same point in code.
This is my ExceptionMapper:
#Provider
public class GeneralExceptionMapper implements ExceptionMapper<Throwable> {
private static final Logger logger = LoggerFactory.getLogger(GeneralExceptionMapper.class);
#Override
public Response toResponse(Throwable e) {
logger.error("Caught a WAE", e);
...
}
Is it possible to create an ExceptionMapper which will catch WebApplicationExceptions even if their response is already built with an entity?
We're using Jersey 1.17.
Thanks.
This behaviour is describe in the JAX-RS specification and it's still true in version 3.0 :
Instances of WebApplicationException and its subclasses MUST be mapped to a response as follows. If the response property of the exception does not contain an entity and an exception mapping provider (see Exception Mapping Providers) is available for WebApplicationException or the corresponding subclass, an implementation MUST use the provider to create a new Response instance, otherwise the response property is used directly. The resulting Response instance is then processed according to Return Type.
Source : https://jakarta.ee/specifications/restful-ws/3.0/jakarta-restful-ws-spec-3.0.html#method_exc
I don't know why and I think it's not ideal but it's definitely not a bug.
please consider looking at the code in GIST :
https://gist.github.com/jeorfevre/9fb2c447a01bcc724998
Register a Mapper that of MagicException that contains Response
Define Exception that contain response
Throw this exception

Global Exception Handling in Jersey

Is there a way to have global exception handling in Jersey? Instead of individual resources having try/catch blocks and then calling some method that then sanitizes all of the exceptions to be sent back to the client, I was hoping there was a way to put this where the resources are actually called. Is this even possible? If so, how?
Instead of, where sanitize(e) would throw some sort of Jersey-configured exception to the Jersey servlet:
#GET
public Object getStuff() {
try {
doStuff();
} catch (Exception e) {
ExceptionHandler.sanitize(e);
}
}
Having:
#GET
public Object getStuff() throws Exception {
doStuff();
}
where the exception would get thrown to something that I can intercept and call sanitize(e) from there.
This is really just to simplify all the Jersey resources and to guarantee that the exceptions going back to the client are always in some sort of understandable form.
Yes. JAX-RS has a concept of ExceptionMappers. You can create your own ExceptionMapper interface to map any exception to a response. For more info see: https://jersey.github.io/documentation/latest/representations.html#d0e6352
javax.ws.rs.ext.ExceptionMapper is your friend.
Source: https://jersey.java.net/documentation/latest/representations.html#d0e6665
Example:
#Provider
public class EntityNotFoundMapper implements ExceptionMapper<javax.persistence.EntityNotFoundException> {
public Response toResponse(javax.persistence.EntityNotFoundException ex) {
return Response.status(404).
entity(ex.getMessage()).
type("text/plain").
build();
}
}
All the answers above are still valid. But with latest versions of spring Boot consider one of below approaches.
Approach 1 :
#ExceptionHandler- Annotate a method in a controller with this annotation.
Drawback of this approach is we need to write a method with this annotation in each controller.
We can work around this solution by extending all controllers with base controller (that base controller can have a method annotated with #ExceptionHandler. But it may not be possible all the times.
Approach 2 :
Annotating a class with #ControllerAdvice and define methods with #ExceptionHandler
This is similar to Controller based exception (refer approach 1) but this is used when controller class is not handling the exception.
This approach is good for global handling of exceptions in Rest Api

What's the simplest way to change from exception logging to exception handling in a Spring MVC app?

My Spring MVC app is full of methods that look like this:
#RequestMapping(value = "/foo", method = RequestMethod.GET)
public final void foo(HttpServletRequest request, ModelMap modelMap){
try{
this.fooService.foo();
}
catch (Exception e){
log.warn(e.getMessage(), e);
}
}
Exceptions are caught and logged but not handled otherwise.
The fooService called above does the same thing, never throwing exceptions up to the controller but catching and logging them. So, actually this controller exception code will never get invoked.
What's the best and simplest approach to implement proper exception handling in my app?
Get rid of all catch statements if all they do is logging carelessly. catch is meant to handle the error, not hide it.
Once all these catches are removed, install one global exception resolver in Spring MVC (1, 2, 3, ...) Simply implement this trivial interface:
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
In your exception resolver you might simply log the exception once and let it go as unprocessed (return null), so that error mappings in web.xml will forward request to proper error page. Or you can handle exception yourself and render some error page. AFAIK in simplest case there is no need for register exception resolver, just define it as a Spring bean/annotate with #Service.
Remember, catch the exception only when you know what to do with. Logging is only for troubleshooting, it doesn't handle anything.
BTW this:
log.warn(e.getMessage(), e);
is not only a very poor exception handling, but it is also slightly incorrect. If your exception does not have a message, you will see mysterious null just before the stack trace. If it does, the message will appear twice (tested with Logback):
22:51:23.985 WARN [main][Foo] OMG! - this is the exception message
java.lang.IllegalStateException: OMG! - this is the exception message
at Foo.bar(Foo.java:20) ~[test-classes/:na]
...sometimes undesirable, especially when exception message is very long.
UPDATE: When writing your own exception logger consider implementing both org.springframework.web.servlet.HandlerExceptionResolver and org.springframework.core.Ordered. The getOrder() should return something small (like 0) so that your handler takes precedence over built-in handlers.
It just happened to me that org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver running prior to my handler returned HTTP 500 without logging the exception.

#ExceptionHandler doesn't handle the thrown exceptions

I have a method in my controller which will handle the exceptions thrown by the application. So I have a method like this one.
#Controller
public class ExceptionController {
#RequestMapping(value="/error")
#ExceptionHandler(value={Exception.class, NullPointerException.class})
public String showError(Exception e, Model model){
return "tiles:error";
}
}
And to try I if it works I throw a NullPointerException in another method in other method controller:
boolean a = true;
if(a){
throw new NullPointerException();
}
After the exception is thrown it is printed in the JSP, but it doesn't go throw my showError() method (I've set a breakpoint there and it never enters). showError() method will catch the exception and will show different error pages depending on the exception type (though now it always shows the same error page). If I go to the url /error it shows the error page so the showError() method is OK.
I'm using Spring 3.
What can be the problem?
Thanks.
If you look at your logs, you'll probably see this:
java.lang.IllegalStateException:
Unsupported argument
[org.springframework.ui.Model] for
#ExceptionHandler method
In other words, #ExceptionHandler methods are not permitted to declare a Model parameter (see docs).
Remove that parameter (which you're not using anyway), and it should work as expected.
if you want to handle exceptions globally (outside your controller), then you can use a #ControllerAdvice annotated class in which you put your #ExceptionHandler annotated methods.
see http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc for an example.
And http://blog.codeleak.pl/2013/11/controlleradvice-improvements-in-spring.html for the improvements being made in spring 4.

Categories

Resources