package org.springmodules.javaspaces.tx; import java.rmi.RemoteException; import junit.framework.TestCase; import net.jini.core.entry.Entry; import net.jini.core.entry.UnusableEntryException; import net.jini.core.lease.Lease; import net.jini.core.transaction.CannotAbortException; import net.jini.core.transaction.CannotCommitException; import net.jini.core.transaction.TimeoutExpiredException; import net.jini.core.transaction.Transaction; import net.jini.core.transaction.TransactionException; import net.jini.core.transaction.UnknownTransactionException; import net.jini.core.transaction.Transaction.Created; import net.jini.core.transaction.server.TransactionManager; import net.jini.space.JavaSpace; import org.easymock.MockControl; import org.springframework.transaction.InvalidIsolationLevelException; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; import org.springmodules.javaspaces.JavaSpaceCallback; import org.springmodules.javaspaces.JavaSpaceTemplate; import org.springmodules.transaction.jini.JiniTransactionManager; import org.springmodules.transaction.jini.JiniTransactionManager.JiniHolder; public class TransactionTests extends TestCase { private TransactionManager tm; private MockControl tmCtrl; private JiniTransactionManager jiniTM; private JavaSpace space; private MockControl spaceCtrl; private net.jini.core.transaction.server.TransactionManager.Created txCreated; private MockControl leaseCtrl; private Lease lease; private MockControl txCtrl; private Transaction tx; private long txId; private JavaSpaceTemplate spaceTemplate; protected void setUp() throws Exception { // mocks tmCtrl = MockControl.createControl(TransactionManager.class); tm = (TransactionManager) tmCtrl.getMock(); txCtrl = MockControl.createControl(Transaction.class); tx = (Transaction) txCtrl.getMock(); leaseCtrl = MockControl.createControl(Lease.class); lease = (Lease) leaseCtrl.getMock(); txId = 1; txCreated = new net.jini.core.transaction.server.TransactionManager.Created(txId, lease); spaceCtrl = MockControl.createControl(JavaSpace.class); space = (JavaSpace) spaceCtrl.getMock(); jiniTM = new JiniTransactionManager(tm, space); spaceTemplate = new JavaSpaceTemplate(space); } protected void tearDown() throws Exception { // super.tearDown(); mockVerify(); space = null; spaceCtrl = null; jiniTM = null; tm = null; tmCtrl = null; spaceTemplate = null; } private void mockReplay() { tmCtrl.replay(); spaceCtrl.replay(); leaseCtrl.replay(); txCtrl.replay(); } private void mockVerify() { tmCtrl.verify(); spaceCtrl.verify(); leaseCtrl.verify(); txCtrl.verify(); } private void assertSynchronizationManager() { assertTrue("Hasn't thread session", !TransactionSynchronizationManager .hasResource(space)); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); } public void testTransactionCommit() throws Exception { // tx begin tmCtrl.expectAndReturn(tm.create(-1), txCreated); // tx commit tm.commit(txId); final Entry entry = new Entry() { }; // dummy mock Transaction tx = new Transaction() { public void abort() throws UnknownTransactionException, CannotAbortException, RemoteException { } public void abort(long arg0) throws UnknownTransactionException, CannotAbortException, TimeoutExpiredException, RemoteException { } public void commit() throws UnknownTransactionException, CannotCommitException, RemoteException { } public void commit(long arg0) throws UnknownTransactionException, CannotCommitException, TimeoutExpiredException, RemoteException { } }; // space play space.write(entry, tx, Lease.FOREVER); spaceCtrl.setMatcher(MockControl.ALWAYS_MATCHER); spaceCtrl.setReturnValue(lease); mockReplay(); TransactionTemplate tt = new TransactionTemplate(jiniTM); assertSynchronizationManager(); tt.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); spaceTemplate.write(entry, Lease.FOREVER); } }); assertSynchronizationManager(); } public void testTransactionRollback() throws Exception { tmCtrl.expectAndReturn(tm.create(-1), txCreated); // tx abort tm.abort(txId); mockReplay(); TransactionTemplate tt = new TransactionTemplate(jiniTM); assertSynchronizationManager(); final RuntimeException testException = new RuntimeException(); try { tt.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult( TransactionStatus status) { assertTrue("Has thread session", TransactionSynchronizationManager .hasResource(space)); throw testException; } }); } catch (RuntimeException re) { assertSame(testException, re); } assertSynchronizationManager(); } public void testTransactionRollbackOnly() throws Exception { tmCtrl.expectAndReturn(tm.create(-1), txCreated); // tx abort tm.abort(txId); final Entry entry = new Entry() { }; spaceCtrl.expectAndReturn(space.write(entry, null, Lease.FOREVER), lease); mockReplay(); TransactionTemplate tt = new TransactionTemplate(jiniTM); assertSynchronizationManager(); tt.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); spaceTemplate.execute(new JavaSpaceCallback() { public Object doInSpace(JavaSpace js, Transaction transaction) throws RemoteException, TransactionException, UnusableEntryException, InterruptedException { assertSame(lease, js.write(entry, null, Lease.FOREVER)); return null; } }); status.setRollbackOnly(); } }); assertSynchronizationManager(); } public void testInvalidIsolation() throws Exception { tmCtrl.expectAndReturn(tm.create(-1), txCreated); // no need to call abort since the exception appears before the transaction gets used. //tm.abort(txId); mockReplay(); TransactionTemplate tt = new TransactionTemplate(jiniTM); assertSynchronizationManager(); tt.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE); try { tt.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); spaceTemplate.execute(new JavaSpaceCallback() { public Object doInSpace(JavaSpace js, Transaction transaction) throws RemoteException, TransactionException, UnusableEntryException, InterruptedException { return null; } }); } }); fail("Should have thrown InvalidIsolationLevelException"); } catch (InvalidIsolationLevelException e) { // it's okay } assertSynchronizationManager(); } public void testTransactionCommitWithPrebound() throws Exception { mockReplay(); TransactionTemplate tt = new TransactionTemplate(jiniTM); assertSynchronizationManager(); Created crtd = new Created(tx, lease); // bind tx object JiniHolder holder = new JiniHolder(crtd); TransactionSynchronizationManager.bindResource(space, holder); assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); tt.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); spaceTemplate.execute(new JavaSpaceCallback() { public Object doInSpace(JavaSpace js, Transaction transaction) throws RemoteException, TransactionException, UnusableEntryException, InterruptedException { return null; }}); } }); assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); TransactionSynchronizationManager.unbindResource(space); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); } public void testTransactionRollbackOnlyWithPrebound() throws Exception { mockReplay(); TransactionTemplate tt = new TransactionTemplate(jiniTM); assertSynchronizationManager(); Created crtd = new Created(tx, lease); // bind tx object JiniHolder holder = new JiniHolder(crtd); TransactionSynchronizationManager.bindResource(space, holder); assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); tt.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); spaceTemplate.execute(new JavaSpaceCallback() { public Object doInSpace(JavaSpace js, Transaction transaction) throws RemoteException, TransactionException, UnusableEntryException, InterruptedException { return null; }}); status.setRollbackOnly(); } }); assertTrue("Has thread session", TransactionSynchronizationManager.hasResource(space)); TransactionSynchronizationManager.unbindResource(space); assertTrue("JTA synchronizations not active", !TransactionSynchronizationManager.isSynchronizationActive()); } }