Files
alfresco-community-repo/src/test/java/org/alfresco/util/transaction/SpringAwareUserTransactionTest.java
Nicolas Barithel 539dc45e6d ALF-22007 fix TransactionListeners execution order
Change-Id: I5b5359948a631f17f2030145a847b1db728617a4
2018-06-15 16:13:31 +02:00

486 lines
14 KiB
Java

/*
* 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 java.util.Objects;
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);
}
public void testTransactionListenerOrder() throws Throwable
{
testNoTxnStatus();
try
{
txn.begin();
StringBuffer buffer = new StringBuffer();
TransactionSupportUtil.bindListener(new TestTransactionListener("5x", buffer), 5);
TransactionSupportUtil.bindListener(new TestTransactionListener("0a", buffer), 0);
TransactionSupportUtil.bindListener(new TestTransactionListener("0e", buffer), 0);
TransactionSupportUtil.bindListener(new TestTransactionListener("0d", buffer), 0);
TransactionSupportUtil.bindListener(new TestTransactionListener("0b", buffer), 0);
TransactionSupportUtil.bindListener(new TestTransactionListener("0c", buffer), 0);
TransactionSupportUtil.bindListener(new TestTransactionListener("4x", buffer), 4);
TransactionSupportUtil.bindListener(new TestTransactionListener("1x", buffer), -1);
TransactionSupportUtil.bindListener(new TestTransactionListener("3a", buffer), 3);
TransactionSupportUtil.bindListener(new TestTransactionListener("3e", buffer), 3);
TransactionSupportUtil.bindListener(new TestTransactionListener("3d", buffer), 3);
TransactionSupportUtil.bindListener(new TestTransactionListener("3b", buffer), 3);
TransactionSupportUtil.bindListener(new TestTransactionListener("3c", buffer), 3);
TransactionSupportUtil.bindListener(new TestTransactionListener("2x", buffer), -2);
txn.commit();
assertEquals("0a0e0d0b0c2x1x3a3e3d3b3c4x5x", buffer.toString());
}
catch (Exception e)
{
try
{
txn.rollback();
}
catch (Exception ee)
{
e.addSuppressed(ee);
}
throw e;
}
checkNoStatusOnThread();
}
private static class TestTransactionListener extends TransactionListenerAdapter
{
private final String name;
private final StringBuffer buffer;
public TestTransactionListener(String name, StringBuffer buffer)
{
Objects.requireNonNull(name);
Objects.requireNonNull(buffer);
this.name = name;
this.buffer = buffer;
}
@Override
public void beforeCommit(boolean readOnly)
{
buffer.append(name);
}
public String getName()
{
return name;
}
@Override
public boolean equals(Object obj)
{
if (obj instanceof TestTransactionListener)
{
return name.equals(((TestTransactionListener) obj).getName());
}
return false;
}
@Override
public int hashCode()
{
return name.hashCode();
}
}
/**
* 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;
}
}
}