Intercept exceptions thrown by repository method with different error codes - java

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);
}
}

Related

ResponseEntityExceptionHandler in SpringBoot 2.0.4.RELEASE

I have a basic SpringBoot 2.0.4.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
I have created this class to manage the exceptions
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public RestResponseEntityExceptionHandler() {
super();
}
// API
// 400
...
}
But when I try to use it in 1 of my method :
#GetMapping(path = "/getUsers", consumes = "application/json", produces = "application/json")
#ExceptionHandler({RestResponseEntityExceptionHandler.class })
public ResponseEntity<List<User>> testErrors(HttpServletRequest request, #RequestHeader(value = "Authorization") String authHeader) {
...
}
I got this compilation error :
Type mismatch: cannot convert from Class to Class>
If you read the docs for #ExceptionHandler you will see that it is used to mark a method to handle exception(s). Therefore you have to specify which exception(s) it can handle (e.g. #ExceptionHandler(MyException.class) or #ExceptionHandler({MyException.class, MyOtherException.class})). When such an exception occurs in your controller this method gets invoked.
#ControllerAdvice makes the methods defined in the class available to all your controllers.
I don't know what you want to do, but since you extend ResponseEntityExceptionHandler in the #ControllerAdvice class, you could just delete the #ExceptionHandler annotation:
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
...
}
If you want to handle specific exceptions on your own, you can do it like this:
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleMyException() {
...
}
}
#Controller
public class MyController {
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
throw new MyException();
}
}
Now handleException() will be called when an error occurs in testErrors().
If you want to handle exceptions only in one controller you can do this:
#Controller
public class MyController {
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
throw new MyException();
}
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleMyException() {
...
}
}
It's expected for this not to work correctly.
You're attempting to pass in the RestResponseEntityExceptionHandler as the arguments of the #ExceptionHandler annotation in your test method. This is wrong, as this annotation accepts the type of the exception that gets intercepted.
Also in general it seems that the placement of your #ExceptionHandler annotation seems to be wrong. This are placed within method that reside in the actual exception handling class.
I suggest you have a good read on the way Spring handles exceptions in that manner.
Have a look at this piece of documentation: exception handling in Spring.

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.

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

Is it good practice to throw exception from DAO layer to controller?

I am writing one REST api. There might be two exceptions in my DAO layer namely Exception_X and Exception_Y. If I encountered a exception Exception_X in DAO layer, my controller should return status code 200, if Exception_Y then 401 and if all goes well controller should return 201.
Now what was I thinking that I will throw encountered exception as it is from DAO layer to controller via service layer and in catch block of controller I will return response.
Is it acceptable or there is some other standard way?
Yes that is quite an acceptable way. However, rather than using try-catch, I would suggest to implement Exception Handlers for your REST Controllers. That way, you won't have to clutter your REST methods.
Also, it would be better to create a model object in REST layer for Error messages - ErrorResponse, with appropriate information:
class ErrorResponse {
int statusCode;
String errorMessage;
}
And return it's object from the exception handlers. BTW, you can also map your exception class directly to a response using #ResponseStatus annotation:
#ResponseStatus(value=401, reason="message")
class Exception_Y extends RuntimeException {
}
Then you don't have to write exception handler for that exception.
My suggestion would be wrap any unchecked exceptions with a service layer for loose coupling, and clean abstraction. Keep your controller free from conditions and let Service layer take care of this pain.
Keeping security concern in mind if you exposing it externally wrap your exception with service oriented exception it also helps to achieve generic layer specific exceptions say PersistentException, ServiceException etc. keeping good degree of decoupling in mind.
For handling exception globally you can use spring inbuild ControllerAdvice annotation with JsonExceptionModel for formatted error response.
#ControllerAdvice
public class GlobalExceptionHandler {
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(SQLException.class)
public Map<String, Object> handleSQLException(HttpServletRequest request, Exception ex) {
//json response here
}
}
public class JsonExceptionModel {
private int code;
private String type;
private String url;
private String message;
private String moreInfo;
// getters/setters here
}
I suggest you to go with Exception Resolver which is providing by spring.
Spring Framework provides HandlerExceptionResolver interface that we can implement to create global exception handler. We can also override it to create our own global handler with our application specific changes, such as logging of exception messages.
Here is the sample implementation of HandlerExceptionResolver,which will fullfill your need
public class RestResponseStatusExceptionResolver extends HandlerExceptionResolver {
#Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
if (ex instanceof InvalidInputException) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return handleException(ex);
} else if (ex instanceof ResourceNotFoundException) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return handleException(ex);
}
//Adding error details to modelView object
modelAndView.addObject("errors", ErrorDetails);
// Custom error message details
public class ErrorDetails {
private String code;
private List<String> data;
}

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.

Categories