/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.aries.transaction; import static org.easymock.EasyMock.createControl; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.transaction.NotSupportedException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.easymock.EasyMock; import org.easymock.IMocksControl; import org.junit.Before; import org.junit.Test; public class TranStrategyTest { TransactionManager tm; Transaction t; private IMocksControl c; @Before public void clean() { c = createControl(); tm = c.createMock(TransactionManager.class); t = c.createMock(Transaction.class); } @Test public void testMandatoryBegin() throws Exception { // MANDATORY strategy should throw IllegalStateException when tran manager // status is Status.STATUS_NO_TRANSACTION it should not return null. expect(tm.getStatus()).andReturn(Status.STATUS_NO_TRANSACTION); try { assertNotNull("TransactionStrategy.MANDATORY.begin(tm) returned null when manager " + "status is STATUS_NO_TRANSACTION", TransactionAttribute.MANDATORY.begin(tm).getActiveTransaction()); } catch (IllegalStateException ise) { // Expected to be in here } catch (Exception e) { fail("TransactionStrategy.MANDATORY.begin() threw an unexpected exception when tran manager status is STATUS_NO_TRANSACTION"); } // MANDATORY strategy should return null for all tran manager states other // than Status.STATUS_NO_TRANSACTION. int[] invalids = new int[]{ Status.STATUS_COMMITTED, Status.STATUS_COMMITTING, Status.STATUS_MARKED_ROLLBACK, Status.STATUS_ACTIVE, Status.STATUS_PREPARED, Status.STATUS_PREPARING, Status.STATUS_ROLLEDBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_UNKNOWN }; for (int i = 0; i < invalids.length ; i++) { c.reset(); expect(tm.getStatus()).andReturn(invalids[i]); expect(tm.getTransaction()).andReturn(null); c.replay(); try { Transaction tran = TransactionAttribute.MANDATORY.begin(tm).getActiveTransaction(); assertNull("TransactionStrategy.MANDATORY.begin() did not return null when manager status value is " + invalids[i], tran); } catch (Exception ise) { fail("TransactionStrategy.MANDATORY.begin() threw Exception when manager status value is " + invalids[i]); } c.verify(); } } @Test public void testMandatoryFinish() { try { TransactionToken tranToken = new TransactionToken(t, null, TransactionAttribute.MANDATORY); TransactionAttribute.MANDATORY.finish(tm, tranToken); } catch (Exception e) { fail("TransactionStrategy.MANDATORY.finish() threw an unexpected exception"); } } @Test public void testNeverBegin() throws Exception { // NEVER strategy should throw IllegalStateException when tran manager // status is Status.STATUS_ACTIVE it should not return null. expect(tm.getStatus()).andReturn(Status.STATUS_ACTIVE); try { assertNotNull("TransactionStrategy.NEVER.begin() returned null when manager status is STATUS_ACTIVE", TransactionAttribute.NEVER.begin(tm)); } catch (IllegalStateException ise) { // Expect to be in here } catch (Exception e) { fail("TransactionStrategy.NEVER.begin() threw an unexpected exception when tran manager status is STATUS_ACTIVE"); } // NEVER strategy should return null for all tran manager states other // than Status.STATUS_ACTIVE. int[] invalids = new int[]{ Status.STATUS_COMMITTED, Status.STATUS_COMMITTING, Status.STATUS_MARKED_ROLLBACK, Status.STATUS_NO_TRANSACTION, Status.STATUS_PREPARED, Status.STATUS_PREPARING, Status.STATUS_ROLLEDBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_UNKNOWN }; for (int i = 0; i < invalids.length ; i++) { c.reset(); expect(tm.getStatus()).andReturn(invalids[i]); expect(tm.getTransaction()).andReturn(null).anyTimes(); c.replay(); try { assertNull("TransactionStrategy.NEVER.begin() did not return null when manager status value is " + invalids[i], TransactionAttribute.NEVER.begin(tm).getActiveTransaction()); } catch (Exception ise) { fail("TransactionStrategy.NEVER.begin() threw unexpected exception when manager status value is " + invalids[i]); } c.verify(); } } @Test public void testNeverFinish() { try { TransactionToken tranToken = new TransactionToken(null, null, TransactionAttribute.NEVER); TransactionAttribute.NEVER.finish(tm, tranToken); } catch (Exception e) { fail("TransactionStrategy.NEVER.finish() threw an unexpected exception"); } } @Test public void testNotSupportedBegin() throws Exception { // NOT_SUPPORTED strategy should suspend an active transaction // and _NOT_ begin a new one expect(tm.getStatus()).andReturn(Status.STATUS_ACTIVE); expect(tm.suspend()).andReturn(null); c.replay(); TransactionAttribute.NOT_SUPPORTED.begin(tm); c.verify(); // For all situations where there is no active transaction the // NOT_SUPPORTED strategy should return null int[] invalids = new int[]{ Status.STATUS_COMMITTED, Status.STATUS_COMMITTING, Status.STATUS_MARKED_ROLLBACK, Status.STATUS_NO_TRANSACTION, Status.STATUS_PREPARED, Status.STATUS_PREPARING, Status.STATUS_ROLLEDBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_UNKNOWN }; for (int i = 0; i < invalids.length ; i++) { c.reset(); expect(tm.getStatus()).andReturn(invalids[i]); expect(tm.getTransaction()).andReturn(null).anyTimes(); c.replay(); try { assertNull("TransactionStrategy.NOT_SUPPORTED.begin() did not return null when manager status value is " + invalids[i], TransactionAttribute.NOT_SUPPORTED.begin(tm).getActiveTransaction()); } catch (Exception ise) { fail("TransactionStrategy.NOT_SUPPORTED.begin() threw unexpected exception when manager status value is " + invalids[i]); } c.verify(); } } @Test public void testNotSupportedFinish() { // If finish is called with a previously active transaction, then // we expect this transaction to be resumed for a NOT_SUPPORTED strategy try { tm.resume(t); EasyMock.expectLastCall(); c.replay(); TransactionToken tranToken = new TransactionToken(null, t, TransactionAttribute.NOT_SUPPORTED); TransactionAttribute.NOT_SUPPORTED.finish(tm, tranToken); c.verify(); c.reset(); tranToken = new TransactionToken(null, null, TransactionAttribute.NOT_SUPPORTED); TransactionAttribute.NOT_SUPPORTED.finish(tm, tranToken); } catch (Exception e) { fail("TransactionStrategy.NOT_SUPPORTED.finish() threw unexpected exception, " + e); } } @Test public void testRequiredBegin() throws Exception { // If there is no previously active transaction when the REQUIRED strategy // is invoked then we expect a call to begin one expect(tm.getStatus()).andReturn(Status.STATUS_NO_TRANSACTION); expect(tm.getTransaction()).andReturn(null); tm.begin(); expectLastCall(); c.replay(); TransactionAttribute.REQUIRED.begin(tm); c.verify(); c.reset(); // For all cases where there is a transaction we expect REQUIRED to return null int[] invalids = new int[]{ Status.STATUS_COMMITTED, Status.STATUS_COMMITTING, Status.STATUS_MARKED_ROLLBACK, Status.STATUS_ACTIVE, Status.STATUS_PREPARED, Status.STATUS_PREPARING, Status.STATUS_ROLLEDBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_UNKNOWN }; for (int i = 0; i < invalids.length ; i++) { c.reset(); expect(tm.getStatus()).andReturn(invalids[i]); expect(tm.getTransaction()).andReturn(null); c.replay(); try { assertNull("TransactionStrategy.REQUIRED.begin() did not return null when manager status value is " + invalids[i], TransactionAttribute.REQUIRED.begin(tm).getActiveTransaction()); } catch (Exception ise) { fail("TransactionStrategy.REQUIRED.begin() threw unexpected exception when manager status value is " + invalids[i]); } c.verify(); } } @Test public void testRequiredFinish() throws Exception { // In the REQUIRED strategy we expect a call to rollback when a call to finish() // is made with a tran where the tran manager status shows Status.STATUS_MARKED_ROLLBACK expect(tm.getStatus()).andReturn(Status.STATUS_MARKED_ROLLBACK); TransactionToken tranToken = new TransactionToken(t, null, TransactionAttribute.REQUIRED, true); tm.rollback(); expectLastCall(); c.replay(); TransactionAttribute.REQUIRED.finish(tm, tranToken); c.verify(); int[] invalids = new int[]{ Status.STATUS_COMMITTED, Status.STATUS_COMMITTING, Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, Status.STATUS_PREPARED, Status.STATUS_PREPARING, Status.STATUS_ROLLEDBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_UNKNOWN }; // For all other tran manager states we expect a call to commit for (int i = 0; i < invalids.length ; i++) { c.reset(); expect(tm.getStatus()).andReturn(invalids[i]); tm.commit(); expectLastCall(); c.replay(); TransactionAttribute.REQUIRED.finish(tm, tranToken); c.verify(); } // If null is passed instead of a tran we expect nothing to happen c.reset(); c.replay(); tranToken = new TransactionToken(null, null, TransactionAttribute.REQUIRED); c.verify(); TransactionAttribute.REQUIRED.finish(tm, tranToken); } @Test public void testRequiresNew_BeginActiveTran() throws Exception { // Suspend case (no exception from tm.begin()) expect(tm.getStatus()).andReturn(Status.STATUS_ACTIVE); // In the case of the REQUIRES_NEW strategy we expect an active tran to be suspended // a new new transaction to begin expect(tm.suspend()).andReturn(null); expectLastCall(); tm.begin(); expectLastCall(); expect(tm.getTransaction()).andReturn(null); c.replay(); TransactionAttribute.REQUIRES_NEW.begin(tm); c.verify(); } @Test public void testRequiresNew_BeginNoActiveTran() throws Exception { // No active tran cases (no exception from tm.begin()) int[] manStatus = new int[]{ Status.STATUS_COMMITTED, Status.STATUS_COMMITTING, Status.STATUS_MARKED_ROLLBACK, Status.STATUS_NO_TRANSACTION, Status.STATUS_PREPARED, Status.STATUS_PREPARING, Status.STATUS_ROLLEDBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_UNKNOWN }; // For all cases where the tran manager state is _not_ Status.STATUS_ACTIVE // we expect a call to begin a new tran, no call to suspend and null to be // returned from TransactionStrategy.REQUIRES_NEW.begin(tm) for (int i = 0; i < manStatus.length ; i++) { c.reset(); expect(tm.getStatus()).andReturn(manStatus[i]); expect(tm.getTransaction()).andReturn(null); tm.begin(); expectLastCall(); c.replay(); try { assertNull("TransactionStrategy.REQUIRES_NEW.begin() did not return null when manager status value is " + manStatus[i], TransactionAttribute.REQUIRES_NEW.begin(tm).getActiveTransaction()); } catch (Exception ise) { fail("TransactionStrategy.REQUIRES_NEW.begin() threw unexpected exception when manager status value is " + manStatus[i]); } c.verify(); } } @Test public void testRequiresNew_TmExceptions() throws Exception { int[] allStates = new int[]{ Status.STATUS_COMMITTED, Status.STATUS_COMMITTING, Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, Status.STATUS_PREPARED, Status.STATUS_PREPARING, Status.STATUS_ROLLEDBACK, Status.STATUS_MARKED_ROLLBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_UNKNOWN }; // SystemException and NotSupportedException from tm.begin() Set<Exception> ees = new HashSet<Exception>(); ees.add(new SystemException("KABOOM!")); ees.add(new NotSupportedException("KABOOM!")); // Loop through all states states twice changing the exception thrown // from tm.begin() for (int i = 0 ; i < allStates.length ; i++ ) { Iterator<Exception> iterator = ees.iterator(); while (iterator.hasNext()) { Exception e = iterator.next(); c.reset(); expect(tm.getStatus()).andReturn(allStates[i]); expect(tm.getTransaction()).andReturn(null).anyTimes(); tm.begin(); expectLastCall().andThrow(e); requiresNewExceptionCheck(tm, allStates[i]); } } } private void requiresNewExceptionCheck(TransactionManager tm, int managerStatus) throws Exception { // If an ACTIVE tran is already present we expect a call to suspend this tran. // All states should call begin(), whereupon we receive our exception resulting // in calls to resume(t) if (managerStatus == Status.STATUS_ACTIVE) { expect(tm.suspend()).andReturn(null); } tm.resume(EasyMock.anyObject(Transaction.class)); expectLastCall(); c.replay(); try { TransactionAttribute.REQUIRES_NEW.begin(tm); } catch (SystemException se) { // Expect to be in here } catch (NotSupportedException nse) { // or to be in here } catch (Exception thrownE) { fail("TransactionStrategy.REQUIRES_NEW.begin() threw unexpected exception when manager status is " + managerStatus); } finally { // If Status.STATUS_ACTIVE } c.verify(); c.reset(); } @Test public void testRequiresNew_Finish() throws Exception { int[] allStates = new int[]{ Status.STATUS_COMMITTED, Status.STATUS_COMMITTING, Status.STATUS_NO_TRANSACTION, Status.STATUS_ACTIVE, Status.STATUS_PREPARED, Status.STATUS_PREPARING, Status.STATUS_ROLLEDBACK, Status.STATUS_MARKED_ROLLBACK, Status.STATUS_ROLLING_BACK, Status.STATUS_UNKNOWN }; // Loop through all states calling TransactionStrategy.REQUIRES_NEW.finish // passing tran manager and a tran, then passing tran manager and null for (int i = 0 ; i < allStates.length ; i++ ) { c.reset(); expect(tm.getStatus()).andReturn(allStates[i]); requiresNew_assertion(tm, allStates[i]); tm.resume(EasyMock.anyObject(Transaction.class)); expectLastCall(); c.replay(); try { TransactionToken tranToken = new TransactionToken(t, t, TransactionAttribute.REQUIRES_NEW, true); TransactionAttribute.REQUIRES_NEW.finish(tm, tranToken); } catch (Exception e) { fail("TransactionStrategy.REQUIRES_NEW.finish() threw unexpected exception when manager status is " + allStates[i]); } c.verify(); c.reset(); try { expect(tm.getStatus()).andReturn(allStates[i]); requiresNew_assertion(tm, allStates[i]); c.replay(); TransactionToken tranToken = new TransactionToken(t, null, TransactionAttribute.REQUIRES_NEW, true); TransactionAttribute.REQUIRES_NEW.finish(tm, tranToken); } catch (Throwable e) { e.printStackTrace(); fail("TransactionStrategy.REQUIRES_NEW.finish() threw unexpected exception when manager status is " + allStates[i]); } finally { } c.verify(); } } // If tran manager status reports Status.STATUS_MARKED_ROLLBACK we expect // a call to rollback ... otherwise we expect a call to commit private void requiresNew_assertion(TransactionManager tm, int status) throws Exception { if (status == Status.STATUS_MARKED_ROLLBACK) { tm.rollback(); } else { tm.commit(); } expectLastCall(); } @Test public void testSupports() throws Exception { expect(tm.getTransaction()).andReturn(null); expect(tm.getStatus()).andReturn(Status.STATUS_ACTIVE); c.replay(); assertNull("TransTransactionStrategy.SUPPORTS.begin(tm) did not return null", TransactionAttribute.SUPPORTS.begin(tm).getActiveTransaction()); c.verify(); } }