org.alfresco.util.transaction.SpringAwareUserTransactionTest.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.util.transaction.SpringAwareUserTransactionTest.java

Source

/*
 * Copyright (C) 2005-2014 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package org.alfresco.util.transaction;

import java.util.NoSuchElementException;

import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.UserTransaction;

import junit.framework.TestCase;

import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;

/**
 * @see org.alfresco.util.transaction.SpringAwareUserTransaction
 * 
 * @author Derek Hulley
 */
public class SpringAwareUserTransactionTest extends TestCase {
    private DummyTransactionManager transactionManager;
    private FailingTransactionManager failingTransactionManager;
    private UserTransaction txn;

    public SpringAwareUserTransactionTest() {
        super();
    }

    @Override
    protected void setUp() throws Exception {
        transactionManager = new DummyTransactionManager();
        failingTransactionManager = new FailingTransactionManager();
        txn = getTxn();
    }

    private UserTransaction getTxn() {
        return new SpringAwareUserTransaction(transactionManager, false, TransactionDefinition.ISOLATION_DEFAULT,
                TransactionDefinition.PROPAGATION_REQUIRED, TransactionDefinition.TIMEOUT_DEFAULT);
    }

    public void testSetUp() throws Exception {
        assertNotNull(transactionManager);
        assertNotNull(txn);
    }

    private void checkNoStatusOnThread() {
        try {
            TransactionAspectSupport.currentTransactionStatus();
            fail("Spring transaction info is present outside of transaction boundaries");
        } catch (NoTransactionException e) {
            // expected
        }
    }

    public void testNoTxnStatus() throws Exception {
        checkNoStatusOnThread();
        assertEquals("Transaction status is not correct", Status.STATUS_NO_TRANSACTION, txn.getStatus());
        assertEquals("Transaction manager not set up correctly", txn.getStatus(), transactionManager.getStatus());
    }

    public void testSimpleTxnWithCommit() throws Throwable {
        testNoTxnStatus();
        try {
            txn.begin();
            assertEquals("Transaction status is not correct", Status.STATUS_ACTIVE, txn.getStatus());
            assertEquals("Transaction manager not called correctly", txn.getStatus(),
                    transactionManager.getStatus());

            txn.commit();
            assertEquals("Transaction status is not correct", Status.STATUS_COMMITTED, txn.getStatus());
            assertEquals("Transaction manager not called correctly", txn.getStatus(),
                    transactionManager.getStatus());
        } catch (Throwable e) {
            // unexpected exception - attempt a cleanup
            try {
                txn.rollback();
            } catch (Throwable ee) {
                e.printStackTrace();
            }
            throw e;
        }
        checkNoStatusOnThread();
    }

    public void testSimpleTxnWithRollback() throws Exception {
        testNoTxnStatus();
        try {
            txn.begin();

            throw new Exception("Blah");
        } catch (Throwable e) {
            txn.rollback();
        }
        assertEquals("Transaction status is not correct", Status.STATUS_ROLLEDBACK, txn.getStatus());
        assertEquals("Transaction manager not called correctly", txn.getStatus(), transactionManager.getStatus());
        checkNoStatusOnThread();
    }

    public void testNoBeginCommit() throws Exception {
        testNoTxnStatus();
        try {
            txn.commit();
            fail("Failed to detected no begin");
        } catch (IllegalStateException e) {
            // expected
        }
        checkNoStatusOnThread();
    }

    public void testPostRollbackCommitDetection() throws Exception {
        testNoTxnStatus();

        txn.begin();
        txn.rollback();
        try {
            txn.commit();
            fail("Failed to detect rolled back txn");
        } catch (RollbackException e) {
            // expected
        }
        checkNoStatusOnThread();
    }

    public void testPostSetRollbackOnlyCommitDetection() throws Exception {
        testNoTxnStatus();

        txn.begin();
        txn.setRollbackOnly();
        try {
            txn.commit();
            fail("Failed to detect set rollback");
        } catch (RollbackException e) {
            // expected
            txn.rollback();
        }
        checkNoStatusOnThread();
    }

    public void testMismatchedBeginCommit() throws Exception {
        UserTransaction txn1 = getTxn();
        UserTransaction txn2 = getTxn();

        testNoTxnStatus();

        txn1.begin();
        txn2.begin();

        txn2.commit();
        txn1.commit();

        checkNoStatusOnThread();

        txn1 = getTxn();
        txn2 = getTxn();

        txn1.begin();
        txn2.begin();

        try {
            txn1.commit();
            fail("Failure to detect mismatched transaction begin/commit");
        } catch (RuntimeException e) {
            // expected
        }
        txn2.commit();
        txn1.commit();

        checkNoStatusOnThread();
    }

    /**
     * Test for leaked transactions (no guarantee it will succeed due to reliance
     * on garbage collector), so disabled by default.
     * 
     * Also, if it succeeds, transaction call stack tracing will be enabled
     * potentially hitting the performance of all subsequent tests.
     * 
     * @throws Exception
     */
    public void xtestLeakedTransactionLogging() throws Exception {
        assertFalse(SpringAwareUserTransaction.isCallStackTraced());

        TrxThread t1 = new TrxThread();
        t1.start();
        System.gc();
        Thread.sleep(1000);

        TrxThread t2 = new TrxThread();
        t2.start();
        System.gc();
        Thread.sleep(1000);

        assertTrue(SpringAwareUserTransaction.isCallStackTraced());

        TrxThread t3 = new TrxThread();
        t3.start();
        System.gc();
        Thread.sleep(3000);
        System.gc();
        Thread.sleep(3000);
    }

    private class TrxThread extends Thread {
        public void run() {
            try {
                getTrx();
            } catch (Exception e) {
            }
        }

        public void getTrx() throws Exception {
            UserTransaction txn = getTxn();
            txn.begin();
            txn = null;
        }
    }

    public void testConnectionPoolException() throws Exception {
        testNoTxnStatus();
        txn = getFailingTxn();
        try {
            txn.begin();
            fail("ConnectionPoolException should be thrown.");
        } catch (ConnectionPoolException cpe) {
            // Expected fail
        }
    }

    private UserTransaction getFailingTxn() {
        return new SpringAwareUserTransaction(failingTransactionManager, false,
                TransactionDefinition.ISOLATION_DEFAULT, TransactionDefinition.PROPAGATION_REQUIRED,
                TransactionDefinition.TIMEOUT_DEFAULT);
    }

    /**
     * Used to check that the transaction manager is being called correctly
     * 
     * @author Derek Hulley
     */
    @SuppressWarnings("serial")
    private static class DummyTransactionManager extends AbstractPlatformTransactionManager {
        private int status = Status.STATUS_NO_TRANSACTION;
        private Object txn = new Object();

        /**
         * @return Returns one of the {@link Status Status.STATUS_XXX} constants
         */
        public int getStatus() {
            return status;
        }

        protected void doBegin(Object arg0, TransactionDefinition arg1) {
            status = Status.STATUS_ACTIVE;
        }

        protected void doCommit(DefaultTransactionStatus arg0) {
            status = Status.STATUS_COMMITTED;
        }

        protected Object doGetTransaction() {
            return txn;
        }

        protected void doRollback(DefaultTransactionStatus arg0) {
            status = Status.STATUS_ROLLEDBACK;
        }
    }

    /**
     * Throws {@link NoSuchElementException} on begin()
     * 
     * @author alex.mukha
     */
    private static class FailingTransactionManager extends AbstractPlatformTransactionManager {
        private static final long serialVersionUID = 1L;
        private int status = Status.STATUS_NO_TRANSACTION;
        private Object txn = new Object();

        /**
         * @return Returns one of the {@link Status Status.STATUS_XXX} constants
         */
        @SuppressWarnings("unused")
        public int getStatus() {
            return status;
        }

        protected void doBegin(Object arg0, TransactionDefinition arg1) {
            throw new CannotCreateTransactionException("Test exception.");
        }

        protected void doCommit(DefaultTransactionStatus arg0) {
            status = Status.STATUS_COMMITTED;
        }

        protected Object doGetTransaction() {
            return txn;
        }

        protected void doRollback(DefaultTransactionStatus arg0) {
            status = Status.STATUS_ROLLEDBACK;
        }
    }
}