Pawel Pawel - 1 month ago 18
Java Question

How to handle exception thrown by aspect using custom handler

I'm fighting with AOP and custom error handler.
I have one controller "WorkingController" - it is working as expected.

@Controller
public class WorkingController extends BaseController {

@RequestMapping("/welcome")
public ModelAndView helloWorld() {
return new ModelAndView("welcome", "message", "<h3>********** Hello World, Success!</h3>");
}

@RequestMapping("/success")
public void test(HttpServletResponse response) {
writeResponse(response, "Success!", ContentType.TEXT_JSON);
}

@RequestMapping("/test")
public void test2(HttpServletResponse response) throws IOException {
throw new IOException("This exception should be handled by custom handler.");
}

@InterestingMethod
@RequestMapping("/aspect")
public void test3(@InterestingArg @RequestParam(value = "id", required = false) String id,
HttpServletResponse response) throws IOException, AccessDeniedException {
throw new IOException("This exception should also be handled by custom handler.");
}
}

class BaseController {

@ExceptionHandler(Exception.class)
public void handleException(Throwable ex, HttpServletRequest request, HttpServletResponse response) {
System.out.println("Exception handled by BaseController, great work!: " + ex.getMessage());
ServletUtil.writeResponse(response, JSONObject.fromObject("Error").toString(),
ContentType.TEXT_JSON.toString(), false, "UTF-8");
}

void writeResponse(HttpServletResponse response, String data, ContentType contentType) {
ServletUtil.writeResponse(response, data, contentType.toString(), false, "UTF-8");
}

}


My case forces me to create interface, mark my controller and Aspect will check if user has access to given endpoint - so I have MarkedController interface and NotWorkingController.

@Controller
public class NotWorkingController extends BaseController implements MarkedController {

public void test2(HttpServletResponse response) throws IOException {
throw new IOException("NotWorkingController, This exception should be handled by custom handler.");
}

@InterestingMethod
public void test3(@InterestingArg String id,
HttpServletResponse response) throws IOException, AccessDeniedException {
throw new IOException("NotWorkingController, This exception should also be handled by custom handler.");
}
}

@Controller
public interface MarkedController {

@RequestMapping("/test2")
void test2(HttpServletResponse response) throws IOException;

@RequestMapping("/aspect2")
void test3(@RequestParam(value = "id", required = false) String id,
HttpServletResponse response) throws IOException, AccessDeniedException;
}

@Aspect
public class InterestingMethodAspect {
@Pointcut("@annotation(interestingMethod)")
public void interestingMethodPointcut(InterestingMethod interestingMethod) {
}

@Before("(interestingMethodPointcut(interestingMethod))")
public void asd(JoinPoint joinPoint, InterestingMethod interestingMethod) throws AccessDeniedException {
processArguments(joinPoint);
}

private void processArguments(JoinPoint joinPoint) throws AccessDeniedException {
throw new AccessDeniedException("You don't have access to this endpoint.");
}
}


And this is kind of magic for me, because:


  1. In NotWorkingController exception thrown by Aspect code is not handled by custom exception handler (located in BaseController).

  2. I'm forced to move all @RequestMappings to interface - it's a little bit annoying in my case. How to prevent that?



So, where should i look, what should I check?

Thanks in advance for any suggestions.

BTW: Complete maven project is available here.

Answer

Yhm, and I have the answers.

For the first problem (exception catching): HandlerExceptionResolver did all what I needed. I've removed @ExceptionHandler annotation from handleException method in BaseController and custom implementantion of HandlerExceptionResolver is catching all my exceptions).

Second problem: I had to switch from JDK-based proxies to CGLIB-based one.