Why is java ws rs ExceptionMapper is picking non application exceptions also? - java

I have a web application in which I throw some custom exceptions(application exceptions annotated with #ApplicationException) and there is an exception mapper(provider annotated with #Provider) for each. Recently I forgot to annotate an exception with #ApplicationException and still the mapper is able to identify the exception and format the response properly.
Then I checked the documentation and I understood that the annotation will be inherited by its child class by default. So I removed the annotation from the super class and checked. The mapper still identified the exception and formatted the response.
Then I went even forward and tried throwing java.lang.IllegalArgumentException and wrote a mapper class for it. It also worked properly. Is javax.ws.rs.ext.ExceptionMapper independent of the exception being thrown. Will it not check if whether thrown exception is really annotated with #ApplicationException?
#Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<java.lang.IllegalArgumentException> {
#Override
public Response toResponse(java.lang.IllegalArgumentException exception) {
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Somewhere in my service class:
throw new java.lang.IllegalArgumentException("Problem with the payload. Please check the payload you are sending");

The answer is no, it will not check if whether thrown exception is really annotated with #ApplicationException.
The exception mapper is independent of the #ApplicationException.
All the exception mapper knows is, if there's no exception caught until the almost last layer, it will be processed here, if it find a matching provider.
You can also actually create a provider for RuntimeException and all exception happened in the REST request will land here (but this is not good practice, we should always throw custom exception and catch them with the provider and convert them to good error response).
When you annotate the exception with #ApplicationException you can control things like whether the transaction should be rollback, and whether it will be wrapped by EJBException etc etc.

Related

How to Throw a Custom Exception When Bean Validation In Spring Boot with Hibernate Validator Fails?

I'm using Spring Boot 2.0.1 with inclusion of validation-starter (which comes with Hibernate Validation 6.x) and trying to validate a method parameter and return objects in my Spring repository layer. There are basically a ParamObj and ReturnObj for this method,
#Repository
#Validated // Spring integration of validation
public class MyRepoImpl implements MyRepo {
#Override
#Valid
public ReturnObj myMethod(#Valid ParamObj param) throw CustomException {
try {
// makes a call that might throw an exception
} catch (Exception e) {
throw new CustomException(/*...*/, e);
}
return returnObj;
}
}
ParamObj and ReturnObj both use a simple set of Bean Validation annotations on some of their properties, e.g #Pattern, #Min, etc.. My problem is that with these annotations, if either ParamObj or ReturnObj fails the validation process, MyRepoImpl#myMethod() would throw javax.validation.ConstraintViolationException.
However, as you can see, the contract for myMethod() mandates to throw a CustomException. Yes, ConstraintViolationException is a RuntimeException, however, now the caller of MyRepoImpl#myMethod(), e.g. a service layer logic, needs to catch this RuntimeException in addition to CustomException.
And the reason is, I need to prevent all the exceptions from bubbling out of the service layer (for this particular Spring #Service I have which calls this repository) -- this service is part of a long list of services that are called via a service orchestration which constructs a larger encompassing object. In other word, this piece (result coming obtained via MyRepoImpl) is considered optional, and subsequently, its failure should only be logged but do not stop the whole process.
For that reason, I can't utilize ControllerAdvice to capture ConstraintViolationException for the entire application.
My goal is to be able, when the validation fails for either method parameter or return, to throw only CustomException back to the caller.
Is this possible with annotations only? I looked into MethodValidationPostProcessor but can't figure out if I can implement my own to accomplish this goal.
P.S. The only way I can think to achieve this is to forgo #Valid/#Validated and call Validator#validate(obj, ...) programmatically for the parameter and return value inside the try-catch block, catch the constraint exception, and throw my CustomException(). However, I want to know if I can avoid that and leave this hard coded validation out of the repository.
Since I was not here a year ago I would not have answered but since this is a common question as I can see through my experience with fellow students, I think the answer might be #ControllerAdvice, comes handy with any type of exceptions
doc --> https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html

JAX-RS exception mapper: What about wrapped exceptions and default case?

A typical way to deal with exceptions in REST service is to define custom exception types (typically from RuntimeException) and then implement a mapper class to produce HTTP codes, for example:
public class MyExceptionMapper implements ExceptionMapper<MyException> {
#Override
public Response toResponse(MyExceptionex) {
return Response.status(400).entity("bad request")
.type(MediaType.APPLICATION_JSON).build();
}
}
Now, I have 2 questions:
How would I implement a mapper for the "default case", meaning for every exception not mapped here or in another mapper? When for example implementing one for Throwable in order to produce an HTTP 500, wouldn't it catch my own exceptions again? Or can an order be defined in which mappers work?
When calling managed components like EJBs from REST service, wouldn't an exception thrown there result in an EJBException or some Transaction...Exception wrapping my own one?
mapper for the "default case"...
Just as Paul Samsotha writes in the comment, the JAX-RS server runtime is supposed to pick the most specific exception mapper. Or to quote the JAX-RS specs (for JEE7/version 2.0):
3.3.4 Exceptions
[...]
If an exception mapping provider (see Section 4.4) is available for the exception or one of its superclasses, an implementation MUST use the provider whose generic type is the nearest superclass of the exception to create a Response instance that is then processed according to Section 3.3.3.[...]
So I guess you can use an exception mapper for Throwable - its signature verifies it anyway:
public interface ExceptionMapper<E extends Throwable> {...}
When calling managed components like EJBs from REST service...
The EJB container will wrap the exception, if it needs to be wrapped. Not all exceptions thrown by EJBs are required to be wrapped. The EJB spec (v3.1) makes the distinction between application exceptions (annotated with javax.ejb.ApplicationException) and "all other exceptions" (see section 14.3).
So make the exception #ApplicationException and provide a mapper for it. But if you still want to respond based on the wrapped exception:
Respond based on the wrapped exception
You cannot directly select a mapper based on the wrapped exception. But you can create an exception mapper for the wrapper exception that unwraps it and selects an appropriate mapper for the wrapped exception based on the Providers context (see JAX-RS 2.0 section 9.2.6 and the javax.ws.rs.ext.Providers Javadocs). An example, untested code for a hypothetical MyWrapperException would be:
#Provider
public class MyWrapperExceptionMapper implements ExceptionMapper<MyWrapperException> {
#Context
private Providers providers;
public Response toResponse(MyWrapperException e) {
Throwable t = e.getCause();
ExceptionMapper mapper = providers.getExceptionMapper(t.getClass());
if( mapper != null ) {
return mapper.toResponse(t);
}
else {
// custom handling...
}
}
}

#ExceptionHandler for Error gets called only if there's no mapping for Exception

Using spring-web-4.2.6, I have the following Controller and ExceptionHandler:
#ControllerAdvice
public class ExceptionsHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDTO> HandleDefaultException(Exception ex) {
...
}
#ExceptionHandler(InternalError.class)
public ResponseEntity<ErrorDTO> HandleInternalError(InternalError ex) {
...
}
}
#RestController
#RequestMapping("/myController")
public class MyController {
#RequestMapping(value = "/myAction", method = RequestMethod.POST)
public boolean myAction() {
throw new InternalError("");
}
}
For some reason, the ExceptionsHandler's HandleDefaultException (for Exception.class) method is invoked, with an exception of type NestedServletException, instead of the HandleInternalError call.
When removing the default call, the IntenalError call is called with the proper InternalError exception.
I do not want to remove the default call as it is important to me to have a default handler to allow for a better experience for my users.
What am I missing here?
EDIT:
Apparently I'm using spring-web-4.3.3, without asking for it. I don't understand why exactly, here's my Gradle dependencies tree: http://pastebin.com/h6KXSyp2
Spring MVC should only exhibit the behavior you describe with version 4.3 and above. See this JIRA issue. Previously, Spring MVC would not expose any Throwable values to #ExceptionHandler methods. See
ExceptionHandler doesn't work with Throwable
Since 4.3, Spring MVC will catch any Throwable thrown from your handler methods and wrap it in a NestedServletException, which it will then expose to the normal ExceptionHandlerExceptionResolver process.
Here's a short description of how it works:
Checks if the handler method's #Controller class contains any #ExceptionHandler methods.
If it does, tries to resolve one that can handle the Exception type (including NestedServletException). If it can, it uses that (there's some sorting if multiple matches are found). If it can't, and the Exception has a cause, it unwraps and tries again to find a handler for that. That cause might now be a Throwable (or any of its subtypes).
If it doesn't. It gets all the #ControllerAdvice classes and tries to find a handler for the Exception type (including NestedServletException) in those. If it can, it uses that. If it can't, and the Exception has a cause, it unwraps it and tries again with that Throwable type.
In your example, your MyController throws an InternalError. Since this is not a subclass of Exception, Spring MVC wraps it in an NestedServletException.
MyController doesn't have any #ExceptionHandler methods, so Spring MVC skips it. You have a #ControllerAdvice annotated class, ExceptionsHandler, so Spring MVC checks that. The #ExceptionHandler annotated HandleDefaultException method can handle Exception, so Spring MVC chooses it to handle the NestedServletException.
If you remove that HandleDefaultException, Spring MVC won't find something that can handle Exception. It will then attempt to unwrap the NestedServletException and check for its cause. It'll then find the HandleInternalError which can handle that InternalError.
This is not an easy issue to deal with. Here are some options:
Create an #ExceptionHandler that handles NestedServletException and do the check for InternalError yourself.
#ExceptionHandler(NestedServletException.class)
public ResponseEntity<String> HandleNested(NestedServletException ex) {
Throwable cause = ex.getCause();
if (cause instanceof InternalError) {
// deal with it
} else if (cause instanceof OtherError) {
// deal in some other way
}
}
This is fine unless there's a bunch of different Error or Throwable types you want to handle. (Note that you can rethrow these if you can't or don't know how to handle them. Spring MVC will default to some other behavior, likely returning a 500 error code.)
Alternatively, you can take advantage of the fact that Spring MVC first checks the #Controller (or #RestController) class for #ExceptionHandler methods first. Just move the #ExceptionHandler method for InternalError into the controller.
#RestController
#RequestMapping("/myController")
public class MyController {
#RequestMapping(value = "/myAction", method = RequestMethod.POST)
public boolean myAction() {
throw new InternalError("");
}
#ExceptionHandler(value = InternalError.class)
public ResponseEntity<String> HandleInternalError(InternalError ex) {
...
}
}
Now Spring will first attempt to find a handler for NestedServletException in MyController. It won't find any so it will unwrap NestedServletException and get an InternalError. It will try to find a handler for InternalError and find HandleInternalError.
This has the disadvantage that if multiple controllers' handler methods throw InternalError, you have to add an #ExceptionHandler to each. This might also be an advantage. Your handling logic will be closer to the thing that throws the error.

Make #ExceptionHandler handle every exception except AccessDeniedException?

I wrote an #ExceptionHandler to send the exception in JSON for REST requests. However, due to the way Spring Security works, AccessDeniedExceptions must not be handled.
If I simply rethrow an AccessDeniedException in the handler, I get an ERROR level logging saying:
Failed to invoke #ExceptionHandler method: SomeMethod() throws java.lang.Exception
org.springframework.security.access.AccessDeniedException: Access is denied
...Stack...
I cannot safely disable ERROR level logging for the class logging it (ExceptionHandlerExceptionResolver), but having these stacktrace is quite confusing for operations people.
Is there anyway to make #ExceptionHandler not handle this specific exception?
I think you could just use global exception handler instead, so you don't manually handle this AccessDeniedExceptions,
this global exception will be thrown for every kinds of exceptions (except for the exception handlers that you handle manually):
#ControllerAdvice
public class GlobalHandler {
#ExceptionHandler(Exception.class)
public ModelAndView handleException() {
return new ModelAndView("errorGlobal");
}
}

Exception Logging with Spring

I have heard that it is possible to log (or do something else) Exceptions with Spring in my web-App, so I don't have to manually insert in every "catch(){}" block the Log-function.
Does anyone have experience with Spring-overall-logging? I just want to get informed when an error appears
ExceptionHandler is the central point for handling unexpected Exceptions that are thrown during the Faces lifecycle. The ExceptionHandler must not be notified of any Exceptions that occur during application startup or shutdown.
See the specification prose document for the requirements for the default implementation. Exceptions may be passed to the ExceptionHandler in one of two ways:
1.)By ensuring that Exceptions are not caught, or are caught and re-thrown.
This approach allows the ExceptionHandler facility specified in section JSF.6.2 to operate on the Exception.
2.)By using the system event facility to publish an ExceptionQueuedEvent that wraps the Exception.
This approach requires manually publishing the ExceptionQueuedEvent, but allows more information about the Exception to be stored in the event. The following code is an example of how to do this.
Global Exception Handler – Exception Handling is a cross-cutting concern, it should be done for all the pointcuts in our application. We have already looked into Spring AOP and that’s why Spring provides #ControllerAdvice annotation that we can use with any class to define our global exception handler.
The handler methods in Global Controller Advice is same as Controller based exception handler methods and used when controller class is not able to handle the exception.
Sample Code
#ExceptionHandler(Exception.class)
public ModelAndView getExceptionPage(Exception e, HttpServletRequest request) {
request.setAttribute("errorMessageObject", e.toString());
return model;
}
** Here we can catch the base exception class Exception.class or any other exception class. Also we can throw and catch our own custom defines exception class.

Categories