/* * JBoss, Home of Professional Open Source * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * 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, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * (C) 2005-2006, * @author JBoss Inc. */ /* * Copyright (C) 2004, * * Arjuna Technologies Ltd, * Newcastle upon Tyne, * Tyne and Wear, * UK. * * $Id: xidcheck.java 2342 2006-03-30 13:06:17Z $ */ package com.hp.mwtests.ts.jta.recovery; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter; import org.jboss.tm.XAResourceWrapper; import org.junit.Test; import com.arjuna.ats.arjuna.common.Uid; import com.arjuna.ats.arjuna.coordinator.ActionStatus; import com.arjuna.ats.arjuna.coordinator.AddOutcome; import com.arjuna.ats.arjuna.coordinator.BasicAction; import com.arjuna.ats.arjuna.coordinator.RecordType; import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome; import com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeManager; import com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeMap; import com.arjuna.ats.arjuna.recovery.RecoverAtomicAction; import com.arjuna.ats.arjuna.recovery.RecoveryManager; import com.arjuna.ats.internal.jta.recovery.arjunacore.NodeNameXAResourceOrphanFilter; import com.arjuna.ats.internal.jta.recovery.arjunacore.RecoveryXids; import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule; import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord; import com.arjuna.ats.internal.jta.transaction.arjunacore.AtomicAction; import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.TransactionImple; import com.arjuna.ats.jta.common.jtaPropertyManager; import com.arjuna.ats.jta.recovery.XAResourceOrphanFilter; import com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper; import com.arjuna.ats.jta.xa.XidImple; import com.hp.mwtests.ts.jta.common.RecoveryXAResource; public class XARecoveryModuleUnitTest { private boolean rolledback; @Test public void testNull () { XARecoveryModule xarm = new XARecoveryModule(); xarm.periodicWorkFirstPass(); xarm.periodicWorkSecondPass(); assertNotNull(xarm.id()); } @Test public void testRecoverFromMultipleXAResourceRecovery() throws Exception { // Make sure the file doesn't exist assertFalse(new File("XARR.txt").exists()); ArrayList<String> r = new ArrayList<String>(); AtomicAction aa = new AtomicAction(); aa.begin(); assertEquals(AddOutcome.AR_ADDED, aa.add(new XAResourceRecord(null, new XARRTestResource(), new XidImple(aa), null))); Class c = BasicAction.class; Method method = c.getDeclaredMethod("prepare", boolean.class); method.setAccessible(true); int result = (Integer) method.invoke(aa, new Object[] { true }); assertEquals(result, TwoPhaseOutcome.PREPARE_OK); // Make sure the file exists assertTrue(new File("XARR.txt").exists()); // AtomicActionRecoveryModule aaRecoveryModule = new AtomicActionRecoveryModule(); // aaRecoveryModule.periodicWorkFirstPass(); // aaRecoveryModule.periodicWorkSecondPass(); RecordTypeManager.manager().add(new RecordTypeMap() { @SuppressWarnings("unchecked") public Class getRecordClass () { return XAResourceRecord.class; } public int getType () { return RecordType.JTA_RECORD; } }); List<String> xarn = new ArrayList<String>(); xarn.add(NodeNameXAResourceOrphanFilter.RECOVER_ALL_NODES); jtaPropertyManager.getJTAEnvironmentBean().setXaRecoveryNodes(xarn); XARecoveryModule xaRecoveryModule = new XARecoveryModule(); Field safetyIntervalMillis = RecoveryXids.class.getDeclaredField("safetyIntervalMillis"); safetyIntervalMillis.setAccessible(true); safetyIntervalMillis.set(null, 0); xaRecoveryModule.addXAResourceRecoveryHelper(new XARROne()); xaRecoveryModule.addXAResourceRecoveryHelper(new XARRTwo()); xaRecoveryModule.addXAResourceOrphanFilter(new com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter()); xaRecoveryModule.addXAResourceOrphanFilter(new com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter()); RecoveryManager.manager().addModule(xaRecoveryModule); // This is done rather than using the AtomicActionRecoveryModule as the transaction is inflight RecoverAtomicAction rcvAtomicAction = new RecoverAtomicAction(aa.get_uid(), ActionStatus.COMMITTED); rcvAtomicAction.replayPhase2(); // The XARM would execute next xaRecoveryModule.periodicWorkFirstPass(); xaRecoveryModule.periodicWorkSecondPass(); // Make sure the file doesn't exist assertFalse(new File("XARR.txt").exists()); aa.abort(); } @Test public void testRecover () throws Exception { ArrayList<String> r = new ArrayList<String>(); TransactionImple tx = new TransactionImple(0); assertTrue(tx.enlistResource(new RecoveryXAResource())); assertEquals(tx.doPrepare(), TwoPhaseOutcome.PREPARE_OK); r.add("com.hp.mwtests.ts.jta.recovery.DummyXARecoveryResource"); jtaPropertyManager.getJTAEnvironmentBean().setXaResourceRecoveryClassNames(r); XARecoveryModule xarm = new XARecoveryModule(); assertNull(xarm.getNewXAResource( new XAResourceRecord(null, null, new XidImple(), null) )); for (int i = 0; i < 11; i++) { xarm.periodicWorkFirstPass(); xarm.periodicWorkSecondPass(); } assertTrue(xarm.getNewXAResource( new XAResourceRecord(null, null, new XidImple(new Uid()), null) ) == null); assertNull(xarm.getNewXAResource( new XAResourceRecord(null, null, new XidImple(), null) )); } @Test public void testFailures () throws Exception { XARecoveryModule xarm = new XARecoveryModule(); Class[] parameterTypes = new Class[2]; Uid u = new Uid(); Xid x = new XidImple(); parameterTypes[0] = Xid.class; parameterTypes[1] = Uid.class; Method m = xarm.getClass().getDeclaredMethod("addFailure", parameterTypes); m.setAccessible(true); Object[] parameters = new Object[2]; parameters[0] = x; parameters[1] = u; m.invoke(xarm, parameters); parameterTypes = new Class[1]; parameterTypes[0] = Xid.class; parameters = new Object[1]; parameters[0] = x; m = xarm.getClass().getDeclaredMethod("previousFailure", parameterTypes); m.setAccessible(true); Uid ret = (Uid) m.invoke(xarm, parameters); assertEquals(ret, u); parameterTypes = new Class[2]; parameterTypes[0] = Xid.class; parameterTypes[1] = Uid.class; parameters = new Object[2]; parameters[0] = x; parameters[1] = u; m = xarm.getClass().getDeclaredMethod("removeFailure", parameterTypes); m.setAccessible(true); m.invoke(xarm, parameters); m = xarm.getClass().getDeclaredMethod("clearAllFailures", (Class[]) null); m.setAccessible(true); m.invoke(xarm, (Object[]) null); } @Test public void testXAResourceRecoveryHelperRegistration() { XARecoveryModule xaRecoveryModule = new XARecoveryModule(); XAResourceRecoveryHelper xaResourceRecoveryHelper = new DummyXAResourceRecoveryHelper(); xaRecoveryModule.addXAResourceRecoveryHelper(xaResourceRecoveryHelper); xaRecoveryModule.removeXAResourceRecoveryHelper(xaResourceRecoveryHelper); } class DummyXAResourceRecoveryHelper implements XAResourceRecoveryHelper { @Override public boolean initialise(String p) throws Exception { return false; //To change body of implemented methods use File | Settings | File Templates. } @Override public XAResource[] getXAResources() throws Exception { return new XAResource[0]; //To change body of implemented methods use File | Settings | File Templates. } } @Test public void testXAResourceOrphanFilterRegistration() { XARecoveryModule xaRecoveryModule = new XARecoveryModule(); XAResourceOrphanFilter xaResourceOrphanFilter = new DummyXAResourceOrphanFilter(); xaRecoveryModule.addXAResourceOrphanFilter(xaResourceOrphanFilter); xaRecoveryModule.removeXAResourceOrphanFilter(xaResourceOrphanFilter); } @Test public void testXAResourceOrphanFilter () throws Exception { XAResourceOrphanFilter xaResourceOrphanFilter = new DummyXAResourceOrphanFilter(XAResourceOrphanFilter.Vote.ROLLBACK); XARecoveryModule xarm = new XARecoveryModule(); xarm.addXAResourceOrphanFilter(xaResourceOrphanFilter); Class[] parameterTypes = new Class[2]; parameterTypes[0] = XAResource.class; parameterTypes[1] = Xid.class; Method m = xarm.getClass().getDeclaredMethod("handleOrphan", parameterTypes); m.setAccessible(true); Object[] parameters = new Object[2]; parameters[0] = new RecoveryXAResource(); parameters[1] = new XidImple(); m.invoke(xarm, parameters); } @Test public void testCanRepeatFirstPass () throws Exception { XARecoveryModule xarm = new XARecoveryModule(); xarm.addXAResourceRecoveryHelper(new XAResourceRecoveryHelper() { @Override public boolean initialise(String p) throws Exception { return false; } @Override public XAResource[] getXAResources() throws Exception { return new XAResource[]{new XAResource() { @Override public void commit(Xid xid, boolean b) throws XAException { } @Override public void end(Xid xid, int i) throws XAException { } @Override public void forget(Xid xid) throws XAException { } @Override public int getTransactionTimeout() throws XAException { return 0; } @Override public boolean isSameRM(XAResource xaResource) throws XAException { return false; } @Override public int prepare(Xid xid) throws XAException { return 0; } @Override public Xid[] recover(int i) throws XAException { recoverCalled++; return new Xid[0]; } @Override public void rollback(Xid xid) throws XAException { } @Override public boolean setTransactionTimeout(int i) throws XAException { return false; } @Override public void start(Xid xid, int i) throws XAException { } }}; } }); xarm.periodicWorkFirstPass(); assertEquals(recoverCalled, 1); xarm.periodicWorkSecondPass(); assertEquals(recoverCalled, 2); xarm.periodicWorkFirstPass(); assertEquals(recoverCalled, 3); xarm.periodicWorkFirstPass(); assertEquals(recoverCalled, 5); xarm.periodicWorkSecondPass(); assertEquals(recoverCalled, 6); } @Test public void testRecoverPassFailure() throws Exception { int orphanSafetyInterval = jtaPropertyManager.getJTAEnvironmentBean().getOrphanSafetyInterval(); List<String> xaRecoveryNodes = jtaPropertyManager.getJTAEnvironmentBean().getXaRecoveryNodes(); jtaPropertyManager.getJTAEnvironmentBean().setOrphanSafetyInterval(0); jtaPropertyManager.getJTAEnvironmentBean().setXaRecoveryNodes(Arrays.asList(new String[]{NodeNameXAResourceOrphanFilter.RECOVER_ALL_NODES})); XARecoveryModule xarm = new XARecoveryModule(); xarm.addXAResourceOrphanFilter(new JTANodeNameXAResourceOrphanFilter()); xarm.addXAResourceRecoveryHelper(new XAResourceRecoveryHelper() { XAResource[] xares = new XAResource[]{new XAResourceWrapper() { @Override public XAResource getResource() { return null; } @Override public String getProductName() { return null; } @Override public String getProductVersion() { return null; } @Override public String getJndiName() { return "test"; } int count = 0; Xid xid = new XidImple(new Uid()); @Override public void commit(Xid xid, boolean b) throws XAException { } @Override public void end(Xid xid, int i) throws XAException { } @Override public void forget(Xid xid) throws XAException { } @Override public int getTransactionTimeout() throws XAException { return 0; } @Override public boolean isSameRM(XAResource xaResource) throws XAException { return false; } @Override public int prepare(Xid xid) throws XAException { return 0; } @Override public Xid[] recover(int i) throws XAException { count++; if (count == 1 || count == 5) { return new Xid[]{xid}; } else if (count > 5) { return new Xid[0]; } else { throw new XAException(); } } @Override public void rollback(Xid xid) throws XAException { if (count == 1) { // This comes from the first end scan throw new XAException(XAException.XA_RETRY); } rolledback = true; } @Override public boolean setTransactionTimeout(int i) throws XAException { return false; } @Override public void start(Xid xid, int i) throws XAException { } } }; @Override public boolean initialise(String p) throws Exception { return false; } @Override public XAResource[] getXAResources() throws Exception { return xares; } }); // The first two recovery cycles do nothing with the resource (because phase two is getting the exception) // When count reaches 6 it sees that the xid has gone and presumes abort so calls rollback and hence assertTrue(rolledback) passes // 1st pass: returns one xid (count is 1) xarm.periodicWorkFirstPass(); // 2nd pass: throws an exception (count is 2) xarm.periodicWorkSecondPass(); assertTrue(xarm.getContactedJndiNames().contains("test")); assertFalse(rolledback); // 1st pass: throws an exception (count is 3) xarm.periodicWorkFirstPass(); // 2nd pass: throws an exception (count is 4) xarm.periodicWorkSecondPass(); assertFalse(xarm.getContactedJndiNames().contains("test")); assertFalse(rolledback); // 1st pass: returns an empty list of xids (count is 5) xarm.periodicWorkFirstPass(); // 2nd pass: returns an empty list of xids (count is 6) xarm.periodicWorkSecondPass(); assertTrue(xarm.getContactedJndiNames().contains("test")); assertTrue(rolledback); jtaPropertyManager.getJTAEnvironmentBean().setOrphanSafetyInterval(orphanSafetyInterval); jtaPropertyManager.getJTAEnvironmentBean().setXaRecoveryNodes(xaRecoveryNodes); } class DummyXAResourceOrphanFilter implements XAResourceOrphanFilter { public DummyXAResourceOrphanFilter () { _vote = null; } public DummyXAResourceOrphanFilter (Vote v) { _vote = v; } @Override public Vote checkXid(Xid xid) { return _vote; } private Vote _vote; } private int recoverCalled; }