Skip to content

Commit

Permalink
JBTM-3859 MBean for JTA transaction heuristics is imprecise
Browse files Browse the repository at this point in the history
  • Loading branch information
mmusgrov committed Nov 21, 2024
1 parent 347a0ef commit 8b657d9
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
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;

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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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() {
Expand All @@ -79,7 +107,8 @@ public static void setUp() {
@Before
public void beforeTest() {
FailureXAResource.resetForgetCounts();
osb = createObjStoreBrowser();

osb = new ObjStoreBrowser();

osb.start();
}
Expand All @@ -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());
}

/**
Expand Down Expand Up @@ -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<LogRecordWrapper> participants = actionBean.getParticipants();
// and the transaction should contain only one participant
// (namely the FailureXAResource that generated the heuristic):
Collection<LogRecordWrapper> 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);
}
}
}

0 comments on commit 8b657d9

Please sign in to comment.