Calling a custom ExceptionMapper in JAX-RS - java

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

Related

Intercept exceptions thrown by repository method with different error codes

Is there a way to intercept DataAccessException that is thrown by data-layer (#Repository) with knowing which method is causing this exception?
Writing custom SQLExceptionTranslator does not fit my need as I cannot determine which method caused the exception.
I have a repository like this:
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
#ErrorCode("E1000")
User findById(int id);
#ErrorCode("E1001")
User findByUsername(String username);
}
ErrorCode is a custom annotation holds an error code that I need to send to client whenever DataAccessException occurs.
If there is a way to intercept the call to findById with catching DataAccessException, then it is easy to extract error code from annotation and re-throw a custom exception that can be catched by exception handler.
If Spring AOP is allowed, you could build your own aspect, for example:
#Aspect
public class ErrorCodeAspect {
#Around("#annotation(errorCode)")
public Object aroundErrorCode(ProceedingJoinPoint joinPoint, ErrorCode errorCode) throws Throwable {
try {
return joinPoint.proceed();
} catch (DataAccessException dae) {
throw new YourCustomException(errorCode.value(), dae);
}
}
}
Note that annotations on interface methods are not inherited by implementing class methods (even with #Inherited which applies to parent classes only), so you will probably need to annotate your concrete service classes instead for the aspect to plug in (unless Spring does some additional black magic with the repository proxies I wouldn't be aware of).
You can define the custom ExceptionHandler.
#RestControllerAdvice
public class RestExceptionResolver {
#ExceptionHandler(DataAccessException.class)
public ResponseEntity<String> handleNoSuchElementException(DataAccessException ex) {
return ResponseEntity.status(yourErrorCode);
}
}

How can I set up my application to ONLY return messages for ResponseStatusException?

By default, Spring Boot does not return messages for any exceptions, including ResponseStatusException, meaning that the message about bar below will not be returned to the client:
#GetMapping("/foo")
#ResponseBody
public Foo getFoo(#RequestParam(name = "bar", defaultValue = "0") int bar) {
if (bar <= 0) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "bar must always be positive");
}
return example.getFoo(bar);
}
This can be changed by setting server.error.include-message=always in the application.properties, however this causes ALL exception messages to be returned to the client, including this one:
#GetMapping("/baz")
#ResponseBody
public Baz getBaz() {
if (!security.checkSecurity()) {
throw new RuntimeException("Security breach! Hope no one finds out!");
}
return example.getBaz();
}
I know this is a trivial example and the solution would be just "don't throw server exceptions from your controller", but the exception might actually come from some other code buried deep in the application, it could even be a NullPointerException or whatever.
How can I get the application to show messages only from ResponseStatusException and not other types of exception? (I guess other than adding try-catch clauses to every single controller method.)
You can add extra (#ExceptionHandler) methods to any controller to specifically handle exceptions thrown by request handling (#RequestMapping) methods in the same controller. Such methods can:
Handle exceptions without the #ResponseStatus annotation (typically predefined exceptions that you didn’t write)
Redirect the user to a dedicated error view
Build a totally custom error response
Controller advice allows you to use exactly the same exception handling techniques but apply them across the whole application, not just to an individual controller. You can think of them as an annotation-driven interceptor.
Any class annotated with #ControllerAdvice becomes a controller-advice and three types of method are supported:
Exception handling methods annotated with #ExceptionHandler.
Model enhancement methods (for adding additional data to the model) annotated with #ModelAttribute. Note that these attributes are not available to the exception handling views.
Binder initialization methods (used for configuring form-handling) annotated with
#InitBinder.
Solution:
#ControllerAdvice
public class RestControllerAdvice {
#ExceptionHandler(ResponseStatusException.class)
public ResponseEntity<String> handleStatusException(ResponseStatusException exception) {
throw exception;
}
#ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception exception) {
return new ResponseEntity<>("Exception", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Catch all the exceptions and in the catch block throw ResponseStatusException like in:
#GetMapping("/actor/{id}")
public String getActorName(#PathVariable("id") int id) {
try {
return actorService.getActor(id);
} catch (ActorNotFoundException ex) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Actor Not Found", ex);
}
}

Handling Exception in Spring boot Application with Hibernate

I am building a REST API with Spring boot and DAO layer is implemented in Hibernate.I need to understand the correct way of throwing and handling Exception in the Application.Currently I am doing it in this way
#Repository
public class UserDaoImpl
{
public getAllUsers() throws Exception
{
//get All Users from DB
}
}
#Service
public class UserServiceImpl
{
public getAllUsers throws MyCustomException
{ try{
userDaoImpl.getAllUsers();
}
catch(Exception e)
{
throw MyCustomException();
}
}
}
and In Exception Mapper
#ControllerAdvice
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({MyCustomException.class})
#ResponseBody
public ResponseEntity<?> handleCustomException(Exception e) {
log.error("", e);
Map<String, String> error = new HashMap<String, String>();
error.put("message", e.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_ACCEPTABLE, MessageResource.getLogMessage("BAD_REQUEST_EXCEPTION"));
}
}
public class MyCustomException extends RuntimeException
{
///// ....
}
So I have added throws clause (throws Exception) in DAO layer and catch at service layer and wrap it in Custom Exception(unchecked exception) and do not propogate the exception at controller layer.
Is this correct ? or there is some better way?
I'd recommend you to have general #ExceptionHandler({Exception.class}) for all cases that you don't want to handle specifically.
Also it's okay to create separate exception classes for situations that require custom handling.
It depends on what do you want to achieve.
About your case. Exception in DAO layer does not necessary mean that request was wrong or did not provide correct parameters. It could be mapping problems, DB access problems and etc. So I would not wrap it to my custom exception, or at lest wrap in to general DataAccessException, make good logging around that and return some general error code to the client.

is it possible to handle exception from a single place

I have dao, service and action classes in my spring mvc application.
I am throwing Exception in Dao and Service classes. Now in Action, normally I have to write try catch block and in case exception occurs in dao and service, it will be thrown from there and it will go in catch block in action.
I have a error jsp which will be displayed.
Problem is I need to write same catch block in all action methods.
Is it possible to throw it again in action methods too and handle it from a single point rather than writing same code everywhere.
Please suggest.
You can also have a look at Spring Integration. It provides the use of gateways, filters and channels. Each can have a Request, Response and Error channel assigned. Or there is even a default error handler. In case all data flows through a specific channel, having a custom error handler is as simple as follows:
#MessageEndpoint
public class MyErrorHandler {
#ServiceActivator(inputChannel = "errorChannel")
public String handle(String messsage) {
// do whatever you like
}
}
The Integration framework offers lots of usefull stuff for general handling.
I think you are looking for cross-cutting exception handling and good news, you are working with Spring MVC yes you can use this feature.
All you need to do, is throw your CustomExcptions or whatever other Exceptions that are from your services to your action methods.
Let's say here is your service:
#Service
public class MyService {
public void someMethod throws RuntimeException {
...
}
}
In your controller method:
#Controller
public class MyController {
#Autowired
MyService service;
#RequestMapping("/someuri"){
try {
service.someMethod();
} catch {
throw new RuntimeException();
}
}
#ExceptionHandler(RuntimeException.class)
public ModelAndView handleException(RuntimeException ex) {
ModelAndView model = new ModelAndView("errorpage");
return model;
}
}
The handleException method annotated with ExceptionHandler is your advice method for exception handling and it will be called anytime a RuntimeException is throw inside your controller and you can keep up like this for all other exceptions.

#ExceptionHandler not getting called once exception is thrown from REST layer

I'm developing a REST service using Spring MVC. I'm trying to implement exception handling using #ExceptionHandler . When exception is thrown from REST layer, it's not been intercepted by #ExceptionHandler. Am i missing anything ?
#Service
#Path("/customer")
public class CustomerResource extends BaseResource{
#Autowired
private CustomerDao customerDao;
........
#GET
#Path("/customer/{accountNumber}")
public Response findCustomerByAccountNumber(String accountNumber) throw Exception{
Customer customer=null;
customer=customerDao.find(....);
if(customer==null)
throw new ResourceNotFoundException();
else
..........
}
}
Base class which has Exception handler method
public abstract class BaseResource {
.......
#ExceptionHandler({ResourceNotFoundException.class })
public Response handleException(Exception ex) {
ErrorResource errResource = new ErrorResource();
.....
return Response.status(Response.Status.NOT_FOUND).entity(errResource).build();
}
}
You are throwing ResourceNotFound but have specified ResourceNotFoundException in the exception handler - these seem to be different exceptions. Either throw ResourceNotFoundException instead of ResourceNotFound or add ResourceNotFound to the exception handler.
EDIT:
Don't know how I missed it at first: just noticed you don't actually use Spring MVC controller. Spring MVC exception handlers only work for requests handled by Spring MVC controllers. They handle exceptions that happen in the body of controller handler methods. You seem to use something else to handle REST requests.

Categories