Use Spring Aop on Spring mvc controller - java

I have a Spring MVC controller throw two kind of exception:
#RequestMapping(value = "forwardRefundApply",method = RequestMethod.GET)
public ModelAndView forwardRefundApply1(String ticketNbr)throws Exception {
if(true)
throw new Exception();
else
throw new ApplicationException("test");
}
then I write a AOP class to hanlder the Exception then return Model like this:
#Pointcut("execution(public * ..*(..))")
public void getRefundPointCut() {
}
#AfterThrowing(pointcut="getRefundPointCut()", throwing="e")
public ModelAndView throwException(Exception e){
ModelAndView mav = null;
if(e instanceof ApplicationException)
{
e.printStackTrace();
mav = new ModelAndView(CommonConstants.ERRORPAGE);
mav.addObject("errorMsg", "application error");
return mav;
}
else{
e.printStackTrace();
mav = new ModelAndView(CommonConstants.ERRORPAGE);
mav.addObject("errorMsg", "system error");
return mav;
}
}
the aop is work . but the the result is error. system error:
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoSuchMethodError
is Aspect class cannot return ModelAndView to Controller?

Why use AOP in this case at all? Spring comes with everything you need:
HandlerExceptionResolver is a global entry point into exception handling.
#ExceptionHandler lets you define handlers in controllers.
#ControllerAdvice lets you define #ExceptionHandlers on a global level.

The NoSuchMethodError is unrelated to your question and is caused by something you haven't shown us.
As for the question
is Aspect class cannot return ModelAndView to Controller?
I couldn't find any reference to it anywhere in Spring's AOP documentation, but you can see it in the implementation.
When you declare a #AfterThrowing advice, Spring uses a AspectJAfterThrowingAdvice to handle it. Its invoke(..) method is implemented as
#Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable t) {
if (shouldInvokeOnThrowing(t)) {
invokeAdviceMethod(getJoinPointMatch(), null, t);
}
throw t;
}
}
where mi.proceed() invokes your advised method and invokeAdviceMethod(..) invokes your #AfterThrowing advice method. Notice that it does nothing with the return value. As such, you can return a ModelAndView object from a #AfterThrowing advice method, but it won't serve any purpose, it'll simply be discarded.
A possible alternative is to declare a #Around advice. Within it, you wrap the proceeding call and catch the possible exceptions, handling them appropriately
#Around(value = "getRefundPointCut()")
public ModelAndView throwException(ProceedingJoinPoint joinPoint) throws Throwable {
ModelAndView mav = null;
try {
return (ModelAndView) joinPoint.proceed(); // might want to make sure that it is a ModelAndView
} catch(ApplicationException e) {
e.printStackTrace();
mav = new ModelAndView("home");
mav.addObject("errorMsg", "application error");
return mav;
} catch (Exception e) {
e.printStackTrace();
mav = new ModelAndView("home");
mav.addObject("errorMsg", "system error");
return mav;
}
}
Here you return the value of the advised method if it returns correctly. Or your catch any thrown Exception and again handle it appropriately by returning a different ModelAndView.

I haven't tested it myself but Spring AOP works with proxy objects and not the actual objects. So e is actually an instance of proxy and not of ApplicationException. So the following condition never executes to true.
if(e instanceof ApplicationException)
Easiest way of handling this would be to mark your aop setting in Spring configuration file with proxy-target-class="true".
<aop:aspectj-autoproxy proxy-target-class="true"/>
HTH

Related

How to throw custom exception from transactional Spring service?

I have this spring service:
#Service
#Transactional
public class ConsorcioServiceImpl implements ConsorcioService {
...
#Autowired
private ConsorcioRepository consorcioRepository;
#Override
public void saveBank(Consorcio consorcio) throws BusinessException {
try {
consorcioRepository.save(consorcio);
}
catch(DataIntegrityViolationException divex) {
if(divex.getMessage().contains("uq_codigo")) {
throw new DuplicatedCodeException(divex);
}
else {
throw new BusinessException(dives);
}
}
catch (Exception e) {
throw new BusinessException(e);
}
}
}
That service uses this Spring Data repository:
#Repository
public interface ConsorcioRepository extendsCrudRepository<Consorcio, Integer> {
}
I'm calling the service from a spring controller:
#Controller
#RequestMapping(value = "/bank")
public class BancaController {
#Autowired
private ConsorcioService consorcioService;
#RequestMapping(value="create", method=RequestMethod.POST)
public ModelAndView crearBanca(#Valid BancaViewModel bancaViewModel, BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
ModelAndView modelAndView;
MessageViewModel result;
try {
consorcioService.saveBank(bancaViewModel.buildBanca());
result = new MessageViewModel(MessageType.SUCESS);
redirectAttributes.addFlashAttribute("messageViewModel", result);
modelAndView = new ModelAndView("redirect:/banca/crear");
return modelAndView;
} catch (Exception e) {
result = new MessageViewModel(MessageType.ERROR);
modelAndView = new ModelAndView("crear-bancas");
modelAndView.addObject("messageViewModel", result);
return modelAndView;
}
}
But the exception I get in the controller is: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
instead of the DuplicatedCodeException I throw in the service. I need to identify the type of exception so I can give a custom friendly user message.
also your DuplicatedCodeException , BusinessException should be runtime exception , or add for method saveBank :
#Transactinal(rolbackFor={BusinessException.class,DuplicatedCodeException.,class })
in other case spring will not rollback transaction.
from Spring documentation:
While the EJB default behavior is for the EJB container to
automatically roll back the transaction on a system exception (usually
a runtime exception), EJB CMT does not roll back the transaction
automatically on an application exception (that is, a checked
exception other than java.rmi.RemoteException). While the Spring
default behavior for declarative transaction management follows EJB
convention (roll back is automatic only on unchecked exceptions), it
is often useful to customize this.
Just add catch (TransactionSystemException tse) before catch (Exception e) branch and then extract your exception with getOriginalException().
try {
consorcioService.saveBank(bancaViewModel.buildBanca());
result = new MessageViewModel(MessageType.SUCESS);
redirectAttributes.addFlashAttribute("messageViewModel", result);
modelAndView = new ModelAndView("redirect:/banca/crear");
return modelAndView;
} catch (TransactionSystemException tse) {
final Throwable ex = tse.getOriginalException();
if (ex instanceof DuplicatedCodeException) {
// DuplicatedCodeException
}
} catch (Exception e) {
result = new MessageViewModel(MessageType.ERROR);
modelAndView = new ModelAndView("crear-bancas");
modelAndView.addObject("messageViewModel", result);
return modelAndView;
}
That happens because your exception is wrapped in RollbackException as Throwable cause. In it's turn RollbackException is also a cause of TransactionSystemException.
You can build global exception handler to catch and customize all exceptions as you wish:
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT)
#ExceptionHandler(TransactionSystemException.class)
public ModelAndView handleDupilatedCode(HttpServletRequest req, TransactionSystemException ex) {
// Build you exception body here
Throwable e = ex.getOriginalException();
if(e instanceof DuplicatedCodeException)
// throw
// Or build custom exception as you want
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
#ControllerAdvice is available since Spring 3.2
Also you can use #ExceptionHandler at the #Controller level, or create this handling in abstract-controller if you have one as a super class of all your controllers.
public class FooController {
//...
#ExceptionHandler({ CustomException1.class, CustomException2.class })
public void handleException() {
//
}
}
There are some other approaches, for full reference please follow the:
Spring IO: Exception handling in Spring MVC
Baeldung: Exception handling with Spring

spring security: send redirect in case of exception

I have a question about Spring Security.
My idea is that in the case when the SM_USER header is wrong I don't want to send an uncatched exception (as the method loadUserByUsername of my class CustomUserDetailsService does).
public class CustomUserDetailsService implements UserDetailsService {...}
I want to catch it and thus redirect to the default page (with mapping my/default/page) and write a message text there like: try again please.
I already have an ExceptionResolver but it works only on controller level and not earlier.
#ControllerAdvice
public class ExceptionResolver {
#ExceptionHandler(PreAuthenticatedCredentialsNotFoundException.class)
public ResponseEntity<?> handleBindException(PreAuthenticatedCredentialsNotFoundException e) {
log.error(e.getMessage(), e);
...
return response;
}
#ExceptionHandler(UsernameNotFoundException.class)
public ResponseEntity<?> handleBindException(UsernameNotFoundException e) {
log.error(e.getMessage(), e);
...
return response;
}
}
As far as I understand I need to implement a new exception resolver for such cases, but when I try to built it in the application context, my whole programm crashes down.
#RequestMapping("/resource")
public class GlobalExceptionResolver extends AbstractHandlerExceptionResolver{
#Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse responce, Object handler, Exception exception) {
try {
responce.sendRedirect("/my/defualt/page");
} catch (IOException e) {
log.error(e);
}
ModelAndView mav = new ModelAndView();
if(exception instanceof PreAuthenticatedCredentialsNotFoundException){
mav.addObject("errorMessage","This user does not exist");
}
else if(exception instanceof UsernameNotFoundException){
mav.addObject("errorMessage","This user is too old");
}
return mav;
}
}
So, please, could you explain me how can I realize my plan in this case if spring security allows this in general?
Thank you in advance.
If you are using Spring xml you can call one bean with #PostConstruct when failed like this
<sec:form-login authentication-failure-handler-ref="afterLoginFail"
Example AfterLoginFail
public class AfterLoginFail extends SimpleUrlAuthenticationFailureHandler {
#PostConstruct
public void init() {
setDefaultFailureUrl("/login?status=failure");
}
}
Or if you use javaconfig use formLogin().failureUrl(authenticationFailureUrl) or .failureHandler()

Why #ExceptionHandler(value = Throwable.class) doesn't catch OutOfMemoryError? [duplicate]

Ours is a Spring MVC based REST application. I am trying to use ExceptionHandler annotation to handle all errors and exceptions.
I have
#ExceptionHandler(Throwable.class)
public #ResponseBody String handleErrors() {
return "error";
}
This works whenever there is an exception thrown and it doesn't work for any errors.
I am using Spring 4.0. Is there any work-around?
Contrary to what the ExceptionHandler#value() attribute indicates
Class<? extends Throwable>[] value() default {};
and #ExceptionHandler is only meant to handle Exception and its sub types.
Spring uses ExceptionHandlerExceptionResolver to resolve your annotated handlers, using the following method
doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception)
which as you can see only accepts an Exception.
You cannot handle Throwable or Error types with #ExceptionHandler with this configuration.
I would tell you to provide your own HandlerExceptionResolver implementation which does handle Throwable instances, but you'd need to provide your own DispatcherServlet (and most of the MVC stack) yourself since DispatcherServlet does not catch Throwable instances at any place where you could make any significant difference.
Update:
Since 4.3, Spring MVC wraps a thrown Throwable value in a NestedServletException instance and exposes that to the ExceptionHandlerExceptionResolver.
You can do a kind of Hacking to capture Error in Spring MVC.
First, define an Interceptor like this :
public class ErrorHandlingInterceptor extends HandlerInterceptorAdapter {
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
{
super.afterCompletion(request, response, handler, ex);
controller.handleError(ex.getCause(), request, response);
} }
Second, define a method in your controller like "handleError" method:
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setExceptionId(exceptionId);
errorResponse.setErrorMsg(ex.toString());
errorResponse.setServerStackTrace(serverStackTrace(ex));
response.setStatus(responseCode);
response.setContentType("application/json");
ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
writer.writeValue(response.getOutputStream(), errorResponse);
Finally, config your interceptor in Spring configuration.
<mvc:interceptors>
<bean class="ErrorHandlingInterceptor" />
</mvc:interceptors>
Code in DispatchServlet:
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// This is where to handle Exception by Spring.
// If Error happens, it will go to catch Error statement
// which will call afterCompletion method
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
// Trigger after-completion for successful outcome.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}

ExceptionHandler doesn't work with Throwable

Ours is a Spring MVC based REST application. I am trying to use ExceptionHandler annotation to handle all errors and exceptions.
I have
#ExceptionHandler(Throwable.class)
public #ResponseBody String handleErrors() {
return "error";
}
This works whenever there is an exception thrown and it doesn't work for any errors.
I am using Spring 4.0. Is there any work-around?
Contrary to what the ExceptionHandler#value() attribute indicates
Class<? extends Throwable>[] value() default {};
and #ExceptionHandler is only meant to handle Exception and its sub types.
Spring uses ExceptionHandlerExceptionResolver to resolve your annotated handlers, using the following method
doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception)
which as you can see only accepts an Exception.
You cannot handle Throwable or Error types with #ExceptionHandler with this configuration.
I would tell you to provide your own HandlerExceptionResolver implementation which does handle Throwable instances, but you'd need to provide your own DispatcherServlet (and most of the MVC stack) yourself since DispatcherServlet does not catch Throwable instances at any place where you could make any significant difference.
Update:
Since 4.3, Spring MVC wraps a thrown Throwable value in a NestedServletException instance and exposes that to the ExceptionHandlerExceptionResolver.
You can do a kind of Hacking to capture Error in Spring MVC.
First, define an Interceptor like this :
public class ErrorHandlingInterceptor extends HandlerInterceptorAdapter {
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
{
super.afterCompletion(request, response, handler, ex);
controller.handleError(ex.getCause(), request, response);
} }
Second, define a method in your controller like "handleError" method:
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setExceptionId(exceptionId);
errorResponse.setErrorMsg(ex.toString());
errorResponse.setServerStackTrace(serverStackTrace(ex));
response.setStatus(responseCode);
response.setContentType("application/json");
ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
writer.writeValue(response.getOutputStream(), errorResponse);
Finally, config your interceptor in Spring configuration.
<mvc:interceptors>
<bean class="ErrorHandlingInterceptor" />
</mvc:interceptors>
Code in DispatchServlet:
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// This is where to handle Exception by Spring.
// If Error happens, it will go to catch Error statement
// which will call afterCompletion method
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
// Trigger after-completion for successful outcome.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}

Spring MVC #ExceptionHandler method in controller never being invoked

I have a Spring MVC controller with some simple REST service requests. I would like to add some error handling when specific exceptions are thrown from my services, but I cannot get a handler method annotated with #ExceptionHandler to actually ever be called. Here is one service I am deliberately throwing an exception to try and get my handler method to take over. The handler method is never invoked and Spring just returns a 500 error to the calling client. Do you have any ideas on what I'm doing wrong?
#ExceptionHandler(IOException.class)
public ModelAndView handleIOException(IOException ex, HttpServletRequest request, HttpServletResponse response) {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
System.out.println("It worked!");
return new ModelAndView();
}
#RequestMapping(value = "/json/remove-service/{id}", method = RequestMethod.DELETE)
public void remove(#PathVariable("id") Long id) throws IOException {
throw new IOException("The handler should take over from here!");
}
Frustratingly, I have suffered from this as well. I discovered that if you mistakenly implement Throwable instead of Exception the Exception resolver will just rethrow your Throwable as a IllegalStateException. This will fail to invoke your #ExceptionHandler.
If you've implemented Throwable instead of Exception try changing it to Exception instead.
Here's the code in question from InvocableHandlerMethod
catch (InvocationTargetException e) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = e.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
throw new IllegalStateException(msg, targetException);
}
}
This tip on the Spring forum may help you.
Likely you have configured the beans for your DispatchServlet in a webmvc-servlet.xml file (the *-servlet.xml file may be named differently)
If the XML file already includes another ExceptionResolver (like SimpleMappingExceptionResovler Spring wont automatically add any other resolvers for you. So manually adding the annotation resolver like so:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver" />
Should enable the #HandlerException processing.
I have found that #ExceptionHandler works with Throwable when you define method in such a manner:
#ExceptionHandler(Throwable.class)
#ResponseBody
public String handleException(Throwable e) {
}
In this case method has only one argument of type Throwable. If I try to use in this method some additional parameters (I tried to use Model), I receive 500 exception (this method isn't call). However this still works when additional parameters are HttpServlerRequest or HttpServlerResponse.
It will not work, because when you return String you return View name.
Now your Spring MVC controller is searching for This method is never called! Why not?! view and can find.
Make sure that #ExceptionHandler is mapped to existing view.

Categories

Resources