/*
* JBoss, Home of Professional Open Source.
* Copyright 2014, Red Hat Middleware LLC, 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.jts.tools;
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 static org.junit.Assert.fail;
import java.lang.reflect.Field;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosTransactions.HeuristicHazard;
import com.arjuna.ats.arjuna.AtomicAction;
import com.arjuna.ats.arjuna.common.recoveryPropertyManager;
import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
import com.arjuna.ats.arjuna.coordinator.ActionStatus;
import com.arjuna.ats.arjuna.coordinator.RecordType;
import com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator;
import com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeManager;
import com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeMap;
import com.arjuna.ats.arjuna.recovery.RecoveryDriver;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean;
import com.arjuna.ats.arjuna.tools.osb.mbean.LogRecordWrapper;
import com.arjuna.ats.arjuna.tools.osb.mbean.OSEntryBean;
import com.arjuna.ats.arjuna.tools.osb.mbean.ObjStoreBrowser;
import com.arjuna.ats.arjuna.tools.osb.mbean.UidWrapper;
import com.arjuna.ats.arjuna.tools.osb.util.JMXServer;
import com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule;
import com.arjuna.ats.internal.arjuna.thread.ThreadActionData;
import com.arjuna.ats.internal.jta.recovery.jts.XARecoveryModule;
import com.arjuna.ats.internal.jts.ORBManager;
import com.arjuna.ats.internal.jts.orbspecific.coordinator.ArjunaTransactionImple;
import com.arjuna.ats.jts.common.jtsPropertyManager;
import com.arjuna.orbportability.OA;
import com.arjuna.orbportability.ORB;
import com.hp.mwtests.ts.jta.jts.common.ExtendedCrashRecord;
import com.hp.mwtests.ts.jta.jts.common.TestBase;
/**
* Test the the ObjStoreBrowser MBean in a JTS environment.
*
* @author Mike Musgrove
*/
/**
* @deprecated as of 5.0.5.Final In a subsequent release we will change packages names in order to
* provide a better separation between public and internal classes.
*/
@Deprecated // in order to provide a better separation between public and internal classes.
public class JTSObjStoreBrowserTest extends TestBase {
private XARecoveryModule xarm;
private AtomicActionRecoveryModule aarm;
@BeforeClass
public static void initOrb() throws InvalidName {
int recoveryOrbPort = jtsPropertyManager.getJTSEnvironmentBean().getRecoveryManagerPort();
final Properties p = new Properties();
p.setProperty("OAPort", ""+recoveryOrbPort);
p.setProperty("com.sun.CORBA.POA.ORBPersistentServerPort", ""+recoveryOrbPort);
p.setProperty("com.sun.CORBA.POA.ORBServerId", ""+recoveryOrbPort);
ORB orb = ORB.getInstance("test");
OA oa = OA.getRootOA(orb);
orb.initORB(new String[] {}, p);
oa.initOA();
ORBManager.setORB(orb);
ORBManager.setPOA(oa);
}
@AfterClass
public static void shutdownOrb() {
ORBManager.getPOA().destroy();
ORBManager.getORB().shutdown();
ORBManager.reset();
}
@Before
public void beforeTest () throws Exception
{
recoveryPropertyManager.getRecoveryEnvironmentBean().setPeriodicRecoveryPeriod(1);
recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryBackoffPeriod(1);
xarm = new XARecoveryModule();
aarm = new AtomicActionRecoveryModule();
recoveryManager.addModule(xarm);
recoveryManager.addModule(aarm);
}
@After
public void afterTest () throws Exception
{
recoveryManager.removeModule(aarm, false);
recoveryManager.removeModule(xarm, false);
}
private ObjStoreBrowser createObjStoreBrowser(boolean probe) throws MBeanException {
ObjStoreBrowser osb = new ObjStoreBrowser();
osb.setType("com.arjuna.ats.arjuna.AtomicAction", "com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean");
osb.setType("com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ArjunaTransactionImpleWrapper", "com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean");
if (probe) {
osb.start();
osb.probe();
}
return osb;
}
/*
TODO JTS test-compile doesn't pull in com.arjuna.common.tests.simple
@Test
public void testXAResourceRecordBean() throws Exception {
com.arjuna.common.tests.simple.EnvironmentBeanTest.testBeanByReflection(new XAResourceRecordBean(new UidWrapper(Uid.nullUid())));
}
*/
/**
* Create an atomic action with two participants, one of which will generate a heuristic during phase 2.
* The test will move the heuristic back into the prepared state and trigger recovery to replay phase 2.
* The test then asserts that the corresponding MBeans have been unregistered.
* @throws Exception if test fails unexpectedly
*/
@Test
public void aaReplayTest() throws Exception {
AtomicAction A = new AtomicAction();
ExtendedCrashRecord recs[] = startTest(A);
int outcome = A.commit();
assertEquals(ActionStatus.H_HAZARD, outcome);
finishTest(A, true, recs);
}
/**
* Similar to @aaReplayTest except that the whole transaction record is removed from the object store
* (instead of replaying the record that generates a heuristic).
* @throws Exception if test fails unexpectedly
*/
@Test
public void aaRemoveTest() throws Exception {
AtomicAction A = new AtomicAction();
ExtendedCrashRecord recs[] = startTest(A);
int outcome = A.commit();
assertEquals(ActionStatus.H_HAZARD, outcome);
finishTest(A, false, recs);
}
/**
* Similar to aaReplayTest but uses a JTS transaction instead of an AtomicAction
* @throws Exception if test fails unexpectedly
*/
// TODO for replay to work on JTS participants ExtendedCrashReocrd needs to extend XAResourceRecord
// TODO @Test
public void jtsReplayTest() throws Exception {
ArjunaTransactionImple A = new ArjunaTransactionImple(null);
ExtendedCrashRecord recs[] = startTest(A);
int outcome = ActionStatus.COMMITTED;
try {
A.commit(true);
} catch (HeuristicHazard e) {
outcome = ActionStatus.H_HAZARD;
}
assertEquals(ActionStatus.H_HAZARD, outcome);
finishTest(A, true, recs);
}
/**
* Test that MBeans corresponding to JTS record types are created
* @throws Exception
*/
@Test
public void jtsMBeanTest() throws Exception {
ArjunaTransactionImple A = new ArjunaTransactionImple(null);
startTest(A);
try {
A.commit(true);
fail("transaction commit should have produced a heuristic hazzard");
} catch (HeuristicHazard e) {
}
createObjStoreBrowser(true); // start an mbean server and object store browser
// there should now be an MBean entry corresponding to a JTS record, read it via JMX:
MBeanServer mbs = JMXServer.getAgent().getServer();
Set<ObjectInstance> transactions = mbs.queryMBeans(new ObjectName("jboss.jta:type=ObjectStore,*"), null);
boolean foundJTSType = false;
Pattern pattern = Pattern.compile("itype=(.*?),");
for (ObjectInstance oi : transactions) {
String id = oi.getObjectName().getCanonicalName();
Matcher matcher = pattern.matcher(id);
while (matcher.find())
foundJTSType = true; // matched type is in matcher.group(1)
}
assertTrue("MBean for JTS record type not found", foundJTSType);
}
/**
* Similar to aaRemoveTest but uses a JTS transaction instead of an AtomicAction
* @throws Exception if test fails unexpectedly
*/
@Test
public void jtsRemoveTest() throws Exception {
ArjunaTransactionImple A = new ArjunaTransactionImple(null);
ExtendedCrashRecord recs[] = startTest(A);
int outcome = ActionStatus.COMMITTED;
try {
A.commit(true);
} catch (HeuristicHazard e) {
outcome = ActionStatus.H_HAZARD;
}
assertEquals(ActionStatus.H_HAZARD, outcome);
finishTest(A, false, recs);
}
// create 2 participants, start the action and enlist both participants
private ExtendedCrashRecord[] startTest(TwoPhaseCoordinator A) throws Exception {
ThreadActionData.purgeActions();
ExtendedCrashRecord recs[] = {
new ExtendedCrashRecord(ExtendedCrashRecord.CrashLocation.NoCrash, ExtendedCrashRecord.CrashType.Normal),
new ExtendedCrashRecord(ExtendedCrashRecord.CrashLocation.CrashInCommit, ExtendedCrashRecord.CrashType.HeuristicHazard)
};
RecordTypeManager.manager().add(new RecordTypeMap() {
public Class<? extends AbstractRecord> getRecordClass () { return ExtendedCrashRecord.class;}
public int getType () {return RecordType.USER_DEF_FIRST0;}
});
A.start();
for (ExtendedCrashRecord rec : recs)
A.add(rec);
return recs;
}
/*
* Make sure there is an MBean corresponding to A and that at least one of recs has a heuristic.
* Then either remove the action or replay (via the MBean) the record that got the heuristic
* checking that the MBeans have all been unregistered from the MBeanServer.
*/
private void finishTest(TwoPhaseCoordinator A, boolean replay, ExtendedCrashRecord ... recs) throws Exception {
ObjStoreBrowser osb = createObjStoreBrowser(false);
// there should now be an entry in the object store containing two participants
osb.start();
osb.probe();
// there should be one MBean corresponding to the AtomicAction A
UidWrapper w = osb.findUid(A.get_uid());
assertNotNull(w);
OSEntryBean ai = w.getMBean();
assertNotNull(ai);
// the MBean should wrap an ActionBean
assertTrue(ai instanceof ActionBean);
ActionBean actionBean = (ActionBean) ai;
// and there should be one MBean corresponding to the CrashRecord that got the heuristic:
int recCount = 0;
for (ExtendedCrashRecord rec : recs) {
LogRecordWrapper lw = actionBean.getParticipant(rec);
if (lw != null) {
recCount += 1;
if (lw.isHeuristic()) {
if (replay) {
rec.forget();
lw.setStatus("PREPARED");
// the participant record should no longer be on the heuristic list
assertFalse(lw.isHeuristic());
}
}
}
}
assertEquals(1, recCount);
if (!replay) {
actionBean.remove();
} else {
/*
* prompt the recovery manager to have a go at replaying the record that was
* moved off the heuristic list and back onto the prepared list
*/
recoveryManager.scan();
}
// another probe should no longer find the record that got the heuristic
// (since it was either removed or the RecoveryManager replayed the commit
// phase) so its corresponding MBean will have been unregistered
osb.probe();
// look up the MBean and verify that it no longer exists
w = osb.findUid(A.get_uid());
assertNull(w);
osb.stop();
}
}