add action in transaction rollback - java

I have a problem running some code right before a transaction rollback in my application. What I want is to have a rollback when an exception occurs but I also want to store some information in a table about the application's state when that exception happened including any errors or stacktrace.
Here is the code I have:
public void performAction(String approverId, Document document, String action) {
try {
LOG.info(String.format("routing document %s %s %s", approverId, document.getDocumentId(), action));
getDocumentService().route(approverId, document, action);
} catch (Exception e) {
LOG.error(String.format("error routing document %s %s %s", approverId, document.getDocumentId(), action));
LOG.error(e, e);
saveException(document, action, e); //this is what I want
}
}
The saveException() method simply creates an objects and saves it to a table.
Now according to Spring documentation about transactions, This rollback happens by default where the exception is a runtime exception and I have confirmed that the rollback works correctly but it's somehow not allowing my code to run and save the information I need or rolling that one back too (?).
Any help or hints to a solution is appreciated.

This is not an uncommon use case: the transaction failed and we want to roll it back, but we would still like to update some monitoring database table with the error cause.
For doing this in a Spring application use REQUIRES_NEW propagation of the #Transactional annotation.
For doing this, create an application state tracking service, and annotate it with REQUIRES_NEW transaction propagation.
Each method in the state tracking service will run in it's own separate transaction, so when the transaction on the main business method rolls back, the state tracking information will still be available on the database.

Related

How to partially rollback data in spring boot, errors to be persited

I have sequence of table data creation in transaction, using springboot, java, jpa and hibernate sequence shown below. Database used is Oracle
Place order - insert into order details_table
Process Payment - insert into payment_info table
Error Message(info, warning, error) - insert into error_message table
I am inserting error messages in error_message table if any error occurs(info, warning, error).
My info and warning message get persisted into error_message if no error occurs during the processing of the request.
But if there is an error, I am throwing an error from code to rollback the transaction, its working but my error_message also get rollback.
I want to rollback details_table and payment_info but not the error_message, I want them to be persisted.
How I can achieve the same?
Use two different transactions.
The default transaction propagation is REQUIRED, when calling your #Transactional annotated method without a transactional context, a new transaction will be started but if there's already a transaction in place, the transaction will be joined, ending up with a single transaction. In case of rollback, all operations will be rollbacked as they belong to the same transaction.
Change the transaction propagation to REQUIRES_NEW in order to always force starting a new transaction (instead of joining it if there's one). This will allow you to commit the error messages transaction independently from the payment info.
#Transactional
public void placeOrder(Order oder) {
// process order
paymentGateway.processPayment();
// save order <- an exception here will rollback the current
// transaction but not the one in PaymentGateway which is independent
}
// In PaymentGateway
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment() {
// process payment
// insert error messages
}
Further reading: Understanding PROPAGATION_REQUIRED. Make sure you also understand how exceptions affect transactions.

How to make some actions when timeout was reached on transaction

I am maintaining code which looks like that
#Asynchronous
#TransactionTimeout(value = 1, unit = TimeUnit.HOUR)
public void downloadFile(Long fileId) {
//This method takes more than 1hour
service.download(fileId)
//this method should be called even when download finished with error
service.fileDownloadedFinishedNotification(fileId);
}
This is just an example code, to the fileDownloadedFinished we are passing message which we want to display etc, and inside of that we want to mark process as finished with error/success.
So as you can see on download we can get timeout, and after that the fileDownloadedFinishedNotification wont be called, because transaction failed because of the timeout.
I was thinking about extracting notification to other method and call it like this:
#Asynchronous
#TransactionTimeout(value = 1, unit = TimeUnit.HOUR)
public Future<String> downloadFile(Long fileId) {
//This method takes more than 1hour
service.download(fileId)
return new AsyncResult<String>("Test");
}
public void example(){
long id = 15;
String msg = "default stuff";
try {
msg = downloadFile(id).get();
}
catch (Exception e) {
e.printStackTrace();
}
service.fileDownloadedFinishedNotification(fileId, string);
}
But I am not sure if it is good idea, or maybe there is some other functionality, which I can call when timeout is reaced. Something like onTimeout.
Some considerations :
There is no simple way to handle transaction timeout with a listener AFAIK
Annotations use dynamic proxies under the cover, they won't so be applied on a inner call, you have to call your downloadFile from outside (on a bean injected in your caller).
The current transaction will be aborted when fileDownloadedFinishedNotification will be called and so all operations on a transacted resource (DB, etc...) will be rolled back (you may have to invoke the method within a dedicated transaction (e.g. annotate your method with #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW).
Assuming the download method retrieves the content across the network and unless you access this last through a dedicated JCA adapter, no exception will be thrown on transaction timeout, the Transaction reaper only marks the current transaction as aborted and release related resources but does not interrupt the thread, only a subsequent access to a MANAGED resource (Datasource, JMS, etc) will throw an exception.
Regarding the last point, while interacting with a un-managed resource the only way to know if the current transaction is still active is to regularly check its state using EJBContext.getRollbackOnly() or by making a dummy access to any managed resource.
There are ways of achieving what you want but a proper implementation would need more information about your level of access to application change.
There are many places where transaction propagation is explained but giving you are running your app in an EJB containeer I would start from here:
https://docs.oracle.com/javaee/6/tutorial/doc/bncih.html
I would read it all chapter but most specific for your case is the container managed transactions here:
https://docs.oracle.com/javaee/6/tutorial/doc/bncij.html
Now assuming you have full access and you can change your database structure the way I would implemented this would be:
You are running your service in a parent transaction T1
Before you invoking the download method call another service to record the download started and the maximum expected time to finish. Do this in a REQUIRED_NEW transaction. This no time consuming database interaction will run in an autonomous transaction T2
Once the above T2 transaction commits your download started record is committed and available to query
Once back in the parent T1 start your download
Record the success in the same record you persisted in T2 if the download successfully finishes.
If you get a timeout the above will never be recorded and the database will still show the download as started and maximum expected time to finish
Define a monitoring process that would kick off at regular times and check the download status. If the expected time to finish have been passed over have that monitoring process alert or record failure or trigger another retry or whatever your business rules are
Hope it helped. Sorry for not codding examples but I thing you will have enough to start with.
Cheers

Spring transaction management

I am working on spring4 mvc to introduce in our new web application and currently we are using struts1.x and wanted to use new framework for support html5/ajax request as simple as possible and wanted to use the power of DI and spring webflow support.
Currently in our struts1.x application db transaction management is done at our custom GenericAction which is subclass of Action, In GenericAction we are getting the connection from data source and handover to the subclasses then any exception raised then caught and rollback, otherwise commit such that database atomicity (transaction management )is done at one place. All the module action classes should extends GenericAction such that database connection will be available and performs the module related stuff and after completing connection will be either rollback or commit in GenericAction as said above.
In spring, scope of the Transaction is started with #Transactional annotation and then ends with a method in Service Class since the service class marked as #Transactional. This is not feasible solution for me. I have read several documents related to the spring transaction before starting and below are my questions.
I am using HibernateTransactionManager as transaction manager
Transaction should start from interceptors in case web request or any class (in case of unit testing).
Transaction should ends with the after interceptor is executed in case of web request or any class in case of unit testing.
In case of any exception raised then our HandlerExceptionResolverImpl handler will execute then connection should be rollback.
Any workaround or best practices would be greatly appreciate.
Thanks
Dhorrairaajj
In my opinion, you can achieve this with minimal change on the current application by following what Spring does in its org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(Object, TransactionDefinition). In detail, I think something like following should work:
On the GenericAction, get connection from data source (as you've already done)
Bind the connection via its holder with the current data source:
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
Eventually, commit the transaction as org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DefaultTransactionStatus).
In any case, Spring's http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/support/TransactionTemplate.html can help to achieve any programmatic transactional scenario that you want. Just need to ensure that the right transactional resource is bound to the opening transaction.
Hope this helps.
Thanks for you reply. Your post/suggestion drives to do the following
I written an interceptor called TxtManager and below are methods to perform the transaction management.
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception
{
Session session = sessionFactory.openSession();
SessionHolder hold = new SessionHolder(
session);
TransactionSynchronizationManager.bindResource(sessionFactory, hold);
// passing null would accept the default transaction definition.
status = transactionManager.getTransaction(null);
return true;
}
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception
{
Exception hanlderEx = (Exception) request
.getAttribute(HandlerExceptionResolverImpl.EXCEPTION_KEY);
if (hanlderEx != null)
{
transactionManager.rollback(status);
}
else
{
transactionManager.commit(status);
}
}
In HandlerExceptionResolverImpl class which responsible for handling things upon exception, put an exception object in request and read the same exception to rollback or commit in afterCompletion method of the interceptor.
In between(preHandler and afterCompletion) we will going to use the standard practices to perform the module related stuff including any other interceptos. Unit testing may not be supported, i will check other alternative for unit testing.
Finally we were able to simulate the existing frame work with your suggestion Thanks
Any suggestion to improves this would be much appreciate !!..
Thanks
Dhorrairaajj

Force Hibernate To Save A Specific POJO

I have some code calling a webservice and it returns an ID. I am saving this ID in the database using hibernate. I have a filter that opens the session and commits it, rolling back when any exception occurs within the contained code. Since there is no way to get back the ID returned by the webservice I would like to save this in the database EVEN if there is an exception that occurred later on in the code. Is there anyway to do this using the same session?
It depends on who throws the exception. If it is thrown by the hibernate session object you cannot reuse the session anymore as stated in hibernate Session documentation:
If the Session throws an exception, the transaction must be rolled back and the session discarded. The internal state of the Session might not be consistent with the database after the exception occurs.
If the exception is thrown by some other code, then yes, you can do something like this:
Long yourIdFromWebservice = ...
try {
// do some processing that might result in an exception
}
catch(//the interesting exception, but not HibernateException) {
//maybe log it, rethrow it
}
finally {
session.save() //save what needed to be saved
}
or depending on your use case:
try {
// do some processing that might result in an exception
session.save() //save all stuff if no exception
}
catch(//the interesting exception, but not HibernateException) {
session.save() //save only the id from the webservice
}
The right way to do that is to use two transactions: one to save the ID, and one to save the rest of your work.
If you're using EJB, you could do the save of the ID in a bean of its own, and annotate it with a TransactionAttribute of REQUIRES_NEW.
Otherwise, you can look at what control over transactions your environment gives you, and figure out a way to have two of them.

EJB3 - handling RollBackExceptions

I have an EJB3 application which consists of some EJB's for accessing a DB, and exposed via a Session Bean as a web service.
Now there are two things I need to find out:
1) Is there any way I can stop SQL exceptions from causing the web service from throwing a SOAP Fault? The transactions are handled by the container, and currently sql exceptions cause a RollBackException to be thrown, and consequently the transaction to be rolled back (desired behaviour) and the web service to throw a fault (not desired).
2) I wish to extend the webservice to be able to take in a list of entities, and the session bean to persist each. However, I want each entity to be executed in its own transaction, so that if one fails the others are not affected (and again the web service should not fault).
For (1) I have tried to catch the RollBackException, but I assume this is thrown somewhere on another thread, as the catch block is never reached. I assume for (2) I will need to look into User Transactions, but firstly would prefer the container to manage this, and secondly do not know how to force the use of user transactions.
Thanks.
no, you can do all this with container managed transactions (and this is definitely preferable, as managing transactions is a pain).
the gist of the solution is to create a second EJB with a local interface only and the transaction semantics you desire. then your "public" ejb, which the web-service is calling directly, calls into this second ejb via its local interface to do the actual work.
something along the lines of:
public class MyPublicEjb {
#EJB
private MyPrivateImpl impl;
public void doSomething() {
try {
impl.doSomething();
} catch(TXRolledBack) {
// handle rollback ...
}
}
}
I know this looks sort of ugly, but trust me, this is far preferable to directly manipulating transactions.
For (1): Debug your code to find out where the exception is being thrown and what is causing it. Then handle the exception there.
For (2): Wrap each instance with beginTransaction() and commit().
for(each Entity){
try{
//begin transaction
//save entity
//commit
} catch(Exception e) {
//handle Exception, but continue on
}
}

Categories