Custom Exception is not handled with #ExceptionHandler annotated method - java

I have a custom exception class defined as
public class CustomAuthenticationException extends RuntimeException{
}
From a controller method I am throwing this exception as below
#RequestMapping(value="/tasks", method=RequestMethod.GET)
public String loadTasks(HttpServletRequest request){
try
{
if (!isAuthenticatedRequest(request))
{
throw new CustomAuthenticationException();
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
return "tasks/tasks";
}
To catch this exception from that controller's scope, I have defined a method with #ExceptionHandler annotation as below
#ExceptionHandler(CustomAuthenticationException.class)
public void handleCustomException(CustomAuthenticationException ex){
//method is not getting invoked
}
When I initiate the GET request and expect the exception to be handled by the aformentioned method, I get only the error stack trace in the console. The exception handler method is never invoked.
But, with other defined exceptions like MethodArgumentNotValidException, the handler methods are invoked correctly. For example I got the following exception handler working:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ErrorResponseHolder handleFieldValidationError(MethodArgumentNotValidException ex, HttpServletResponse response){
//method got invoked. do something with the exception
}
How can I resolve the issue with the custom exceptions ?

Handling of the exception is not proper, in a method loadTasks you are throwing the exception and catching the exception hence it is not propagating.
If you wanted to handle unhanded exceptions, for example generic exceptions (Exception.class) you need to write a method that should handle all these exception in common exception handling class.
#ExceptionHandler(Exception.class)
public void handleUnhandledException(Exception ex){
// handle the exception here
}
Once you are throwing the exception never ever catch the exception in same method, catch the exception where you are sending the proper response.

Related

How do I get the Exception class that was thrown in my spring error controller

I have my Spring error controller, and I need to get the actual exception class so I can print stack trace and other things like that.
This is my error controller
#Controller
public class ErrorController implements org.springframework.boot.web.servlet.error.ErrorController {
#RequestMapping("/error")
public String handleError() {
return "somethingwentwrong";
}
#Override
public String getErrorPath() {
return null;
}
}
I know its not much, but I need the exception object to be able to do some extra handling.
If you wish to execute different code based on the TYPE of the exception thrown, you should look at using #ControllerAdvice along with #ExceptionHandler. Any exceptions that you do not handle with an #ExceptionHandler will then bubble up to the default ErrorController (though you could handle Exception in a handler and then all exceptions will be handled via your custom handler). Something like:
#ControllerAdvice
#RestController
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
protected ResponseEntity<String> handleMyException(MyException ex) {
// Your code here
}
#ExceptionHandler(Exception.class)
protected ResponseEntity<String> handleException(Exception ex){
// Your code here
}
}
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler
No worries guys, I solved the problem, I just had to do some digging into springs built in error controller to get the stacktrace.

ControllerAdvice, ExceptionHandler and try catch block in RestController

I have generic question about #ControllerAdvice and #ExceptionHandler. I have a rest controller annotated #RestController that has 2 apis. If argument validation fails, it throws MethodArgumentNotValidException. I created ExceptionHandler to handle this:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResponseEntity<String> handleException(MethodArgumentNotValidException e) throws Exception {
return new ResponseEntity<>(e.getBindingResult().getGlobalError().getDefaultMessage(), HttpStatus.BAD_REQUEST);
}
}
If I want to log something when this exception happens, can I just add line of code before return statement like:
LOG.info("something happened");
Will it log it and then return BAD_REQUEST back to the caller?
If I want to log something when this exception happens, can I just add line of code before return statement like:
LOG.info("something happened");
Will it log it and then return BAD_REQUEST back to the caller?
Yes. That's the purpose of using #ExceptionHandlers. They help to reduce the code to handle exceptions across several rest endpoints defined in your project. This also serves as the single point to log exceptions, thus avoiding this anti pattern:
//BAD
class SomeService {
public SomeEntity someMethod() {
try {
/* stuff... */
} catch (Exception e) {
//No need to log the exception here
log.error("An exception happened", e);
throw e;
}
}
}
Still, you can have some benefits like wrapping the exceptions and rethrow them:
//GOOD
class SomeService {
public SomeEntity someMethod(String param) {
try {
/* stuff... */
} catch (Exception e) {
//You may use this to perform other logic like setting specific message or wrap your exception
log.error("Unexpected behaviour with param {}", param);
throw new MyCustomException("Some message", e);
}
}
}
You can think of the #ExceptionHandler as a giant catch block for all your rest endpoints and a specific type of exception.
Besides, your GlobalExceptionHandler class becomes the component with logic associated to handle every exception thrown in backend and handles how to report that to client side.

Calling a custom ExceptionMapper in JAX-RS

I've created a custom ExceptionMapper that I wan' to call every time an exception occurs in the API to map it to a suitable response. The following is my custom exception class:
#Provider
public class ServiceExceptionMapper implements ExceptionMapper<Throwable> {
private Logger logging = LoggerFactory.getLogger(getClass());
#Override
public Response toResponse(Throwable throwable) {
log.error("There is an exception: ", throwable);
if (throwable instanceof IllegalArgumentException) {
return Response.status(Response.Status.BAD_REQUEST).entity(throwable.getMessage()).type (MediaType.TEXT_PLAIN).build();
}
if (throwable instanceof WebApplicationException) {
WebApplicationException we = (WebApplicationException) throwable;
return we.getResponse();
}
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(throwable.getMessage()).type(MediaType.TEXT_PLAIN).build();
}
}
Now, in my resource class, I have a try and a catch block. If there is an exception, the catch block should catch it and invoke the custom exception mapper class. Usual way of throwing an exception is the following:
catch (Exception e) {
throw new WebApplicationException(Response.status(Status.INTERNAL_SERVER_ERROR).entity("Internal Server Error").build());
}
I'm trying to call the exception mapper class in the following way:
catch (Exception e) {
exceptionMapper.toResponse(e);
}
Where exceptionMapper is a field of the class ServiceExceptionMapper.
What are ExceptionMappers for?
But how can I call my custom exception mapper class to throw the same exception?
I want the custom exception mapper class to take care of throwing the exceptions that occur in the API. Therefore instead of directly throwing an exception (my second code snippet) I want to call the exception mapper class and want it to throw that exception.
The idea behind and ExceptionMapper is to map an Exception that has been thrown to a Response. Check the ExceptionMapper#toResponse(E) method signature and you will see it receives something that extends Throwable and must return a Response. Such method is invoked by the JAX-RS runtime.
If, for any reason, you don't want to throw exceptions directly in your resource method code, you may consider creating an utility class to do it and then you can invoke its methods to instantiate and throw the exceptions. And then the ExceptionMapper will map the exception that has been thrown to a HTTP response.
Looking up providers with JAX-RS
If you need to perform runtime look up of provider instances (ExceptionMappers are providers), you can use the Providers injectable interface. Use the #Context annotation to inject it in your resource classes:
#Context
private Providers providers;
Then you can get an ExceptionMapper for a particular class of exception:
ExceptionMapper<Throwable> exceptionMapper = providers.getExceptionMapper(Throwable.class);
Note 1: For other types that can be injected with the #Context annotation, refer to this answer.
Note 2: The Providers interface API allows you to look up the following providers:
ContextResolver
ExceptionMapper
MessageBodyReader
MessageBodyWriter

#ExceptionHandler local vs global

I am going to introduce the global handler on my web application:
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(CustomRuntimeException.class)
public #ResponseBody ImmutableMap<?, String> handleNullResponseException(CustomRuntimeException e) {
return ImmutableMap.of(e.getClass(), e.getMessage());
}
}
But the issue is that legacy code contains a few controllers with local handlers like this:
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(Exception.class)
public #ResponseBody ExceptionDetails handleException(Exception e) {
return handleException(e);
}
And when controller throws CustomRuntimeException it handles by local one not global. In order to fix it I can add to each of these controllers local handlers similar to global. But as for me it is not a good one.
The question: Is it possible to redirect handling custom exceptions to the global handler?
You need to put more specific Exceptions in Local ExceptionHandler and more general Exceptions in Global ExceptionHandler. Similar to Java Exception handling. If you put the General one in Local, all Exceptions will end up there because it is the closest one and accepts any exception.
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(IOException.class)
public #ResponseBody ExceptionDetails handleIOException(IOException e) {
return handleException(e);
}
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(NullPointerException.class)
public #ResponseBody ExceptionDetails handleNPException(NullPointerException e) {
return handleException(e);
}
Spring exceptionHandler works kind of like the try and catch
When the controller has an exception and it has a local exception handler the request will be handled by the local exception handler. Now if it does not find local then we tries to look for global.
In your case local exceptionHandler handles all exception thus global exception handler not called.

How to catch the exception thrown from a Spring AOP Advice

I have my own exception "MyOwnException" and throw this exception from my service class
public void service() throws MyOwnException
{
// some code
}
Now I want to catch MyOwnException in an advice and rethrow a brand-new Exception
public class SimpleThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] args, Object target,
MyOwnException ex) throws Throwable {
throw new Exception("new Description",ex);
}
}
Now, how can I catch the re-thrown Exception from the above Advice SimpleThrowsAdvice?
You should use the Around advice to do that. See here Spring AOP AfterThrowing vs. Around Advice

Categories