We have a layered architecture and want to control exception handling at the application service layer. The layers below it will throw the necessary exceptions, but the service layer will provide the wrapper to the facade layer so the facade layer can expect a consistent exception class.
However, The service layer is using autowired components and basically all the errors are wrapped in spring exception (and hibernate) classes. Since this is not at the method level, how do I wrap them into a consistent service level exception class? Any ideas on how the service layer can take control over the exceptions wrapped in spring exception classes. I apologize if this question sounds too vague, but I can provide more details if needed. We are not using spring MVC.
Example below:
#Service("RuleService")
#Transactional
public class RuleService implements IRuleService {
#Autowired
IPersistenceManager<IRuleBO, Long> pMgrRule;
public AppServiceResponse createRule(RuleDTO ruleDTO) throws ApplicationException, ServerException {
try {
//do something
}
catch (PersistenceException pe) {
throw new ApplicationException (pe);
}
catch (ServerException se) {
throw se;
}
catch (Exception e) {
throw new ApplicationException (e);
}
At the persistence layer, it is something like..
#Transactional
public T save(T entity) throws ServerException, PersistenceException {
try {
getSession().saveOrUpdate(entity);
return entity;
}
catch (JDBCException je) {
throw new PersistenceException(je);
}
catch (QueryException qe) {
throw new PersistenceException(qe);
}
catch (NonUniqueResultException nre) {
throw new PersistenceException(nre);
}
catch (HibernateException he) {
throw new ServerException(he);
}
}
As you can see we want to return the ApplicationException from the service layer. But since the components are autowired, any database connection error, for example, would result in a HibernateException wrapped in a SpringException. Is there a way to take control of the exception from Spring?
I would not declare any extra exceptions as long as you do not want to handle them later so..
#Service("RuleService")
#Transactional
public class RuleService implements IRuleService {
#Autowired
IPersistenceManager<IRuleBO, Long> pMgrRule;
public AppServiceResponse createRule(RuleDTO ruleDTO) throws ApplicationException {
//
persistenceService.save(myEntity);
}
and persistence like
#Transactional
public T save(T entity){
getSession().saveOrUpdate(entity);
}
then you can create an ExceptionHandler aspect to handle all of exceptions from service layer and wrap them to ApplicationException
#Aspect
public class ExceptionHandler {
#Around("execution(public * xxx.xxx.services.*.*(..))")
public Object handleException(ProceedingJoinPoint joinPoint) throws Throwable {
Object retVal = null;
try {
retVal = joinPoint.proceed();
}
catch (JDBCException jDBCException ) {
throw new ApplicationException(jDBCException);
}
catch (JpaSystemException jpaSystemException) {
throw new ApplicationException(jDBCException);
}
// and others
return retVal;
}
This kind of design can reduce your code complexity. You may appreciate this especially during the testing phase of you project. You have also here a clear design and one special component ONLY for handling exceptions.
Related
UPD I've updated code for Aspects to throw exception further
I have SpringBoot application, service class and I need to implement Exception Handler for my service (not MVC). The task is to log error and throw it further to the client.
I decided to use Aspect with #AfterThrowing advice. I'm gonna catch few exceptions (that extend RuntimeException) at AspectOne aspect. And for other cases I need to catch exceptions (that extend RuntimeException) at AspectTwo aspect.
So I did the following code:
public class MyOwnException extends RuntimeException {
}
#Aspect
#Order(0)
#Component
public class AspectOne {
#Pointcut("execution(* com.test.MyService.*(..))")
public void logException() {}
#AfterThrowing(pointcut="logException()", throwing="ex")
public void logException(MyOwnException ex) {
System.out.println("MyOwnException has been thrown: " + ex.getMessage());
throw ex;
}
}
#Aspect
#Order(1)
#Component
public class AspectTwo {
#Pointcut("execution(* com.test.MyService.*(..))")
public void logException() {}
#AfterThrowing(pointcut="logException()", throwing="ex")
public void logException(RuntimeException ex) {
System.out.println("Some unknown exception has been thrown: " + ex);
throw ex;
}
}
The problem is that AspectTwo is executed in both cases for MyOwnException and other ancestors of RuntimeException. How can I limit AspectTwo to be executed only when AspectOne haven't caught the exception?
Seems like #Order annotation works not as I expected.
How about a little hack to indicate an exception is already handled/adviced ?
Also note that , the order of execution to be AspectOne before AspectTwo here , the Order should be specified as 1 for AspectOne and 0 for AspectTwo.
From the reference documentation section : Advice Ordering
What happens when multiple pieces of advice all want to run at the
same join point? Spring AOP follows the same precedence rules as
AspectJ to determine the order of advice execution. The highest
precedence advice runs first "on the way in" (so, given two pieces of
before advice, the one with highest precedence runs first). "On the
way out" from a join point, the highest precedence advice runs last
(so, given two pieces of after advice, the one with the highest
precedence will run second).
Following code leverages the Throwable.addSuppressed() method to indicate an exception object is already handled/adviced.
--
Add an Exception class to be used as an indicator.
public class AlreadyAdvicedIndicator extends Exception {
private static final long serialVersionUID = 1L;
public AlreadyAdvicedIndicator(String message) {
super(message);
}
}
AspectOne with modified Order and logic to add a suppressed exception.
#Component
#Aspect
#Order(1)
public class AspectOne {
public static final String ALREADY_ADVICED_MSG="Adviced with AspectOne";
private static final AlreadyAdvicedIndicator alreadyAdviced = new AlreadyAdvicedIndicator(ALREADY_ADVICED_MSG);
#Pointcut("execution(* com.test.MyService.*(..))")
public void logException() {}
#AfterThrowing(pointcut="logException()", throwing="ex")
public void logException(MyOwnException ex) {
System.out.println("MyOwnException has been thrown: " + ex.getMessage());
ex.addSuppressed(alreadyAdviced);
}
}
AspectTwo with modified Order and logic to check for already adviced.
#Component
#Aspect
#Order(0)
public class AspectTwo {
#Pointcut("execution(* com.test.MyService.*(..))")
public void logException() {
}
#AfterThrowing(pointcut = "logException()", throwing = "ex")
public void logException(RuntimeException ex) {
if (isAlreadyAdviced(ex)) {
System.out.println("Already Adviced : Skipping");
} else {
System.out.println("RuntimeException has been thrown: " + ex.getMessage());
}
}
private boolean isAlreadyAdviced(RuntimeException ex) {
for(Throwable e : ex.getSuppressed()) {
if(AspectOne.ALREADY_ADVICED_MSG.equals(e.getMessage())){
return true;
}
}
return false;
}
}
I currently have two ControllerAdvice in my application, I'm supposed to merge them into one.
But I need to test them before and after the merge, test the exception and the object that the controller return me.
I'm trying to make a jUnit test with Mockito but it seems impossible to test the exceptions without any context, without a controller, etc ...
Does anyone know how can I proceed to achieve what I'm trying to do ?
I also try to throw manually an exception but obviously it wasn't catched by the ControllerAdvice.
So basically here is what i'm trying to do:
Manually throw an exception
This exception is handled by my ControllerAdvice
Check the returned object (code & message)
Here is a sample of code I have:
#Before
public void setup() {
...
mockMvc = MockMvcBuilders.standaloneSetup(getController())
.setControllerAdvice(new GlobalControllerExceptionHandler())
.setCustomArgumentResolvers(resolver, resolver_0, resolver_1)
.setHandlerExceptionResolvers(exceptionResolver).build();
}
#Controller
#RequestMapping("/tests")
public static class RestProcessingExceptionThrowingController {
#RequestMapping(value = "/exception", method = GET)
public #ResponseBody String find() {
throw new EntityNotFoundException();
}
}
#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().isNotFound());
}
I have the good status code at the end but it doesn't use my ControllerAdvice (I try with the debugger)
You can just call handler method directly
#ControllerAdvice
MyAdvice{
#ExceptionHandeler(listOfExxcetpions)
public ResponseEntity someOfMyExceptionsHandler(Exception e){
.....
}
}
and in test
MuTest{
private MyAdvice advice=new MyAdvice();
#Test
public void oneOfTests(){
Exception e=new SomeSortOfExceptionToTest();
resp=advice.someOfMyExceptionsHandler(e)
assertThat(resp).....dostuff;
}
}
If you want to test how spring integrates with your handlers - if your annotations are correct, ordering serialization etc - well that will be an integration test and you have to boot up test context - then you can throw exceptions directly from controller methods.
I'm very new to spring mvc sorry if I'm asking a basic questions, I need to maintain Global Exception Handling in my spring 4 MVC, Jersey project and return JSON response to IOS mobile app. Now by using #ControllerAdvice and #ExceptionHandler, I have created a class like below
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(Exception.class)
public #ResponseBody handleException(HttpServletRequest reqException ex) {
ErrorInfo response=new ErrorInfo();
if(ex.getMessage.contains("java.io")){
response.setMessage("FileNotFound exception occur");
return response;
}else if ...
}
Please advice if above approach is correct or is there any alternative way to handle all exceptions occur in controller,service and DAO layer.
what you use is correct, all exceptions just be handled.In service or Dao layer,you just need to throw your business exception.The class you have created will catch the exception.But you should handle the exception in different ways,and define your own business exception.
here are some example codes.
#ExceptionHandler(RuntimeException.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ErrorResponse handleUnexpectedServerError(RuntimeException ex) {
ex.printStackTrace();
return new ErrorResponse("012", ex.getMessage());
}
handle the business exception,the BusinessErrorException is custom exception.
#ExceptionHandler(BusinessErrorException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ErrorResponse handleBusinessErrorException(BusinessErrorExceptionex) {
return new ErrorResponse(ex.getCode(), ex.getMessage());
}
Since recently I've been testing the use of the new #TransactionalEventListener from Spring.
But I can't find a nice way to handle error that could be thrown in the event listener method.
By the way for what I've tested the #EventListener annotation doesn't have the same behavior : the RunTimeException is thrown as expected.
For example I would like to avoid write the try catch to be able to know about the error :
#Component
public class PairingEventListener {
...
#TransactionalEventListener
#Transactional
public void onPairingSuccessEvent(PairingSuccessEvent event) {
try {
// some code here that could throws runtime error
} catch (Exception e) {
logger.error(e);
}
}
}
Does anyone know if there a way to achieve something equivalent to JmsErrorHandler but with Spring ApplicationEvent?
#Autowired
DefaultJmsListenerContainerFactory jmsListenerContainerFactory;
...
jmsListenerContainerFactory.setErrorHandler(new JmsErrorHandler());
Thanks !
I've built a spring mvc application using the controller->service->dao architecture. The DAO objects are using hibernate. The services are annotated #Transactional.
I'm trying to catch dao exceptions in the service, wrap them up and then throw them to my controller:
Service
#Override
public Entity createEntity(Entity ent) throws ServiceException {
try {
return entityDAO.createEntity(ent);
} catch (DataAccessException dae) {
LOG.error("Unable to create entity", dae);
throw new ServiceException("We were unable to create the entity for the moment. Please try again later.", dae);
}
}
Controller
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String createEntity(#ModelAttribute(value = "newEntity") Entity newEntity, RedirectAttributes redirectAttributes) {
try {
entityService.createEntity(newEntity);
} catch (ServiceException se) {
redirectAttributes.addFlashAttribute("error", se.getMessage());
}
}
return "redirect:/entity/manage";
}
However, even though the DataAccessException is caught at the service level, it keeps bubbling up to my controller somehow.
If for example I don't meet a unique field criteria on the database level I get an HTTP Error 500 with the following:
org.hibernate.AssertionFailure: null id in com.garmin.pto.domain.Entity entry (don't flush the Session after an exception occurs)
Code is caching DataAccessException not HibernateException, try caching HibernateException
Is there a way to translate HibernateException to something else, then DataAccessException in sping
If you want to handle the exception in the Controller, don't catch it in the Service.
Service
#Override
public Entity createEntity(Entity ent) throws DataAccessException {
return entityDAO.createEntity(ent);
}
Controller
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String createEntity(#ModelAttribute(value = "newEntity") Entity newEntity, RedirectAttributes redirectAttributes) {
try {
entityService.createEntity(newEntity);
} catch (DataAccessException e) {
redirectAttributes.addFlashAttribute("error", e.getMessage());
}
return "redirect:/entity/manage";
}
Or if you want to leverage Spring the handle the exception, use ExceptionHandler annotation. You can find good tutorial online, for example, Spring MVC #ExceptionHandler Example.
To make exception translation working
You have to annotate your DAO with #Repository
Make sure you have declared this bean <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
Here is a beautiful post on different ways to handle exceptions on a Spring MVC project.
Among those, I find using #ControllerAdvice classes, to handle all the exceptions at one place globally, the most convenient in general. Spring Lemon's source code could serve as a good concrete example.