/* * JBoss, Home of Professional Open Source. * Copyright 2014, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 2.1 of * the License, or (at your option) any later version. * * This software 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 this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.hp.mwtests.ts.jta.recovery; import com.arjuna.ats.arjuna.AtomicAction; import com.arjuna.ats.arjuna.common.Uid; import com.arjuna.ats.arjuna.common.recoveryPropertyManager; import com.arjuna.ats.arjuna.objectstore.RecoveryStore; import com.arjuna.ats.arjuna.objectstore.StoreManager; import com.arjuna.ats.arjuna.recovery.RecoveryManager; import com.arjuna.ats.arjuna.recovery.RecoveryModule; import com.arjuna.ats.arjuna.state.InputObjectState; import com.arjuna.ats.internal.arjuna.common.UidHelper; import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule; import com.arjuna.ats.jta.common.jtaPropertyManager; import org.jboss.byteman.contrib.bmunit.BMRule; import org.jboss.byteman.contrib.bmunit.BMUnitRunner; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import javax.transaction.TransactionManager; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.Vector; /** * Tests the scenario when transaction manager crashes after all resources were committed, * but before object store was updated. * * In such scenario, XAResourceWrapper resources with not empty JNDI name should be removed from the object store. * The rest of the resources, should be kept in the object store for the administrator to handle. * * @author <a href="mailto:gytis@redhat.com">Gytis Trikleris</a> */ @RunWith(BMUnitRunner.class) public class CrashAfterResourcesCommitTest { private static String ATOMIC_ACTION_TYPE; private TransactionManager transactionManager; private RecoveryManager recoveryManager; private RecoveryStore recoveryStore; @BeforeClass public static void beforeClass() { if (System.getProperty("com.arjuna.ats.arjuna.common.propertiesFile") == null) { System.setProperty("com.arjuna.ats.arjuna.common.propertiesFile", "jbossts-properties.xml"); } jtaPropertyManager.getJTAEnvironmentBean().setXaResourceRecordWrappingPluginClassName( "com.hp.mwtests.ts.jta.recovery.TestXAResourceRecordWrappingPlugin" ); jtaPropertyManager.getJTAEnvironmentBean().setXaResourceRecoveryClassNames(Arrays.asList( "com.hp.mwtests.ts.jta.recovery.TestXAResourceRecovery" )); recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryBackoffPeriod(1); recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryModuleClassNames(Arrays.asList( "com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule", "com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule")); ATOMIC_ACTION_TYPE = new AtomicAction().type(); } @Before public void before() throws Exception { transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager(); recoveryManager = RecoveryManager.manager(RecoveryManager.DIRECT_MANAGEMENT); recoveryStore = StoreManager.getRecoveryStore(); clearObjectStore(); } @After public void after() throws Exception { clearObjectStore(); } /** * Enlists two XAResourceWrapper resources to the transaction. * * Both resources should be marked as contacted during the recovery. And transaction record should be removed. * * @throws Exception */ @Test @BMRule(name = "Don't clean TM log after commit", targetClass = "com.arjuna.ats.arjuna.coordinator.BasicAction", targetMethod = "updateState", targetLocation = "AT ENTRY", condition = "!flagged(\"testTwoXAResourceWrappers\")", action = "flag(\"testTwoXAResourceWrappers\"); return") public void testTwoXAResourceWrappers() throws Exception { final TestXAResourceWrapper firstXAResourceWrapper = new TestXAResourceWrapper("first", "first", "first"); final TestXAResourceWrapper secondXAResourceWrapper = new TestXAResourceWrapper("second", "second", "second"); TestXAResourceRecovery.setResources(firstXAResourceWrapper, secondXAResourceWrapper); final int uidsCountBeforeTest = getUidsCountInStore(); transactionManager.begin(); transactionManager.getTransaction().enlistResource(firstXAResourceWrapper); transactionManager.getTransaction().enlistResource(secondXAResourceWrapper); transactionManager.commit(); Assert.assertEquals(1, firstXAResourceWrapper.commitCount()); Assert.assertEquals(1, secondXAResourceWrapper.commitCount()); Assert.assertEquals(uidsCountBeforeTest + 1, getUidsCountInStore()); recoveryManager.initialize(); recoveryManager.scan(); Assert.assertEquals(uidsCountBeforeTest, getUidsCountInStore()); final Set<String> contactedJndiNames = getContactedJndiNames(); Assert.assertEquals(2, contactedJndiNames.size()); Assert.assertTrue(contactedJndiNames.contains("first")); Assert.assertTrue(contactedJndiNames.contains("second")); } /** * Enlists one XAResource and one XAResourceWrapper resources. * * XAResourceWrapper resource should be marked as contacted. Transaction record should still be in the object store. * * @throws Exception */ @Test @BMRule(name = "Don't clean TM log after commit", targetClass = "com.arjuna.ats.arjuna.coordinator.BasicAction", targetMethod = "updateState", targetLocation = "AT ENTRY", condition = "!flagged(\"testXAResourceAndXAResourceWrapper\")", action = "flag(\"testXAResourceAndXAResourceWrapper\"); return") public void testXAResourceAndXAResourceWrapper() throws Exception { final TestXAResource xaResource = new TestXAResource(); final TestXAResourceWrapper xaResourceWrapper = new TestXAResourceWrapper("first", "first", "first"); TestXAResourceRecovery.setResources(xaResource, xaResourceWrapper); final int uidsCountBeforeTest = getUidsCountInStore(); transactionManager.begin(); transactionManager.getTransaction().enlistResource(xaResource); transactionManager.getTransaction().enlistResource(xaResourceWrapper); transactionManager.commit(); Assert.assertEquals(1, xaResource.commitCount()); Assert.assertEquals(1, xaResourceWrapper.commitCount()); Assert.assertEquals(uidsCountBeforeTest + 1, getUidsCountInStore()); recoveryManager.initialize(); recoveryManager.scan(); Assert.assertEquals(uidsCountBeforeTest + 1, getUidsCountInStore()); final Set<String> contactedJndiNames = getContactedJndiNames(); Assert.assertEquals(1, contactedJndiNames.size()); Assert.assertTrue(contactedJndiNames.contains("first")); } /** * Enlists two XAResource resources. * * Transaction record should still be in the object store. And no resources marked as contacted. * * @throws Exception */ @Test @BMRule(name = "Don't clean TM log after commit", targetClass = "com.arjuna.ats.arjuna.coordinator.BasicAction", targetMethod = "updateState", targetLocation = "AT ENTRY", condition = "!flagged(\"testTwoXAResources\")", action = "flag(\"testTwoXAResources\"); return") public void testTwoXAResources() throws Exception { final TestXAResource firstXAResource = new TestXAResource(); final TestXAResource secondXAResource = new TestXAResource(); TestXAResourceRecovery.setResources(firstXAResource, secondXAResource); final int uidsCountBeforeTest = getUidsCountInStore(); transactionManager.begin(); transactionManager.getTransaction().enlistResource(firstXAResource); transactionManager.getTransaction().enlistResource(secondXAResource); transactionManager.commit(); Assert.assertEquals(1, firstXAResource.commitCount()); Assert.assertEquals(1, secondXAResource.commitCount()); Assert.assertEquals(uidsCountBeforeTest + 1, getUidsCountInStore()); recoveryManager.initialize(); recoveryManager.scan(); Assert.assertEquals(uidsCountBeforeTest + 1, getUidsCountInStore()); final Set<String> contactedJndiNames = getContactedJndiNames(); Assert.assertEquals(0, contactedJndiNames.size()); } private int getUidsCountInStore() throws Exception { final InputObjectState uids = new InputObjectState(); recoveryStore.allObjUids(ATOMIC_ACTION_TYPE, uids); int counter = 0; for (Uid uid = UidHelper.unpackFrom(uids); !uid.equals(Uid.nullUid()); uid = UidHelper.unpackFrom(uids)) { counter++; } return counter; } private void clearObjectStore() throws Exception { final InputObjectState uids = new InputObjectState(); recoveryStore.allObjUids(ATOMIC_ACTION_TYPE, uids); for (Uid uid = UidHelper.unpackFrom(uids); !uid.equals(Uid.nullUid()); uid = UidHelper.unpackFrom(uids)) { recoveryStore.remove_committed(uid, ATOMIC_ACTION_TYPE); } } private Set<String> getContactedJndiNames() { final Vector<RecoveryModule> recoveryModules = RecoveryManager.manager().getModules(); for (final RecoveryModule recoveryModule : recoveryModules) { if (recoveryModule instanceof XARecoveryModule) { return ((XARecoveryModule) recoveryModule).getContactedJndiNames(); } } return new HashSet<String>(); } }