We're trying to introduce generic logger in our application using Spring AOP for log statements which are under catch block.
Before AOP
try
{
\\Business Logic
}
catch(Exception e){
\\some recovery mechanism that won't be generic across different layers
log.error();//These statements needs to be moved to generic logger
}
After going through Spring Docs,I have found this can be done using AfterThrowing advice.
After throwing advice is Advice to be executed if a method exits by throwing an exception.
In order to do this We'll to change our existing exception handling code by re throwing Exception inside catch block something like this for AfterThrowing Advice to work.
After AOP:
try
{
\\Business Logic
}
catch(Exception e){
\\some recovery mechanism that won't be generic across different layers
throw e;
}
AOP code:
#Aspect
#Sl4j
#Component
public class LoggingAdvice {
#AfterThrowing(pointcut = "execution (* * com..*(..)", throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint, Exception e) {
log.error("Exception occured",e);
}
}
Do you think is there any better solution than this rather than rethrowing Exception in catch block and propagating it upwards as per call hierarchy?
Note any raised or unchecked exceptions would be catched anyway by AfterThrowing Advice..All i want to do is perform logger clean up by removing log.error inside catch block and have it generic using AOP.
As was discussed here, #AfterThrowing is nice for logging exceptions which are actually thrown.
Your case is quite special as you want to log exceptions which are being caught/handled. If you use full AspectJ instead of Spring AOP for this use case you can use a handler(*) pointcut as described with sample code in this answer. It would enable you to factor out your log statements from your catch blocks without the need to escalate (re-throw) exceptions which have already been properly handled, thus changing your logic and making it necessary to catch them somewhere else later.
The better approach is to remove the catch blocks as you are going to use #AfterThrowing anyway. And implement whatever you want to implement on top that aspect execution.
Related
I'm using Spring Boot 2.0.1 with inclusion of validation-starter (which comes with Hibernate Validation 6.x) and trying to validate a method parameter and return objects in my Spring repository layer. There are basically a ParamObj and ReturnObj for this method,
#Repository
#Validated // Spring integration of validation
public class MyRepoImpl implements MyRepo {
#Override
#Valid
public ReturnObj myMethod(#Valid ParamObj param) throw CustomException {
try {
// makes a call that might throw an exception
} catch (Exception e) {
throw new CustomException(/*...*/, e);
}
return returnObj;
}
}
ParamObj and ReturnObj both use a simple set of Bean Validation annotations on some of their properties, e.g #Pattern, #Min, etc.. My problem is that with these annotations, if either ParamObj or ReturnObj fails the validation process, MyRepoImpl#myMethod() would throw javax.validation.ConstraintViolationException.
However, as you can see, the contract for myMethod() mandates to throw a CustomException. Yes, ConstraintViolationException is a RuntimeException, however, now the caller of MyRepoImpl#myMethod(), e.g. a service layer logic, needs to catch this RuntimeException in addition to CustomException.
And the reason is, I need to prevent all the exceptions from bubbling out of the service layer (for this particular Spring #Service I have which calls this repository) -- this service is part of a long list of services that are called via a service orchestration which constructs a larger encompassing object. In other word, this piece (result coming obtained via MyRepoImpl) is considered optional, and subsequently, its failure should only be logged but do not stop the whole process.
For that reason, I can't utilize ControllerAdvice to capture ConstraintViolationException for the entire application.
My goal is to be able, when the validation fails for either method parameter or return, to throw only CustomException back to the caller.
Is this possible with annotations only? I looked into MethodValidationPostProcessor but can't figure out if I can implement my own to accomplish this goal.
P.S. The only way I can think to achieve this is to forgo #Valid/#Validated and call Validator#validate(obj, ...) programmatically for the parameter and return value inside the try-catch block, catch the constraint exception, and throw my CustomException(). However, I want to know if I can avoid that and leave this hard coded validation out of the repository.
Since I was not here a year ago I would not have answered but since this is a common question as I can see through my experience with fellow students, I think the answer might be #ControllerAdvice, comes handy with any type of exceptions
doc --> https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html
I have a global exception handler to share across REST #Controllers. For this I use a #ControllerAdvice with some #ExceptionHandler methods. This works fine. Now, if I add an #ExceptionHandler in a particular Rest Controller then that new handler takes precedence over the global exception handler and the global one is just never called.
What I need is actually to have both called. The order doesn't matter. The point is that there is some global, controller-agnostic error handling code and also some controller-specific error handling and I need both to execute. Is this possible? e.g. Can I somehow in the controller-specific handler (which is called first) mark the exception handling as not handled so the next handler in line is invoked?
I know I could inject the #ControllerAdvice in the #Controller and invoke the global handler from the specific one myself, but I rather keep the controller decoupled from the global exception handler
I don't think you can do this with out-of-the-box Spring. If you look under the hood at this method ExceptionHandlerExceptionResolver#doResolveHandlerMethodException, you can see that at first Spring looking for single method that will handle occurred exception:
...
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
...
You can also look at the implementation of getExceptionHandlerMethod method. First its trying to find appropriate handler within you controller methods, if nothing found - then within controller advisors.
After that it invokes it:
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking #ExceptionHandler method: " + exceptionHandlerMethod);
}
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
catch (Exception invocationEx) {
if (logger.isErrorEnabled()) {
logger.error("Failed to invoke #ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
}
return null;
}
You should also note that Spring swallows any exception that might occur during original exception handling, so you can't even throw new exception from your first handler or rethrow original exception so it can be catched somewhere else (You can actually, but this is pointless).
So, if you really want to do this - I guess the only way is to write you own ExceptionHandlerExceptionResolver (maybe extend Springs ExceptionHandlerExceptionResolver) and modify doResolveHandlerMethodException method, so it looks for multiply exceptionHandlerMethod (one within controllers and one within advisors) and invokes it in a chain. This might be tricky :)
Also, you can look at this Jira ticket.
Hope it helps.
I have heard that it is possible to log (or do something else) Exceptions with Spring in my web-App, so I don't have to manually insert in every "catch(){}" block the Log-function.
Does anyone have experience with Spring-overall-logging? I just want to get informed when an error appears
ExceptionHandler is the central point for handling unexpected Exceptions that are thrown during the Faces lifecycle. The ExceptionHandler must not be notified of any Exceptions that occur during application startup or shutdown.
See the specification prose document for the requirements for the default implementation. Exceptions may be passed to the ExceptionHandler in one of two ways:
1.)By ensuring that Exceptions are not caught, or are caught and re-thrown.
This approach allows the ExceptionHandler facility specified in section JSF.6.2 to operate on the Exception.
2.)By using the system event facility to publish an ExceptionQueuedEvent that wraps the Exception.
This approach requires manually publishing the ExceptionQueuedEvent, but allows more information about the Exception to be stored in the event. The following code is an example of how to do this.
Global Exception Handler – Exception Handling is a cross-cutting concern, it should be done for all the pointcuts in our application. We have already looked into Spring AOP and that’s why Spring provides #ControllerAdvice annotation that we can use with any class to define our global exception handler.
The handler methods in Global Controller Advice is same as Controller based exception handler methods and used when controller class is not able to handle the exception.
Sample Code
#ExceptionHandler(Exception.class)
public ModelAndView getExceptionPage(Exception e, HttpServletRequest request) {
request.setAttribute("errorMessageObject", e.toString());
return model;
}
** Here we can catch the base exception class Exception.class or any other exception class. Also we can throw and catch our own custom defines exception class.
So in my Service layer I have some logic that does some stuff. Sometimes it has to check to see if something is possible before it does it OR it just has to get some OK from the front end ("Are you sure you want to do this?" kind of stuff). The front end, of course, sends a transaction to the screen to get the info.
In the past I have used RuntimeExceptions for this. I will throw a
new MessageException("are.you.sure");
and then the controller level will do a
try{
theService.doSomething();
}catch(MessageException me) {
model.addAttribute(me.getMessageKey());
result.addError(new ObjectError());
}
In another application I made a PostOffice object and would put letters and such in it for messages. It was quite elaborate and very nice, but my new app is much smaller and I don't want all that.
So I am looking for some best practices. How do you guys send messages from the service layer to the front end? And keep in mind, I am NOT talking about Exceptions! I am just talking about messages from the service layer.
One beautiful thing about Spring MVC is the Exception handling. Since the DispatcherServlet has a try-catch(Exception) wrapping the handler method (ie. your controller method), it can catch all exceptions thrown and handle them with #ExceptionHandler methods or some other construct (there are alternatives).
What I've started doing is making my Service methods only throw RuntimeException instances (works well with #Transactional) and define all my #ExceptionHandler methods in a #ControllerAdvice annotated class. Something like:
#ControllerAdvice
public class ControllerHandler {
private static final Logger logger = LoggerFactory.getLogger(ControllerHandler.class);
#ExceptionHandler(value = AuthenticationException.class)
public String handleAuthenticationException(AuthenticationException e) {
if (logger.isInfoEnabled()) {
logger.info("An AuthenticationException occurred: {}", e.getMessage());
}
return "redirect:/";
}
...more
}
#ExceptionHandler annotated methods have a few rules, but are very customizable. You can see all possibilities in the javadoc here. You should also take a look at ResponseStatus.
Personally, I rarely throw checked exceptions from my service layer. The only one that often appears is IOException (parsing JSON, opening files) and even that I like to wrap in a RuntimeException, because it's not like I can do anything special about it at that level.
I'm currently working with MyBatis-Spring integration framework and that's what I read from docs:
Rather than code data access objects (DAOs) manually using SqlSessionDaoSupport or SqlSessionTemplate,
Mybatis-Spring provides a proxy factory: MapperFactoryBean. This class lets you inject data mapper interfaces
directly into your service beans. When using mappers you simply call them as you have always called your
DAOs, but you won't need to code any DAO implementation because MyBatis-Spring will create a proxy for
you.
That's a very nice feature... but what about exception handling? Where should I translate SQL errors? In my service layer? But wouldn't it violate service-DAO patterns?
Example:
public final class AccountServiceImpl implements AccountService {
(...)
private AccountMapper accountMapper;
(...)
#Override
public void addAccount(Account account) throws AccountServiceException {
//Validating, processing, setting timestamps etc.
(...)
//Persistence:
int rowsAffected;
try {
rowsAffected = accountMapper.insertAccount(account);
} catch (Exception e) {
String msg = e.getMessage();
if (msg.contains("accounts_pkey"))
throw new AccountServiceException("Username already exists!");
if (msg.contains("accounts_email_key"))
throw new AccountServiceException("E-mail already exists!");
throw new AccountServiceException(APP_ERROR);
}
LOG.debug("Rows affected: '{}'", rowsAffected);
if (rowsAffected != 1)
throw new AccountServiceException(APP_ERROR);
}
Is it OK to translate exceptions in service layer?
How should it be done?
Thanks in advance for you advice.
Having recently used mybatis-spring for a project I came across the same stumbling block. I also didn't want to litter my service class with DAO exception handling, particularly since some methods in my service layer required read-only access to a lot of different tables.
The solution I arrived at was to catch the exceptions in the service layer but create your own exception type that takes the caught exception as a parameter. This can then filter out what kind of error message should be contained when the exception is actually constructed and remove the need for string matching (in the service layer at least).
You are close to that there, except the AccountServiceException would have a constructor that took the Exception e as a parameter. I also chose to try and do all my data access as early as possible and wrap it all in a single try/catch. Since the MapperFactoryBean always translates thrown exceptions in to Spring DataAccessExceptions you don't have to worry about catching other kinds of exceptions when doing data access.
I hesitate to consider this an answer as such - more of a sharing of experience given I came across that and hesitated as well.
Translating low level DataAccessExceptions thrown by MyBatis to application-defined ones in service layer is a standard practice.
It's usually connected to transaction handling as you can't handle the transaction spanning multiple DAOs in DA layer.
So yes it's OK and even recommended.
Normally I log the exceptions thrown by DAO in error log and rethrow something defined by application.