diff --git a/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/logging/arjunaI18NLogger.java b/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/logging/arjunaI18NLogger.java index d3ea2ec03a..f41c1af084 100644 --- a/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/logging/arjunaI18NLogger.java +++ b/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/logging/arjunaI18NLogger.java @@ -1634,6 +1634,10 @@ public void warn_objectstore_JDBCImple_over_max_image_size(int imageSize, @LogMessage(level = WARN) void warn_invalidObjStoreBrowser_type(String type, @Cause Exception e); + @Message(id = 12407, value = "Skipping handler for bean type: '{0}'", format = MESSAGE_FORMAT) + @LogMessage(level = INFO) + void info_osbSkipHandler(String type); + /* Allocate new messages directly above this notice. - id: use the next id number in numeric sequence. Don't reuse ids. diff --git a/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowser.java b/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowser.java index 9dae2617b2..47c65cf1f8 100644 --- a/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowser.java +++ b/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowser.java @@ -84,6 +84,20 @@ public class ObjStoreBrowser implements ObjStoreBrowserMBean { "StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction", null ), + // JTAActionBean overrides ActionBean when the ArjunaJTA module is present, as opposed to just ArjunaCore + // on its own. + // These handlers are iterated over and added to the osbTypeMap map using typeName as the key so, + // to ensure that JTAActionBean is added to the map in JTA mode, please ensure that it appears + // last in this collection so that it overwrites the previous value. An alternative could be to make + // the beanClass field a comma separated list and the first one that is found to be known to the + // class loader would be used, or a completely different design for the instrumentation could be chosen + new OSBTypeHandler( + true, + "com.arjuna.ats.arjuna.AtomicAction", + "com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean", + "StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction", + null + ), }; private static final OSBTypeHandler[] defaultJTSOsbTypes = { @@ -294,8 +308,17 @@ private void init(String logDir) { setExposeAllRecordsAsMBeans(arjPropertyManager. getObjectStoreEnvironmentBean().getExposeAllLogRecordsAsMBeans()); - for (OSBTypeHandler osbType : defaultOsbTypes) - osbTypeMap.put(osbType.getTypeName(), osbType); + for (OSBTypeHandler osbType : defaultOsbTypes) { + try { + Class.forName(osbType.getBeanClass(), true, this.getClass().getClassLoader()); + osbTypeMap.put(osbType.getTypeName(), osbType); + } catch (ClassNotFoundException ignore) { + // Some record types (currently only StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction) + // use different handlers depending upon whether ArjunaJTA is present. Ignore classes which + // aren't present and log an info message + tsLogger.i18NLogger.info_osbSkipHandler(osbType.getBeanClass()); + } + } for (OSBTypeHandler osbType : defaultJTSOsbTypes) osbTypeMap.put(osbType.getTypeName(), osbType); diff --git a/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/tools/ObjStoreBrowserTest.java b/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/tools/ObjStoreBrowserTest.java index f87b20d69a..3ba00708bc 100644 --- a/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/tools/ObjStoreBrowserTest.java +++ b/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/tools/ObjStoreBrowserTest.java @@ -1,12 +1,12 @@ package com.hp.mwtests.ts.jta.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.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -14,6 +14,13 @@ import javax.management.MBeanException; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; + +import com.arjuna.ats.arjuna.exceptions.ObjectStoreException; +import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord; +import org.jboss.tm.XAResourceWrapper; +import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecordWrappingPlugin; +import com.arjuna.ats.jta.common.JTAEnvironmentBean; +import com.arjuna.ats.jta.common.jtaPropertyManager; import jakarta.transaction.HeuristicMixedException; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; @@ -45,8 +52,13 @@ * provide a better separation between public and internal classes. */ @Deprecated // in order to provide a better separation between public and internal classes. -class ExtendedFailureXAResource extends FailureXAResource { +class ExtendedFailureXAResource extends FailureXAResource implements XAResourceWrapper { private boolean forgotten; + private String productName; + + public ExtendedFailureXAResource(FailLocation loc) { + super(loc); + } @Override public void commit(Xid id, boolean onePhase) throws XAException { @@ -59,18 +71,34 @@ public void forget(Xid xid) throws XAException { super.forget(xid); forgotten = true; } -} -public class ObjStoreBrowserTest { - private ObjStoreBrowser osb; + @Override + public XAResource getResource() { + return null; + } - private ObjStoreBrowser createObjStoreBrowser() { - ObjStoreBrowser osb = new ObjStoreBrowser(); + public void setProductName(String productName) { + this.productName = productName; + } - osb.setType("com.arjuna.ats.arjuna.AtomicAction", "com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean"); + @Override + public String getProductName() { + return productName; + } - return osb; - } + @Override + public String getProductVersion() { + return null; + } + + @Override + public String getJndiName() { + return null; + } +} + +public class ObjStoreBrowserTest { + private ObjStoreBrowser osb; @BeforeClass public static void setUp() { @@ -79,7 +107,8 @@ public static void setUp() { @Before public void beforeTest() { FailureXAResource.resetForgetCounts(); - osb = createObjStoreBrowser(); + + osb = new ObjStoreBrowser(); osb.start(); } @@ -106,9 +135,16 @@ public void testCommitMarkableResourceRecordBean() throws Exception { @Test public void testMBeanHeuristic () throws Exception { - FailureXAResource failureXAResource = new FailureXAResource(FailureXAResource.FailLocation.commit); // generates a heuristic on commit + // generates a heuristic on commit + ExtendedFailureXAResource failureXAResource = + new ExtendedFailureXAResource(FailureXAResource.FailLocation.commit); + String productName = "P"; + + failureXAResource.setProductName(productName); + + XAResourceRecordBeanMBean mbean = getHeuristicMBean(osb, new TransactionImple(1000000000), failureXAResource); - getHeuristicMBean(osb, new TransactionImple(1000000000), failureXAResource); + assertEquals(productName, mbean.getEisProductName()); } /** @@ -337,25 +373,61 @@ private JTAActionBean getTransactionBean(ObjStoreBrowser osb, TransactionImple t } private XAResourceRecordBeanMBean getHeuristicMBean(ObjStoreBrowser osb, TransactionImple tx, FailureXAResource failureXAResource) throws Exception { - generateHeuristic(tx, failureXAResource); + // use the wrapper plugin because we'd also like to test resource metadata + XAResourceRecordWrappingPlugin xarWrapperPlugin = new XAResourceRecordWrappingPlugin() { + String productName = ""; + @Override + public void transcribeWrapperData(XAResourceRecord xaResourceRecord) { + final XAResource xaResource = (XAResource) xaResourceRecord.value(); + + if (xaResource instanceof XAResourceWrapper) { + XAResourceWrapper xaResourceWrapper = (XAResourceWrapper) xaResource; + productName = xaResourceWrapper.getProductName(); + + xaResourceRecord.setProductName(xaResourceWrapper.getProductName()); + xaResourceRecord.setProductVersion(xaResourceWrapper.getProductVersion()); + xaResourceRecord.setJndiName(xaResourceWrapper.getJndiName()); + } + } + + @Override + public Integer getEISName(XAResource xaResource) throws IOException, ObjectStoreException { + return 0; + } + + @Override + public String getEISName(Integer eisName) { + return productName; + } + }; - osb.probe(); - // there should be one MBean corresponding to the Transaction - JTAActionBean actionBean = getTransactionBean(osb, tx, true); + JTAEnvironmentBean env = jtaPropertyManager.getJTAEnvironmentBean(); + XAResourceRecordWrappingPlugin prevWrappingPlugin = env.getXAResourceRecordWrappingPlugin(); + + try { + env.setXAResourceRecordWrappingPlugin(xarWrapperPlugin); + generateHeuristic(tx, failureXAResource); + osb.probe(); + // there should be one MBean corresponding to the Transaction + JTAActionBean actionBean = getTransactionBean(osb, tx, true); - assertNotNull(actionBean); + assertNotNull(actionBean); - // and the transaction should contain only one participant (namely the FailureXAResource that generated the heuristic): - Collection participants = actionBean.getParticipants(); + // and the transaction should contain only one participant + // (namely the FailureXAResource that generated the heuristic): + Collection participants = actionBean.getParticipants(); - assertEquals(1, participants.size()); - assertNotNull(failureXAResource.getXid()); + assertEquals(1, participants.size()); + assertNotNull(failureXAResource.getXid()); - LogRecordWrapper participant = participants.iterator().next(); + LogRecordWrapper participant = participants.iterator().next(); - assertTrue(participant.isHeuristic()); - assertTrue(participant instanceof XAResourceRecordBeanMBean); + assertTrue(participant.isHeuristic()); + assertTrue(participant instanceof XAResourceRecordBeanMBean); - return (XAResourceRecordBeanMBean) participant; + return (XAResourceRecordBeanMBean) participant; + } finally { + env.setXAResourceRecordWrappingPlugin(prevWrappingPlugin); + } } }