import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class ExceptionAlerts {
public ExceptionAlerts() {
System.err.println("Class Scanned");
}
#AfterThrowing(pointcut = "com.name.papp.star", throwing = "ex")
public void doRecoveryAction(Throwable ex) throws Throwable {
System.err.println(">>>>>>>>>>>>>>>>Recovery Actions++++++++++++++++");
}
}
Service Interface
public interface SignInService {
CustomerSignInDTO signIn()
throws LappException;
}
Service Implementation Class
public class SignInServiceImpl implements SignInService {
#Override
#Transactional(readOnly = false, rollbackFor = Exception.class)
public CustomerSignInDTO signInCustomer(CustomerDeviceDTO customerDeviceDTO,
String mobileNumber, boolean createIfNotExist)
throws LappException {
// Throwing Exception Here
}
}
Problem -
Spring-Boot-1.2.5
The method doRecoveryActions never gets called. I am also using #ExceptionHandler somewhere to prepare the error response. Is it because #ExceptionHandler catches all the exceptions and doRecoveryActions is never called? Any suggestions would be appreciated!
Related
I use java spring.
I try to use ControllerAdvice in my project as a global event handler:
#ControllerAdvice
public class ExceptionsHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(RestClientException.class)
public void RestClientException(RestClientException ex)
{
System.out.println("RestClientException" + ex.getMessage());
}
#ExceptionHandler
public void Exception(Exception ex)
{
System.out.println("Exception" + ex.getMessage());
}
}
In this function I create and throw an exception:
#Component
#RequiredArgsConstructor
public class Scheduler {
public void schedule() throws JsonProcessingException, Exception {
throw new Exception();
}
}
And:
#Component
#RequiredArgsConstructor
public class RestfulReaderServiceImpl implements RestfulReaderService {
public String getData(String url) throws RestClientException {
throw new RestClientException();
}
}
But when those functions are fired and exceptions are thrown the exception handlers
in class ExceptionsHandler does not execute.
Any idea why exception handlers in ExceptionsHandler class not catches and handles the
exceptions?
#ControllerAdvice only handles exceptions thrown from Controller methods i.e. methods annotated with #RequestMapping or any of its shortcut annotations (#GetMapping etc.). You can check this by calling your exception throwing method from any #RequestMapping annotated method.
Have a custom error controller on Spring boot:
package com.example.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.boot.web.servlet.error.ErrorController;
import javax.servlet.http.HttpServletRequest;
#Controller
public class CustomErrorController implements ErrorController
{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
...
}
#Override
public String getErrorPath()
{
return "/error";
}
}
But, when compile says: getErrorPath() in ErrorController has been deprecated. Ok, i found information: use server.error.path property. Ok, add this in application.properties and delete the function, but now says: CustomErrorController is not abstract and does not override abstract method getErrorPath() in ErrorController, ¿need a deprecated function?.
How to made the custom error controller?, the ErrorController requires getErrorPath but it is deprecated, what is the correct alternative?.
Starting version 2.3.x, Spring boot has deprecated this method. Just return null as it is anyway going to be ignored. Do not use #Override annotation if you want to prevent future compilation error when the method is totally removed. You can also suppress the deprecation warning if you want, however, the warning (also the #Override annotation) is helpful to remind you to cleanup/fix your code when the method is removed.
#Controller
#RequestMapping("/error")
#SuppressWarnings("deprecation")
public class CustomErrorController implements ErrorController {
public String error() {
// handle error
// ..
}
public String getErrorPath() {
return null;
}
}
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping("/error")
public ModelAndView handleError(HttpServletResponse response) {
int status = response.getStatus();
if ( status == HttpStatus.NOT_FOUND.value()) {
System.out.println("Error with code " + status + " Happened!");
return new ModelAndView("error-404");
} else if (status == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
System.out.println("Error with code " + status + " Happened!");
return new ModelAndView("error-500");
}
System.out.println(status);
return new ModelAndView("error");
}
}
there is an #ControllerAdvice annotation
#ControllerAdvice
public class MyErrorController {
#ExceptionHandler(RuntimeException.class)
public String|ResponseEntity|AnyOtherType handler(final RuntimeException e) {
.. do handle ..
}
#ExceptionHandler({ Exception1.class, Exception2.class })
public String multipleHandler(final Exception e) {
}
}
To handle errors, There is no need to define a controller class
implementing an error controller.
To handle errors in your entire application instead of writing
#Controller
public class CustomErrorController implements ErrorController{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
...
}
}
use the below class
#ControllerAdvice
public class myExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<YourResponseClass> handleAllExceptions(Exception ex, WebRequest request) {
YourResponseClassexceptionResponse = new YourResponseClass(new Date(), ex.getMessage());// Its an example you can define a class with your own structure
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(CustomException.class)
public final ResponseEntity<YourResponseClass> handleAllExceptions(Exception ex, WebRequest request) {
YourResponseClass exceptionResponse = new YourResponseClass(new Date(), ex.getMessage()); // For reference
return new ResponseEntity<YourResponseClass>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(BadCredentialsException.class)
public final ResponseEntity<YourResponseClass> handleBadCredentialsException(BadCredentialsException ex, WebRequest request){
YourResponseClass exceptionResponse = new YourResponseClass(new Date(), ex.getMessage());// For refernece
return new ResponseEntity<>(exceptionResponse, HttpStatus.UNAUTHORIZED);
}
}
The class above annoted with #ControllerAdvice acts as custom exception handler and it handles all the expecptions thrown by ur application. In above code sample only three exceptions are showed for understanding. It can handle many exceptions
In your application if there's any exception thrown it will come to this class and send the response back. You can have a customized message and structure as per ur needs.
#Controller
public class AppErrorController implements ErrorController {
#RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if(status != null) {
int statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.FORBIDDEN.value()) {
return "errorpages/error-403";
} else if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "errorpages/error-404";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "errorpages/error-500";
}
}
return "errorpages/error";
}
}
I have made some subclass for exception handling. The method throws a subclass exception. I have made #ExceptionHandler for both super class as well as subclasses. But only if there is no handling for super class of exception (handleSuperException(SuperclassExceptionexception e)), then the subclass exceptions are handled. SubClassAException, SubClassBException, SubClassCException extends SuperclassExceptionexception.
public class Controller #PostMapping("/path/") {
public ResponseEntity<String> method() throws SuperclassException{
}
#ExceptionHandler(SuperclassException.class)
public ResponseEntity handleSuperException(SuperclassExceptionexception e) {
//hadle superclass related
}
#ExceptionHandler({SubClassAException.class, SubClassBException.class, SubClassCException.class})
public ResponseEntity handleSubClassException(SuperclassExceptionexception e) {
//handle more specific
}
But it never goes to handleSubClassException even if subclass exceptions are thrown.
Unable to reproduce!
Here is Minimal, Reproducible Example, tested with Spring Boot 2.2.0 (Spring 5.2.0).
package web.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class FailController {
#GetMapping("/dmz/fail")
public String failSuper() {
throw new SuperclassException("failSuper()");
}
#GetMapping("/dmz/failA")
public String failA() {
throw new SubClassAException("failA()");
}
#GetMapping("/dmz/failB")
public String failB() {
throw new SubClassBException("failB()");
}
#ExceptionHandler(SuperclassException.class)
public ResponseEntity<?> handleSuperException(SuperclassException e) {
return ResponseEntity.badRequest().body("handleSuperException: " + e);
}
#ExceptionHandler({SubClassAException.class, SubClassBException.class})
public ResponseEntity<?> handleSubClassException(SuperclassException e) {
return ResponseEntity.badRequest().body("handleSubClassException: " + e);
}
}
class SuperclassException extends RuntimeException {
public SuperclassException(String message) {
super(message);
}
}
class SubClassAException extends SuperclassException {
public SubClassAException(String message) {
super(message);
}
}
class SubClassBException extends SuperclassException {
public SubClassBException(String message) {
super(message);
}
}
I'm using /dmz/ so the Spring Security setup won't require login. In a plain vanilla Spring Boot setup, that will of course not be needed.
Output (http://localhost:8080/dmz/fail)
handleSuperException: web.controller.SuperclassException: failSuper()
Output (http://localhost:8080/dmz/failA)
handleSubClassException: web.controller.SubClassAException: failA()
Output (http://localhost:8080/dmz/failB)
handleSubClassException: web.controller.SubClassBException: failB()
I have a Custom exception that handles the expected errors of my program and here is the code.
#ControllerAdvice
#RestController
public class DashboardException {
#ExceptionHandler({Exception.class, IOException.class, ParseException.class, JsonProcessingException.class})
public final ResponseEntity<ErrorDetails> dataNotFoundException(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails();
errorDetails.setTimestamp(new Date().toString());
errorDetails.setMessage(ex.getMessage());
errorDetails.setPath(request.getDescription(false));
errorDetails.setStatus(HttpStatus.BAD_REQUEST.value());
errorDetails.setError(HttpStatus.BAD_REQUEST);
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
}
My problem is on how to properly unit test this class. This is what I have made so far to make it cover, but with no luck.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { DashboardException.class, TestConfiguration.class, DataController.class })
public class testDashboardException {
private MockMvc mockMvc;
#Autowired
WebApplicationContext wac;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Configuration
#EnableWebMvc
public static class TestConfiguration { }
#Controller
#RequestMapping("/tests")
public static class RestProcessingExceptionThrowingController {
#GetMapping(value = "/exception")
public #ResponseBody String find() throws Exception {
throw new Exception("global_error_test");
}
}
#Test
public void testHandleException() throws Exception {
mockMvc.perform(get("/tests/exception"))
.andExpect(new ResultMatcher() {
#Override
public void match(MvcResult result) throws Exception {
result.getResponse().getContentAsString().contains("global_error_test");
}
})
.andExpect(status().isBadRequest());
}
/*
* #Test public void testErrorDetailsValue() {
*
* thrown.expect(Exception.class); thrown.expect(IOException.class);
* thrown.expect(ParseException.class);
* thrown.expect(JsonProcessingException.class);
*
* thrown.expectMessage("Bad Request");
*
* }
*/
}
I only have a little knowledge concerning custom exceptions. What am I missing here? Thanks for any assistance.
I found out how to cover my custom exception. I just included a test on my controller that will fail the endpoint and it did catch an exception and covered my custom exception.
I created an interceptor that intercepts the call to my method and also its response. What happens is that I have to use a service while intercepting and the service works fine when intercepted on the request, but is null when intercepted on the response.
I tried separating the interceptor so the implementation of WriterInterceptor was in another class, but it seems to behavior the same.
#POST
#Path("/test")
#MyAnnotation
public void test(
final #Suspended AsyncResponse response,
final #ApiParam(value = "Test.", required = true) #FormParam("test") String test)
throws ServiceException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new AsyncResponseCallable(response) {
#Override
public Response execute() throws Exception {
Object object = service.foo();
Status status = Status.OK;
return Response.status(status).entity(object).build();
}
});
}
#Provider
#MyAnnotation
public class MyInterceptor implements WriterInterceptor {
#EJB
private MyService myService;
#AroundInvoke
public Object interceptor(InvocationContext context) throws Exception {
myService.test();
return context.proceed();
}
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws WebApplicationException {
myService.test();
context.proceed();
}
}
#Stateless
public class MyService {
public void test() {
System.out.println("Done");
}
}
The method aroundWriteTo should have an instance of myService injected by the EJB annotation, but it is null.
Can you guys help me figure this out?