How to get a 409 for ObjectOptimisticLockingFailureException with Spring Boot? - java

I know that Spring Boot will render a Thymeleaf template at error/409.html for an exception that leads to a statuscode of 409 CONFLICT by default. However, it seems that ObjectOptimisticLockingFailureException gives a 500 SERVER ERROR by default.
I am able to handle the exception and "manually" redirect to the error/409.html template using a #ControllerAdvice like this:
#ControllerAdvice
public class GlobalControllerAdvice {
#ResponseStatus(HttpStatus.CONFLICT)
#ExceptionHandler({DataIntegrityViolationException.class, ObjectOptimisticLockingFailureException.class})
public ModelAndView handleConflict(HttpServletRequest request, Exception e) {
ModelAndView result = new ModelAndView("error/409");
result.addObject("url", request.getRequestURL());
return result;
}
}
Since ObjectOptimisticLockingFailureException is not part of my own code, I cannot annotate it with #ResponseStatus(HttpStatus.CONFLICT) to have a 409.
Is it possible to map ObjectOptimisticLockingFailureException to 409 and use the default error template mechanism for error codes in Spring Boot?

We can try with ResponseEntity as below
#ExceptionHandler({DataIntegrityViolationException.class, ObjectOptimisticLockingFailureException.class})
public ResponseEntity<ModelAndView> handleConflict(HttpServletRequest request, Exception e) {
ModelAndView result = new ModelAndView("error/409");
result.addObject("url", request.getRequestURL());
return new ResponseEntity<>(result, HttpStatus.CONFLICT);
}

This might work
#Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver
createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r =
new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("ObjectOptimisticLockingFailureException", "error/409");
r.setExceptionMappings(mappings);
return r;
}

Could you catch the exception ObjectOptimisticLockingFailureException and re-throw an exception with 409?
try {
//perform your task here
} catch (ObjectOptimisticLockingFailureException e) {
var ex = new ClientErrorException(409, e);
throw ex;
}

Related

Java Spring exception handling

I have multiple controllers whose exceptions are handled in the ControllerAdvice.
All controllers use common exceptions types (like HttpClientException, DBException, etc.).
But there is one specific controller which exceptions should be handled differently.
In my current implementation all methods of this specific controller are wrapped with try-catch and throw CustomException in case of any exception. Then, in ControllerAdvice I process this type of exception.
However, I want to handle all exceptions in ControllerAdvice as well as get rid of CustomException and try-catch in controller methods.
Is there any way to find out the source controller name in the exception advice? I could check it and handle the exception differently.
Or maybe some other solution exist?
Inside of your controller advice, you can provide Handler for your custom exception as below.
#ControllerAdvice
public class CustomGlobalExceptionHandler {
#ExceptionHandler(CustomException.class)
public final ResponseEntity<ApiResponseDTO> manageException(CustomException ex) {
log.error("Error in CustomException: {}", ex.getMessage(), ex);
ApiResponseDTO error = ApiResponseDTO.builder()
.message(ex.getMessage())
.result(0)
.build();
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
#ExceptionHandler(CustomException1.class)
public final ResponseEntity<ApiResponseDTO> manageException1(CustomException1 ex) {
log.error("Error in CustomException1: {}", ex.getMessage(), ex);
ApiResponseDTO error = ApiResponseDTO.builder()
.message(ex.getMessage())
.result(0)
.build();
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
#ExceptionHandler(Exception.class)
public final ResponseEntity<ApiResponseDTO> manageException1(Exception ex) {
log.error("Error in Common Exception Handler: {}", ex.getMessage(), ex);
StackTraceElement[] ste = ex.getStackTrace();
String className=ste[ste.length - 1].getClassName();
System.out.println(className);
if(className.equalsIgnoreCase("com.a")){
System.out.println("Do A related stuff");
}else{
System.out.println("Do B related stuff");
}
ApiResponseDTO error = ApiResponseDTO.builder()
.message(ex.getMessage())
.result(0)
.build();
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
}
As mentioned in last block, you can get class name from where this exception thrown and utilizing that name to branching out your stuff.

ControllerAdvice conditionally handle exception

I have a controller advice that handles the exceptional behavior in my REST controller and I came across a situation when I have to conditionally process SQLIntegrityConstraintViolationException that have a certain message (the one for duplicate keys), returning a 409, letting the other ones be handled by the default handler (returning a 500 error code).
I am thinking of 2 possible ways to achieve that:
Throwing a new bare-boned Exception on the else branch on my condition, so the handling is done by Spring.
Explicitly calling the general exception handler (like return handleGeneralException(exception) from inside my else branch).
I there a "proper" way to pass on a fraction of exceptions of one kind in my ControllerAdvice to another handler than the "original" one?
EDIT 1:
I would like to do something like this in my ControllerAdvice:
if (exception.getMessage.contains("something")) {
// handle exception
} else {
// pass to other handler
}
Have a custom exception class and then when you throw the SQLIntegrityConstraintViolationException wrap it in your custom exception classs with additional fields whatever you want to be accessible in controller advice. Handle the custom exception in your controller advice class.
#ControllerAdvice
public class CustomExceptionHandler {
#ExceptionHandler(YourCustomException.class)
public final ResponseEntity<ExceptionResponse> handleNotFoundException(YourCustomExceptionex,
WebRequest request) {
ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(),
request.getDescription(false), HttpStatus.NOT_ACCEPTABLE.getReasonPhrase());
return new ResponseEntity<>(exceptionResponse, HttpStatus.CONFLICT);
}
}
While having the try catch block to handle this exception in your code , make sure that you handle DataIntegrityViolationException instead of SQLIntegrityConstraintViolationException if you are using Spring Data JPA. So , if you are using Spring Data Jpa then :
try {
anyRepository.save(new YourModel(..));
} catch (DataIntegrityViolationException e) {
System.out.println("history already exist");in res
throw New YourCustomException("additional msg if you need it ", e);
}
Below code will capture the error message of exception SQLIntegrityConstraintViolationException in ControllerAdbvice without having to handle in code
#ControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = DataIntegrityViolationException.class)
public ResponseEntity<ExceptionResponse> dataIntegrityViolationExceptionHandler(Exception ex) {
ExceptionResponse response = new ExceptionResponse();
Throwable throwable = ex.getCause();
while (throwable != null) {
if (throwable instanceof SQLIntegrityConstraintViolationException) {
String errorMessage = throwable.getMessage();
response.setErrors(new ArrayList<>(Arrays.asList(errorMessage)));
}
throwable = throwable.getCause();
}
return new ResponseEntity<Object>(response, HttpStatus.CONFLICT);
}
}

How to handle exception in spring with #ControllerAdvice even though we are handling exception in catch block

Not able to use #ControllerAdvice and #AfterThrowing when method surrounded by try and catch block.
I can explain step by step
Step 1: In my spring application all handlers(methods) are handle exception by try and catch block.
Step 2: So requirement i need to trigger a email when the exception occurs in all handlers methods. But my application having 100's of methods. So try with #ControllerAdvice to handle the exceptions by using #ExceptionHandler annotation. I know that it wont work because we already handling our exception in catch block. So it cant look at #ControllerAdvice.
Step 3: I try with Aop #AfterThrowing advice also. It is not working. So i cant able to remove all catch blocks in entire application code. It is very difficultly to do that.
But my question is that
Is there any way in spring to handle the exception even we are handling it.
We returning back status code like 400 . In Spring they any Advice to identify status code.
Because are retiring ResponseEntity Status back as response.
#RequestMapping(value = "/service/getDetails", method = RequestMethod.POST, produces = { "application/json" })
public ResponseEntity<Map<String, Object>> getDetails(#RequestBody Details details, HttpServletRequest request) {
ResponseEntity<Map<String, Object>> response = null;
try {
/// Some code
} catch (Exception e) {
logger.error("Exception while DetailsController: " + e.getMessage());
response = new ResponseEntity<>(HttpStatus.BAD_REQUEST);
/*
* int status=response.getStatusCodeValue();
* scheduledMailTrigerService.sendErrorLogInfo(request,e,status);
*/
}
return response;
}
#ControllerAdvice
public class AppExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { Exception.class })
public String handleAnyException(Exception ex, WebRequest request) {
System.out.println("Done");
return "Done";
}
}
You can use an #Around advice to capture the result of the method execution... my suggestion is that you remove the try catch from all your endpoints, just write a try catch wrapping the ProceedingJoinPoint proceed() execution. This way you can return a ResponseEntity 400 when an exception occurs, alongside execututing email sending logic.

Spring MVC : the best way to handle exception for Ajax Request and normal request?

I want to define a common exception manger in my project, so I use #ControllerAdvice to do, the snippet of code is below:
#ExceptionHandler(Exception.class)
public ModelAndView handleAllException(HttpServletRequest request, Exception ex) throws Exception
{
LOGGER.error(ex.getMessage());
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", request.getRequestURL());
mav.setViewName(ViewConstants.INTERNAL_ERROR_VIEW);
return mav;
}
it will return a common error page. That's great for normal exception of request. But if this is a Ajax request, the result is so ugly. so I add the code to judge it. The added code is below:
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
// return HTTP Status code and response message
} else {
// return error page name
}
I don't think it is the best way, anybody have a good opinion?
I have all my controllers in different packages based on whether they serve AJAX requests or not. Then I can set #basePackages element on the ControllerAdvice annotations to handle the exception accordingly
Update:
See RequestMapping#params and RequestMapping#headers to separate controllers based on headers and/or params
I would suggest to set error response code on any request, think this is a good practice to notify client that something goes wrong not depending on type of request. And for ajax request you can return same page and identify problem by error code.
If you use jQuery for making requests, you could use the following:
jQuery.ajaxSetup({
headers: { 'ajax-request': true },
statusCode: {
400: function (xhr) {
...do something
},
500: function (xhr) {
...do something
}
...
}
});
...
public class Application extends SpringBootServletInitializer {
#Bean(name = "simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
r.setDefaultErrorView("forward:/errorController");
return r;
}
#Controller
public class ErrorController {
public static final Logger LOG = Logger.getLogger(ErrorController.class);
#RequestMapping(value = "/errorController")
public ModelAndView handleError(HttpServletRequest request,
#RequestAttribute("exception") Throwable th) {
ModelAndView mv = null;
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
if (isBusinessException(th)) {
mv = new ModelAndView("appAjaxBadRequest");
mv.setStatus(BAD_REQUEST);
} else {
LOG.error("Internal server error while processing AJAX call.", th);
mv = new ModelAndView("appAjaxInternalServerError");
mv.setStatus(INTERNAL_SERVER_ERROR);
}
mv.addObject("message", getUserFriendlyErrorMessage(th).replaceAll("\r?\n", "<br/>"));
} else {
LOG.error("Cannot process http request.", th);
mv = new ModelAndView("appErrorPage");
mv.addObject("exeption", th);
}
return mv;
}
}

Why error is not getting caught, even when explicitly thrown?

I want to catch an "Error" in SpringMVC3 using annotated "#ExceptionHandler". I can catch throwable and any exception, but when I tried with "Error" it is not catching the exception. Any idea why? The code below demonstrates the problem.
#Controller
#RequestMapping("/test")
public class TestExceptionController {
static final Logger logger = Logger.getLogger(TestExceptionController.class);
#RequestMapping(method = RequestMethod.GET)
public String processData(int intValue) throws InvalidDataException {
if (intValue < 6) {
try {
throw new Exception();
} catch (Exception e) {
throw new InvalidDataException();
}
}
return "test";
}
#ExceptionHandler(InvalidDataException.class)
public ModelMap handleException(InvalidDataException ex) {
logger.debug("exception catched :" + ex);
return new ModelMap();
}
}
The above code catches, but below code is not catching. Why is it not catching the error?
#Controller
#RequestMapping("/test")
public class TestExceptionController {
static final Logger logger = Logger.getLogger(TestExceptionController.class);
#RequestMapping(method = RequestMethod.GET)
public String processData(int intValue) throws Error{
if (intValue < 6) {
try {
throw new Exception();
} catch (Exception e) {
throw new Error();
}
}
return "test";
}
#ExceptionHandler(Error.class)
public ModelMap handleException(Error ex) {
logger.debug("exception catched :" + ex);
return new ModelMap();
}
}
Actually I looked into the source of spring DispatcherServlet and in line 809 it explains why Error cannot be handled
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
The code is the part where spring processess the ExceptionResolvers may it be annotation or bean based. You can see that Spring only cathces Exception not Throwable. Error is not a subclass of Exception but throwable so you wont be able to handle it with Spring this way. On a related note the annotation is also called #ExceptionHandler so it kind of implies that it wont work with Errors.
From the Error javadocs:
An Error is a subclass of Throwable that indicates serious problems
that a reasonable application should not try to catch. Most such
errors are abnormal conditions. The ThreadDeath error, though a
"normal" condition, is also a subclass of Error because most
applications should not try to catch it.
Catching an Error (or any other Throwable that is not a subclass of Exception) is a bad idea. Spring is doing the right thing by not supporting it.
I also crashed into this, from the code pasted by #peter-szanto seems there is no possibility to handle java.lang.Error with a Spring-registered handler. My use case would be to handle error with a localized page and also to log the error. My workaround is to use web.xml with error-page/error-code 500 defined and as error-page/location a Spring-handled controller (not a JSP) in order to get localization work. Downside is that when controller code runs there is no authentication for the current user. This catches also things Spring could not possibly handle like a wrong URI not mapped to Spring.
For me the below works with Spring 3.0.5 MVC
#ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException{
public NotFoundException() {
super();
}
}
#ExceptionHandler
#RequestMapping(method = RequestMethod.GET, value = "employee/{id}",
headers = "Accept=application/json,application/x-protobuf,application/xml")
public #ResponseBody method(xxxx)
{
..
throw new NotFoundException
...
}
didnt meant to handle Exception instead of Error? Error is very ralely used in Java and has a very few implementing classes.

Categories