spring transaction at service layer - spring

In our application we are applying spring declarative transactions using annotations at service layer.
Here i am not getting any idea on how to handle exceptions properly.
What exactly my requirement is when dao layer throws any hibernate exception we are rolling back the transaction, but in one case i am getting InvalidDataAccessResourceUsageException because there is a unique index violation happening. So what i would like to do here is i want to catch InvalidDataAccessResourceUsageException exception at service class and have to rethrow the application specific exception to controller.
But whats happening here is as we have transaction demarcation at service layer class the session is flushing at service layer(ie when tx commits) after executing the method, as a result i cant catch it into the same method and it is directly propagating to the controller.
Please suggest me work around on this.
also seeking one more clarification, suppose i have a method like below
#Transactional(value="transactionManager",readOnly = false, propagation = Propagation.REQUIRED,rollbackFor = HibernateException.class)
public SomeDTO editObject(SomeDTO someDto, String user) throws EditException {
try{
/*
call to dao.edit();
another call to anotherDao.addEditsTOAnotherTable();
some business logic*/
} catch(HibernateException e){
} catch(InvalidDataAccessResourceUsageException ie){}
}
Can i catch exceptions as above. Note: I am not handling or throwing any exceptions from dao. Also there is no session cache mechanisms like FlushMode.ALWAYS etc at dao layer as it will flush during tx.commit().

By default #Transactional will rollback for any RuntimeException, and since HibernateException is a RuntimeException , roll back will be done automaticaly and you don't have to add rollbackFor = HibernateException.class
You can handle Exception this way:
try{
}catch(InvalidDataAccessResourceUsageException e){
throw new YourApplicationExceptionNotUniqueIndex();
}
and :
YourApplicationExceptionNotUniqueIndex shoud extends RuntimeException that way you wil have a rollback at your sevice layer and you can catch the exception at your Controller .

Better to handle check all the database constraints before editing into database

Related

Spring #ControllerAdvice/ #ExceptionHandler not working

My Spring Boot Service will do a job and exit after success with 0 (there is no restcontroller), but i want it aslo to exit on every exception so i added #ControllerAdvice on a class and put this method:
#ControllerAdvice
#RequiredArgsConstructor
#Slf4j
public class ImportInekData {
final InekService inekService;
final ImportDataService dataService;
public void doTheJob(){
log.info("Fetching new list from Inek.");
UpdatedList updatedList = inekService.getUpdatedList();
List<Standort> toBeUpdated = updatedList.getToBeUpdated();
List<String> toBeDeleted = updatedList.getToBeDeleted();
log.info("List fetched with " + toBeUpdated.size() + " valid entries to be updated and " + toBeDeleted.size() + " entries marked for deletion. ");
log.info("Pushing to DB...");
dataService.importAll(toBeUpdated);
}
#EventListener
public void onStart(ContextStartedEvent start){
log.info("Application started.");
doTheJob();
log.info("Import finished.");
SpringApplication.exit(start.getApplicationContext(), () -> 0);
}
#ExceptionHandler(value = Exception.class)
public String outOnException(Exception e){
log.error("Exception occurred see logs. Stopping..");
SpringApplication.exit(context, () -> -1);
return "dying";
}
}
All is working fine but when i throw an IllegalArgumentException the #ExceptionHandler method is not called. First i had a void method with no parameter and then i began trying with String return and at least one parameter - that is not needed.
How get this working? Is there a better way for my case to react on every exception?
Controller Advices in spring is a mechanism intended to properly handle the Exceptions at the level of spring MVC.
Spring MVC in a nutshell is a web framework, and as such, it assumes that you have some kind of web endpoint that is called by the end user or maybe frontend. This endpoint is an "entry-point" to your backend code that can have services, query the database, and so forth. If during this backend flow the exception is thrown in general you don't want that the web endpoint will return 500 internal server error, so spring provides tooling for convenient mapping of these exceptions: translating them to json with a "good-looking" message, with correct HTTP code, and so forth.
If you don't have any controllers, then the whole concept of controller advices is not applicable in your flow, so there is no point in using it...
Now the real question is what exactly do you want to achieve with this exception handling?
If Application context cannot start usually spring boot application will be closed gracefully...
If you want to close the application programmatically, make sure you've read this thread

Java Spring persist stack trace when 500 error happens

I am hosting an Spring boot application on Amazon EC2. Sometimes in the morning when I go to the webpage I will see
"Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin transaction failed:"
from the browser. However I have no way to get back the stack trace. So is this possible in Spring: When 500 error happens, spring will catch the exception and store it in database or local file so I can get it back later. I think it would be helpful for debug the hard-to-reproduce 500 errors.
Yes it is possible you just need configure your controllers to use Spring exception handling https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
Then you can configure which level of exceptions you want to catch(in your case general Exception would be ok, or if you know the specific exception much better)
// Total control - setup a model and return the view name yourself. Or consider
// subclassing ExceptionHandlerExceptionResolver (see below).
#ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception exception) {
logger.error("Request: " + req.getRequestURL() + " raised " + exception);
//Here you can persist the exception or just write in the log
ModelAndView mav = new ModelAndView();
mav.addObject("exception", exception);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}

How to handle session expired exception in Spring MVC-Spring Security app for GWT RPC calls

I have Spring MVC application where security is handled by Spring Security.
UI is built using GWT which gets the data from server using RPC approach.
I need to handle on UI the situation when session is expired:
For example RPC AsyncCallback can get SessionExpiredException type of exception and popup the window with message like "You session is expired, please click the refresh link" or something.
Did someone deal with such problem?
Thanks.
I suppose that for processing of incoming GWT call you use some Spring MVC controller or some servlet. It can have following logic
try{
// decode payload from GWT call
com.google.gwt.user.server.rpc.RPC.decodeRequest(...)
// get spring bean responsible for actual business logic
Object bean = applicationContext.getBean(beanName);
// execute business logic and encode response
return RPC.invokeAndEncodeResponse(bean, ….)
} catch (com.google.gwt.user.server.rpc.UnexpectedException ex) {
// send unexpected exception to client
return RPC.encodeResponseForFailure(..., new MyCustomUnexpectedException(), …) ;
}
Solution for this case
HttpServletRequest request = getRequest() ;
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
return RPC.encodeResponseForFailure(..., new MyCustomSessionExpiredException(), …) ;
} else {
// first code snippet goes here
}
Then catch custom session expired exception in a client side code. If you do not use RPC directly then provide more details about your bridge implementation between GWT and Spring.
You will need also force GWT compiler to include MyCustomSessionExpiredException type to a serialization white list (to prevent case when GWT security policy stops propogation of the exception to client side). Solution: include MyCustomSessionExpiredException type to each method signature of each synchronous interface:
#RemoteServiceRelativePath("productRpcService.rpc")
public interface ProductRpcService extends RemoteService {
List<Product> getAllProducts() throws ApplicationException;
void removeProduct(Product product) throws ApplicationException;
}
MyCustomSessionExpiredException extends ApplicationException
Then show pop-up in client side code:
public class ApplicationUncaughtExceptionHandler implements GWT.UncaughtExceptionHandler {
#Override
public void onUncaughtException(Throwable caught) {
if (caught instanceof MyCustomSessionExpiredException) {
Window.alert("Session expired");
}
}
}
// Inside of EntryPoint.onModuleLoad method
GWT.setUncaughtExceptionHandler(new ApplicationUncaughtExceptionHandler());
I researched a bit and uploaded the solution here http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed.
Use mvn jetty:run-war to see the demo after checking it out and go to rpc-security-sample/index.htm
There are two ways to solve it.
The first is around to pass the delegate proxy for GWT RemoteServlet which throws SessionExpiredException during method invocation. This requires to declare Exception in every RPC service method. Example: http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed
Steps:
Develop new filter which intercepts first
Declare SessionExpiredException in each RPC method service which could inherit RuntimeException for simplicity (no need to follow this in implementers)
Develop parent generic AsyncCallback handler
Use http://code.google.com/p/gspring/ solution to handle all incoming RCP requests.
The second which is much more simplest: return the 401 HTTP error and handle in UI side (GWT native general exception contains the HTTP status number). Example: http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired-401
The second approach is simplest and does not require declaring Exception in service methods contract. However following the first approach can give you some flexibility: it could contain some additional info like last login time (for SessionExpiredException) etc. Also the second approach can introduce new exceptions which are inherited from SecurityException like blacklisted user (for example if user was blacklisted during his session) or for example if user does the same actions very often like a robot (it could be asked for passing the captcha) etc.

No rollback for ConstraintViolationException in transactional service

I've a service method called add() which is annotated with #Transactional.
I call it but when a ConstraintViolationException occurs inside corresponding DAO method it'll rollback the transaction even when I specify not to.
I expect that ConstraintViolationException to be caught and instead of it NotFoundException checked exception would be thrown.
#Override
#Transactional(noRollbackFor = ConstraintViolationException.class)
public User add(User user) throws NotFoundException {
try {
result = userDao.add(user);
} catch (RuntimeException e) {
throw new NotFoundException("Couldn't find group");
}
}
Is there a way to catch ConstraintViolationException without transaction rollback?
I'm using spring 3.1.1 and hibernate 3.6.
Ah, I see what happens. The ConstraintViolationException happens at commit time, after the method has been executed, when the transaction interceptor around your add() method tries to commit the transaction. Since it can't commit, obviously, the transaction is rollbacked. It can't to anything else.

Linking Server Side Message/Exception with AJAX request

I am making an ajax submit using Primefaces but I am having trouble linking my server side message with my ajax request.
Supposed I have this button that calls an action.
In my managed bean, do I need to raise an exception? How do I pass this message into my ajax request
public void checkout(ActionEvent event){
if(expression){
throw new CustomException("Account balance is not enough!");
}
}
public class CustomException extends RuntimeException {
public CustomException(String message) {
super(message);
}
}
How do I handle this case? Will my onerror javascript method be able to handle this?
Also, in one case supposed DB is down then how do I handle the exception? Do I have accessed to the error message
in my javascript function?
public void checkout(ActionEvent event){
try{
//DB is down
if(expression){
throw new CustomException("Account balance is not enough!");
}
}catch(Exception e){
}
}
As to your concrete question, you need to implement a custom ExceptionHandler for this which does basically the following when an exception occurs in an ajax request:
String errorPageLocation = "/WEB-INF/errorpages/500.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
This is not exactly trivial if you want to take web.xml error pages into account. You'd need to parse the entire web.xml for this to find the error page locations. The OmniFaces utility library has exactly such an exception handler, the FullAjaxExceptionHandler. You can find the full source code here and the showcase example here.
As to your concrete functional requirement, I wouldn't throw an exception when there's just an user error. This is fully recoverable. You need to create and add a FacesMessage and have ajax to update the <h:messages>, <p:messages> or <p:growl>. The PrimeFaces ones support an autoUpdate="true" which would auto-update itself on ajax requests. E.g.
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Insufficient balance", null));
with
<p:messages autoUpdate="true" />
Throwing an exception makes only sense in unrecoverable situations like as when the DB is down. Note that you usually don't throw such an exception yourself. In case of JPA it would already be thrown as PersistenceException which you in turn shouldn't catch in JSF managed bean, but just let it go.

Resources