John Bize [
https://community.jboss.org/people/jbize] created the discussion
"Drools Spring Integration, errors in DroolsSpringTransactionManager"
To view the discussion, visit:
https://community.jboss.org/message/751900#751900
--------------------------------------------------------------
I have an application that integrates jBPM, Spring, Hibernate/JPA, JPA2, and Richfaces4.
It's mostly working ok, except sometimes I am/was getting NullPointer exceptions from
the DroolsSpringTransactionManager. I'm using drools-spring 5.3.1.Final. This class
is apparently a (broken?) wrapper class around (in my case), the
org.springframework.orm.jpa.JpaTransactionManager.
Looking at the source code, I observed several things. There is an obvious bug in the
begin() method in that if the call to getStatus() returns STATUS_NO_TRANSACTION, which it
will if the wrapped tm (ptm) is null, it immediately dereferences the null ptm.
(this.ptm.getTransaction(td)). I'm not too woried about this one as it seems rather
unlikely.
What's happening for me (in the getStatus() method) is that it's making the call
"transaction = this.ptm.getTransaction(td);" which is occasionally throwing an
"IllegalStateException: Transaction already active," wrapped in an outer
RuntimeException and dropping down to the finally block. There, the still null
transaction is committed, throwing an NPE that masks the original exception. To mitigate
this, I added a catch RuntimeException and if the cause is an IllegalStateException, I
return STATUS_UNKNOWN. In the finally block, I also ensure that I don't try to commit
a null transaction.
Clearly this code is fragile, and
https://issues.jboss.org/browse/JBRULES-2791
https://issues.jboss.org/browse/JBRULES-2791 is probably correct, but *can anyone suggest
why I'm sometimes getting the "IllegalStateException: Transaction already
active" exceptions in the first place. I am using 4 separate persistence units (and
transaction managers), two for basic components of my application, one for the jBPM
process tables, and one for the LocalTaskManager Task tables.*
Copying the code from:
https://github.com/droolsjbpm/droolsjbpm-integration/blob/master/drools-c...
https://github.com/droolsjbpm/droolsjbpm-integration/blob/master/drools-c...,
my mitigating changes are in red (typed, not copied):
public class DroolsSpringTransactionManager
implements
TransactionManager {
Logger logger =
LoggerFactory.getLogger( getClass() );
private AbstractPlatformTransactionManager ptm;
TransactionDefinition td = new
DefaultTransactionDefinition();
TransactionStatus currentTransaction = null;
public DroolsSpringTransactionManager(AbstractPlatformTransactionManager ptm) {
this.ptm = ptm;
}
public boolean begin() {
try {
if ( getStatus() == TransactionManager.STATUS_NO_TRANSACTION ) {
// If there is no transaction then start one, we will commit within the
same Command
// it seems in spring calling getTransaction is enough to begin a new
transaction
currentTransaction = this.ptm.getTransaction( td );
return true;
} else {
return false;
}
} catch ( Exception e ) {
logger.warn( "Unable to begin transaction",
e );
throw new RuntimeException( "Unable to begin transaction",
e );
}
}
public void commit(boolean transactionOwner) {
if ( transactionOwner ) {
try {
// if we didn't begin this transaction, then do nothing
this.ptm.commit( currentTransaction );
currentTransaction = null;
} catch ( Exception e ) {
logger.warn( "Unable to commit transaction",
e );
throw new RuntimeException( "Unable to commit transaction",
e );
}
}
}
public void rollback(boolean transactionOwner) {
try {
if ( transactionOwner ) {
this.ptm.rollback( currentTransaction );
currentTransaction = null;
}
} catch ( Exception e ) {
logger.warn( "Unable to rollback transaction",
e );
throw new RuntimeException( "Unable to rollback transaction",
e );
}
}
/**
* Borrowed from Seam
*/
public int getStatus() {
if ( ptm == null ) {
return TransactionManager.STATUS_NO_TRANSACTION;
}
logger.debug( "Current TX name (According to
TransactionSynchronizationManager) : " +
TransactionSynchronizationManager.getCurrentTransactionName() );
if ( TransactionSynchronizationManager.isActualTransactionActive() ) {
TransactionStatus transaction = null;
try {
if ( currentTransaction == null ) {
transaction = ptm.getTransaction( td );
if ( transaction.isNewTransaction() ) {
return TransactionManager.STATUS_COMMITTED;
}
} else {
transaction = currentTransaction;
}
logger.debug( "Current TX: " + transaction );
// If SynchronizationManager thinks it has an active transaction but
// our transaction is a new one
// then we must be in the middle of committing
if ( transaction.isCompleted() ) {
if ( transaction.isRollbackOnly() ) {
return TransactionManager.STATUS_ROLLEDBACK;
}
return TransactionManager.STATUS_COMMITTED;
} else {
// Using the commented-out code in means that if rollback with this
manager,
// I always have to catch and check the exception
// because ROLLEDBACK can mean both "rolled back" and
"rollback only".
// if ( transaction.isRollbackOnly() ) {
// return TransactionManager.STATUS_ROLLEDBACK;
// }
return TransactionManager.STATUS_ACTIVE;
}
{color:#f00}
} catch (RuntimeException e) {
if ( e.getCause() instanceof IllegalStateException ) {
logger.debug( "IllegalStateException in getStatus()", e );
return TransactionManager.STATUS_UNKNOWN;
} else {
logger.debug( "Unexpected Exception in getStatus()", e );
throw e;
}
{color}
} finally {
if ( currentTransaction == null ) {
{color:#f00}
if (transaction != null) {
{color}
ptm.commit( transaction );
{color:#f00}
}
{color}
}
}
}
return TransactionManager.STATUS_NO_TRANSACTION;
}
public void registerTransactionSynchronization(TransactionSynchronization ts) {
TransactionSynchronizationManager.registerSynchronization( new
SpringTransactionSynchronizationAdapter( ts ) );
}
}
--------------------------------------------------------------
Reply to this message by going to Community
[
https://community.jboss.org/message/751900#751900]
Start a new discussion in jBPM at Community
[
https://community.jboss.org/choose-container!input.jspa?contentType=1&...]