Details
-
Bug
-
Resolution: Done
-
Major
-
5.2.0.M1
-
None
Description
This problem concerns the drools-persistence-jpa module and its org.drools.persistence.jta.JtaTransactionManager.
The JtaTransactionManager has a notion of local transactions. These are the JTA transactions that it itself started using UserTransaction.begin().
If a JTA transaction is already active when JtaTransactionManager.begin() is called, then the execution continues in this JTA transaction and a new one is not started. Let's call such JTA transaction non-local.
The problem is that JtaTransactionManager.rollback() treats local and non-local JTA transactions the same way and calls UserTransaction.rollback() in both cases. In case of a non-local transaction is this behaviour IMHO wrong, because the rollback should be a responsibility of the same client that started the JTA transaction.
Imagine the following situation (jBPM process execution):
StatefulKnowledgeSession session = ... UserTransaction ut = ... ut.begin(); try { session.startProcess("my.process"); // the process execution may cause an exception (most likely in some WorkItemHandler) ut.commit(); } catch (Exception e) { ut.rollback(); // now this throws an IllegalStateException, because JtaTransactionManager already rolled back the transaction }
There is of course a workaround: the client code has to check status of the JTA transaction and must not call rollback(), if the transaction was already rolled back by the JtaTransactionManager.
IMHO the relevant JtaTransactionManager code should call UserTransaction.setRollbackOnly for non-local transactions:
public void rollback() { boolean wasLocal = localTransaction; // localTransaction is a private boolean field of JtaTransactionManager already present in current implementation localTransaction = false; try { wasLocal ? this.ut.rollback() : this.ut.setRollbackOnly(); } catch ( Exception e ) { logger.warn( "Unable to rollback transaction", e); throw new RuntimeException( "Unable to rollback transaction", e ); } }