[jboss-user] [jBPM] - Drools Spring Integration, errors in DroolsSpringTransactionManager

John Bize do-not-reply at jboss.com
Fri Aug 3 09:41:25 EDT 2012


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]

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/jboss-user/attachments/20120803/1d148b4c/attachment-0001.html 


More information about the jboss-user mailing list