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-container/drools-spring/src/main/java/org/drools/container/spring/beans/persistence/DroolsSpringTransactionManager.java
 
https://github.com/droolsjbpm/droolsjbpm-integration/blob/master/drools-container/drools-spring/src/main/java/org/drools/container/spring/beans/persistence/DroolsSpringTransactionManager.java,
 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&containerType=14&container=2034]

_______________________________________________
jboss-user mailing list
[email protected]
https://lists.jboss.org/mailman/listinfo/jboss-user

Reply via email to