Spring MVC handling exceptions - spring

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.

Related

Rollback does not work using MongoTransactionManager

Hello I am developping a back-end using Spring boot and MongoDB 4.0. In order to add transactions I have implemented the MongoTransactionManager as seen in the documentations spring mongo transactions
#Bean
MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
But when I annotate a method with #Transactional(rollbackFor = NullPointerException.class) it does not roll back for this exception.
For example the following test does not work.
Do you have any advices to fix this issue please ?
#Test
#Transactional(rollbackFor = NullPointerException.class)
public void testTransaction() {
try {
myRepo.deleteAll();
throw new NullPointerException();
} catch (
NullPointerException e) {
} finally {
assertThat(myRepo.findAll()).isNotEmpty();
}
}
Just figure it out that MongoTransactionManager does not work if you also register a Bean MongoTemplate.
Moreover, surprinsingly, #Transactional method does not work if it is a #Test method. You must extract the #Transactional method in a #Service.
Because you just caught your NPE and didn't do anything with it. For transaction being rollbacked your method should throw NPE out of the method.

#RestControllerAdvice and #ControllerAdvice together

I have an Spring MVC application which has #Controller s and #RestController s.
I was thinking that: When I have some Exception at my #Controller, It gonna be handled by my #ControllerAdvice and when I have some Exception at my #RestController, It gonna be handled by my #RestControllerAdvice... But now I think It's not how things should work, because my #ControllerAdvice are catching everything, even any exception that is thrown by #RestController...I do not know if this should happen. Here my code:
#ControllerAdvice
public class ExceptionHandlerController {
private final String DEFAULT_ERROR_VIEW = "error/default";
#ExceptionHandler(Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e)
{
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("danger", e.getMessage());
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
#RestControllerAdvice
public class ExceptionHandlerRestController {
#ExceptionHandler(Exception.class)
public ResponseEntity<String> defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
return new ResponseEntity<>(" test "+e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Yeah #RestControllerAdvice doesn't work that way. It's just a #ControllerAdvice with #ResponseBody automatically assumed. See #RestControllerAdvice vs #ControllerAdvice.
If you wanted one #ControllerAdvice to work with one controller and one to work with the other then if you put your controllers in separate packages you should be able to do this by doing:
#ControllerAdvice("my.controller1.package")
However, the whole point of #ControllerAdvice is to share common functionality of your separate #Controllers between each other so if you want them to do separate things you might be better off just putting the methods inside the #Controllers themselves.
If you want #RestControllerAdvice to handle only exceptions thrown from #RestController, then you can qualify it with the annotations attribute:
#RestControllerAdvice(annotations = RestController.class)
You may need #Order tag if you happen to have several other #ControllerAdvice.

Spring 4 mvc global exception Handling

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());
}

Exception handling for spring autowired components

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.

Handling addtional exceptions and throwing custom error message in Spring security

I am using Spring 2.5 .I want to do the following:
In the overriden loadUserByUsername method of spring UserDetailsService I want to throw a custom exception and on that exception give a custom error message on the login page.
Could anyone suggest how can I handle this custom exception.
You can extend AbstractHandlerExceptionResolver and handle your exception in this class. Code is not full, but you can understand the idea from that part.
public class ExceptionResolver extends AbstractHandlerExceptionResolver {
#Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mav;
if(ex instanceof YourException){
mav = loginController.showLoginPage();
mav.addObject("errorMessage","Some error message text.");
\\also you can change response code here or add some logic
}
else {
\\some another page
}
return mav;
}
Also add this to applicationContext.xml(or another file with your spring context configuration):
<bean class="com.mycompany.ExceptionResolver" />

Resources