[jboss-svn-commits] JBL Code SVN: r37829 - in labs/jbosstm/trunk: ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator and 69 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Wed Jan 4 11:33:49 EST 2012


Author: tomjenkinson
Date: 2012-01-04 11:33:42 -0500 (Wed, 04 Jan 2012)
New Revision: 37829

Added:
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBeanException.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/SubordinateJTAXAResourceOrphanFilter.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/SubordinateActionBean.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/SubordinateActionBeanMBean.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/SubordinateXidImple.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/recovery/SerializableXAResourceDeserializer.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/ats/
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/XATxConverterTest.java
   labs/jbosstm/trunk/atsintegration/examples/
   labs/jbosstm/trunk/atsintegration/examples/classes/
   labs/jbosstm/trunk/atsintegration/examples/classes/com/
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/ExampleDistributedJTATestCase.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestResource.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestResourceRecovery.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestSynchronization.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/IsolatableServersClassLoader.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/LocalServer.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/LookupProvider.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/RemoteServer.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxySynchronization.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResource.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResourceDeserializer.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResourceRecovery.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/RemoteServerImpl.java
   labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ServerImpl.java
   labs/jbosstm/trunk/atsintegration/tests/
   labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/
   labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/fail2pc.txt
   labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leave-subordinate-orphan.txt
   labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leaveorphan.txt
   labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leaverunningorphan.txt
   labs/jbosstm/trunk/atsintegration/tests/classes/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/internal/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/internal/jbossatx/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/internal/jbossatx/jta/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/internal/jbossatx/jta/TestXAResourceRecordWrapperImpl.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/SimpleIsolatedServers.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestResource.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestResourceRecovery.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestSynchronization.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/CompletionCounter.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/IsolatableServersClassLoader.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/LocalServer.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/LookupProvider.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/RemoteServer.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxySynchronization.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResource.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResourceDeserializer.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResourceRecovery.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/RemoteServerImpl.java
   labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ServerImpl.java
   labs/jbosstm/trunk/atsintegration/tests/resources/
   labs/jbosstm/trunk/atsintegration/tests/resources/jbossts-properties.xml
Modified:
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBean.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/Uid.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/BasicAction.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TxControl.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/logging/arjunaI18NLogger.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/objectstore/StoreManager.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ActionBean.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ActionBeanWrapperInterface.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/LogRecordWrapper.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowser.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowserMBean.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/UidWrapper.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/abstractrecords/LastResourceRecord.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/atomicaction/TxControlUnitTest.java
   labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/tools/ObjStoreBrowserTest.java
   labs/jbosstm/trunk/ArjunaJTA/INSTALL
   labs/jbosstm/trunk/ArjunaJTA/integration/pom.xml
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/NodeNameXAResourceOrphanFilter.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/RecoveryXids.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/XARecoveryModule.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/XARecoveryResourceImple.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/resources/arjunacore/XAResourceRecord.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/resources/arjunacore/XAResourceRecordWrappingPlugin.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/JTAActionBean.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/XAResourceMBean.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/TransactionImple.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/TransactionImporterImple.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/XATerminatorImple.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/SubordinateAtomicAction.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/TransactionImple.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/jca/SubordinateAtomicAction.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/jca/TransactionImple.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/utils/XAUtils.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/common/JTAEnvironmentBean.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/xa/XATxConverter.java
   labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/xa/XidImple.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/common/RecoveryXAResource.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/nested/SimpleNestedTest.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/RecoveryManagerTest.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/RecoveryTest.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/XARecoveryModuleUnitTest.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/XAResourceOrphanFilterTest.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/tools/ObjStoreBrowserTest.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/xa/XAUtilsUnitTest.java
   labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/xa/xidcheck.java
   labs/jbosstm/trunk/ArjunaJTS/INSTALL
   labs/jbosstm/trunk/ArjunaJTS/integration/pom.xml
   labs/jbosstm/trunk/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jts/ArjunaTransactionImpleWrapper.java
   labs/jbosstm/trunk/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jts/XAResourceRecordBean.java
   labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/common/RecoveryXAResource.java
   labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/nested/SimpleNestedTest.java
   labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/subordinate/SubordinateTestCase.java
   labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/tools/JTSObjStoreBrowserTest.java
   labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/jts/utils/Utility.java
   labs/jbosstm/trunk/ArjunaJTS/jts/idl/omg/XA.idl
   labs/jbosstm/trunk/ArjunaJTS/jts/tests/classes/com/hp/mwtests/ts/jts/local/orbsetup/ORBSetupTest.java
   labs/jbosstm/trunk/ArjunaJTS/jts/tests/classes/com/hp/mwtests/ts/jts/orbspecific/recovery/RecoveryEnablementUnitTest.java
   labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/internal/jbossatx/jta/XAResourceRecordWrappingPluginImpl.java
   labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/RecoveryManagerService.java
   labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java
   labs/jbosstm/trunk/common/classes/com/arjuna/common/util/ConfigurationInfo.java
   labs/jbosstm/trunk/common/classes/com/arjuna/common/util/propertyservice/PropertiesFactory.java
   labs/jbosstm/trunk/common/tests/com/arjuna/common/tests/simple/EnvironmentBeanTest.java
Log:
Brought in to line with changes in the 4.16 branch

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBean.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -23,6 +23,7 @@
 import com.arjuna.ats.internal.arjuna.common.ClassloadingUtility;
 import com.arjuna.common.internal.util.propertyservice.PropertyPrefix;
 import com.arjuna.common.internal.util.propertyservice.FullPropertyName;
+import com.arjuna.ats.arjuna.logging.tsLogger;
 import com.arjuna.ats.arjuna.utils.Utility;
 import com.arjuna.common.util.ConfigurationInfo;
 import com.arjuna.ats.arjuna.utils.Process;
@@ -99,10 +100,11 @@
      * Sets the node identifier. Should be uniq amongst all instances that share resource managers or an objectstore.
      *
      * @param nodeIdentifier the Node Identifier.
+     * @throws CoreEnvironmentBeanException 
      */
-    public void setNodeIdentifier(String nodeIdentifier)
+    public void setNodeIdentifier(String nodeIdentifierAsString)
     {
-        this.nodeIdentifier = nodeIdentifier;
+    	this.nodeIdentifier = nodeIdentifierAsString;
     }
 
     /**

Added: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBeanException.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBeanException.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBeanException.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,9 @@
+package com.arjuna.ats.arjuna.common;
+
+public class CoreEnvironmentBeanException extends Exception {
+
+	public CoreEnvironmentBeanException(String string) {
+		super(string);
+	}
+
+}


Property changes on: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/CoreEnvironmentBeanException.java
___________________________________________________________________
Added: svn:executable
   + *

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/Uid.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/Uid.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/common/Uid.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -763,5 +763,5 @@
     private static final Uid MIN_UID = new Uid(
             "-80000000:-80000000:-80000000:-80000000:-80000000");
     
-    private static final int UID_SIZE = 2*8 + 3*4; // in bytes
+    public static final int UID_SIZE = 2*8 + 3*4; // in bytes
 }

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/BasicAction.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/BasicAction.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/BasicAction.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -1767,9 +1767,11 @@
      *
      * Note that at this point the pendingList SHOULD be empty due to the prior
      * invocation of prepare().
+     * 
+     * @throws Error JBTM-895 tests, byteman limitation
      */
 
-    protected synchronized final void phase2Commit (boolean reportHeuristics)
+    protected synchronized final void phase2Commit (boolean reportHeuristics) throws Error
     {
         if (tsLogger.logger.isTraceEnabled()) {
             tsLogger.logger.trace("BasicAction::phase2Commit() for action-id "

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TxControl.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TxControl.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/TxControl.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -160,14 +160,14 @@
         return readonlyOptimisation;
     }
 
-    public static final byte[] getXANodeName()
+    public static final String getXANodeName()
 	{
 		return xaNodeName;
 	}
 
-	public static void setXANodeName(byte[] name)
+	public static void setXANodeName(String name)
 	{
-	    if (name.length > NODE_NAME_SIZE) {
+	    if (name.getBytes().length > 36) {
             tsLogger.i18NLogger.warn_coordinator_toolong();
 
             throw new IllegalArgumentException();
@@ -229,7 +229,7 @@
 
 	private static TransactionStatusManager transactionStatusManager = null;
 
-	static byte[] xaNodeName = null;
+	static String xaNodeName = null;
 
 	static int _defaultTimeout = 60; // 60 seconds
 
@@ -266,7 +266,7 @@
 
 		if (env != null)
 		{
-			xaNodeName = env.getBytes();
+			xaNodeName = env;
 		}
 		else {
             /*
@@ -279,17 +279,17 @@
 
             tsLogger.i18NLogger.warn_coordinator_TxControl_1(nodeName);
 
-            xaNodeName = nodeName.getBytes();
+            xaNodeName = nodeName;
 
             writeNodeName = true;
         }
 
-		if (xaNodeName.length > NODE_NAME_SIZE) {
+		if (xaNodeName.getBytes().length > NODE_NAME_SIZE) {
             String nodeName = DEFAULT_NODE_NAME + Utility.getpid();
 
             tsLogger.i18NLogger.warn_coordinator_TxControl_2(nodeName);
 
-            xaNodeName = nodeName.getBytes();
+            xaNodeName = nodeName;
 
             writeNodeName = true;
         }
@@ -299,7 +299,7 @@
 
             tsLogger.i18NLogger.warn_coordinator_TxControl_3(nodeName);
 
-            xaNodeName = nodeName.getBytes();
+            xaNodeName = nodeName;
 
             writeNodeName = true;
         }
@@ -309,6 +309,7 @@
             arjPropertyManager.getCoreEnvironmentBean().setNodeIdentifier( new String(xaNodeName) );
 		}
 
+
         _enableTSM = arjPropertyManager.getCoordinatorEnvironmentBean().isTransactionStatusManagerEnable();
 
         // TODO -- add this check to respect the environment setting for Environment.START_DISABLED?

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/logging/arjunaI18NLogger.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/logging/arjunaI18NLogger.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/logging/arjunaI18NLogger.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -578,7 +578,7 @@
 	@LogMessage(level = WARN)
 	public void warn_coordinator_notrunning();
 
-	@Message(id = 12138, value = "Node name cannot exceed 64 bytes!", format = MESSAGE_FORMAT)
+	@Message(id = 12138, value = "Node name cannot exceed 36 bytes!", format = MESSAGE_FORMAT)
 	@LogMessage(level = WARN)
 	public void warn_coordinator_toolong();
 
@@ -1446,6 +1446,12 @@
     @Message(id = 12367, value = "Failed to create store dir {0}", format = MESSAGE_FORMAT)
     public String get_dir_create_failed(String arg0);
 
+    @Message(id = 12368, value = "Node identifiers must be an integer and must be 1 or greater: {0}", format = MESSAGE_FORMAT)
+	public String get_node_identifier_invalid(int nodeIdentifier);
+
+    @Message(id = 12369, value = "The node identifier was already set", format = MESSAGE_FORMAT)
+	public String get_node_identifier_reset_attempt();
+
     /*
         Allocate new messages directly above this notice.
           - id: use the next id number in numeric sequence. Don't reuse ids.

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/objectstore/StoreManager.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/objectstore/StoreManager.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/objectstore/StoreManager.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -153,4 +153,8 @@
     public static ObjectStoreAPI getTxOJStore() {
         return (ObjectStoreAPI) setupStore(null, StateType.OS_UNSHARED);
     }
+
+    public static ObjectStoreAPI getEISNameStore() {
+        return (ObjectStoreAPI) setupStore(null, StateType.OS_UNSHARED);
+    }
 }

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ActionBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ActionBean.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ActionBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -1,308 +1,417 @@
 package com.arjuna.ats.arjuna.tools.osb.mbean;
 
+import com.arjuna.ats.arjuna.coordinator.BasicAction;
 import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
 import com.arjuna.ats.arjuna.logging.tsLogger;
-import com.arjuna.ats.arjuna.AtomicAction;
 import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
 import com.arjuna.ats.arjuna.coordinator.RecordList;
-import com.arjuna.ats.arjuna.coordinator.TxControl;
 import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
 import com.arjuna.ats.arjuna.objectstore.StoreManager;
 import com.arjuna.ats.arjuna.tools.osb.util.JMXServer;
 
 import java.lang.reflect.Constructor;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.*;
 
 /**
  * MBean implementation of an ObjectStore entry that represents an AtomicAction
  */
 public class ActionBean extends OSEntryBean implements ActionBeanMBean {
-	// Basic properties this enty
-	private StateManagerWrapper sminfo;
-	// collection of participants belonging to this BasicAction
-	private Collection<LogRecordWrapper> participants = new ArrayList<LogRecordWrapper>();
-	// wrapper around the real AtomicAction
-	private ActionBeanWrapperInterface ra;
+    // Basic properties this enty
+    private StateManagerWrapper sminfo;
+    // collection of participants belonging to this BasicAction
+    private Collection<LogRecordWrapper> participants = new ArrayList<LogRecordWrapper>();
+    // wrapper around the real AtomicAction
+    protected ActionBeanWrapperInterface ra;
 
-	public ActionBean(UidWrapper w) {
-		super(w);
+    public ActionBean(UidWrapper w) {
+        super(w);
 
-		boolean isJTS = JMXServer.isJTS() && w.getType().endsWith("ArjunaTransactionImple");
-		// Participants in a JTS transaction are represented by entries in the ObjectStore
-		List<UidWrapper> recuids = null;
+        boolean isJTS = JMXServer.isJTS() && w.getType().endsWith("ArjunaTransactionImple");
+        // Participants in a JTS transaction are represented by entries in the ObjectStore
+        List<UidWrapper> recuids = null;
 
-		if (isJTS) {
-			try {
-				Class<ActionBeanWrapperInterface> cl = (Class<ActionBeanWrapperInterface>) Class.forName(JMXServer.AJT_WRAPPER_TYPE);
-				Constructor<ActionBeanWrapperInterface> constructor = cl.getConstructor(ActionBean.class, UidWrapper.class);
-				ra = constructor.newInstance(this, w);
-			} catch (Exception e) { // ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException
-				if (tsLogger.logger.isTraceEnabled())
-					tsLogger.logger.trace("Error constructing " + JMXServer.AJT_WRAPPER_TYPE + ": " + e);
-				ra = new AtomicActionWrapper(w);
-			}
-		} else {
-			ra = new AtomicActionWrapper(w);
-		}
+        if (isJTS) {
+            try {
+                Class<ActionBeanWrapperInterface> cl = (Class<ActionBeanWrapperInterface>) Class.forName(JMXServer.AJT_WRAPPER_TYPE);
+                Constructor<ActionBeanWrapperInterface> constructor = cl.getConstructor(ActionBean.class, UidWrapper.class);
+                ra = constructor.newInstance(this, w);
+                ra.activate();
+            } catch (Exception e) { // ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException
+                if (tsLogger.logger.isTraceEnabled())
+                    tsLogger.logger.trace("Error constructing " + JMXServer.AJT_WRAPPER_TYPE + ": " + e);
+                ra = createWrapper(w, true);
+            }
 
-		ra.activate();
-		sminfo = new StateManagerWrapper(StoreManager.getRecoveryStore(), getUid(), getType());
+            /*
+                * for JTS actions the participants will have entries in the ObjectStore.
+                * these entries will be associated with the current MBean (refer to
+                * the method findParticipants below for details)
+                */
+            recuids = w.probe(JMXServer.AJT_RECORD_TYPE, JMXServer.AJT_XAREC_TYPE);
+        } else {
+            ra = createWrapper(w, true);  // com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeManager.manager()
+        }
 
-		if (isJTS) {
-			/*
-			 * for JTS actions the participants will have entries in the ObjectStore.
-			 * these entries will be associated with the current MBean (refer to
-			 * the method findParticipants below for details)
-			 */
-			recuids = w.probe(JMXServer.AJT_RECORD_TYPE, JMXServer.AJT_XAREC_TYPE);
-		}
+        sminfo = new StateManagerWrapper(StoreManager.getRecoveryStore(), getUid(), getType());
 
-		for (ParticipantStatus lt : ParticipantStatus.values()) {
-			findParticipants(recuids, ra.getRecords(lt), lt);
-		}
-	}
+        for (ParticipantStatus lt : ParticipantStatus.values()) {
+            findParticipants(recuids, ra.getRecords(lt), lt);
+        }
+    }
 
-	public StringBuilder toString(String prefix, StringBuilder sb) {
-		ra.toString(prefix, sb);
-		prefix += '\t';
-		sb.append('\n').append(prefix).append(sminfo.getCreationTime());
-		sb.append('\n').append(prefix).append(sminfo.getAgeInSeconds());
+    protected ActionBeanWrapperInterface createWrapper(UidWrapper w, boolean activate) {
+        GenericAtomicActionWrapper action = new GenericAtomicActionWrapper(w.getClassName(), w);
 
-		for (LogRecordWrapper p : participants) {
-			p.toString(prefix, sb);
-		}
+        if (activate)
+            action.activate();
 
-		return sb;
-	}
+        return action;
+    }
 
-	/**
-	 * return the Uid for given AbstractRecord
-	 * @param rec the record whose Uid is required
-	 * @return  the Uid of the requested record
-	 */
-	public Uid getUid(AbstractRecord rec) {
-		return ra.getUid(rec);
-	}
 
-	/**
-	 * Remove this AtomicAction from the ObjectStore
-	 * @return a textual indication of whether the remove operation succeeded
-	 */
-	public String remove() {
-		try {
-			if (!StoreManager.getRecoveryStore().remove_committed(getUid(), getType()))
-				return "remove committed failed"; // TODO com.arjuna.ats.arjuna.tools.osb.mbean.m_1
-			else
-				w.probe();
+    public StringBuilder toString(String prefix, StringBuilder sb) {
+        ra.toString(prefix, sb);
+        prefix += '\t';
+        sb.append('\n').append(prefix).append(sminfo.getCreationTime());
+        sb.append('\n').append(prefix).append(sminfo.getAgeInSeconds());
 
-			return "remove ok"; // TODO com.arjuna.ats.arjuna.tools.osb.mbean.m_2
-		} catch (ObjectStoreException e) {
-			return "remove committed exception: " + e.getMessage(); // TODO com.arjuna.ats.arjuna.tools.osb.mbean.m_3
-		}
-	}
+        for (LogRecordWrapper p : participants) {
+            p.toString(prefix, sb);
+        }
 
-	/**
-	 * create MBean representations of the participants of this transaction
-	 * @param recuids some transaction participants are represented in the ObjectStore
-	 * - if this is the case then recuids contains a list of MBean wrappers representing them.
-	 * Otherwise this list will be empty.
-	 * @param list the records representing the participants
-	 * @param listType indicates the type of the records in list (PREPARED, PENDING, FAILED, READONLY, HEURISTIC)
-	 */
-	private void findParticipants(List<UidWrapper> recuids, RecordList list, ParticipantStatus listType) {
-		if (list != null) {
-			for (AbstractRecord rec = list.peekFront(); rec != null; rec = list.peekNext(rec)) {
-				LogRecordWrapper lw;
-				int i = recuids == null ? -1 : recuids.indexOf(new UidWrapper(ra.getUid(rec)));
+        return sb;
+    }
 
-				if (i != -1) {
-					OSEntryBean p = recuids.get(i).getMBean();
+    /**
+     * return the Uid for given AbstractRecord
+     * @param rec the record whose Uid is required
+     * @return  the Uid of the requested record
+     */
+    public Uid getUid(AbstractRecord rec) {
+        return ra.getUid(rec);
+    }
 
-					if (p instanceof LogRecordWrapper) {
-						lw = (LogRecordWrapper) p;
-						lw.init(this, rec, listType);
-					} else {
-						if (tsLogger.logger.isTraceEnabled())
-							tsLogger.logger.trace("participant record is not a LogRecordWrapper");
-						lw = createParticipant(rec, listType);
-					}
-				} else {
-					lw = createParticipant(rec, listType);
-				}
+    /**
+     * Remove this AtomicAction from the ObjectStore
+     * @return a textual indication of whether the remove operation succeeded
+     */
+    public String remove() {
+        try {
+            if (!StoreManager.getRecoveryStore().remove_committed(getUid(), getType()))
+                return "Attempt to remove transaction failed";
+            else
+                w.probe();
 
-				lw.activate();				
-				participants.add(lw);
-			}
-		}
-	}
+            return "Transaction successfully removed";
+        } catch (ObjectStoreException e) {
+            return "Unable to remove transaction: " + e.getMessage();
+        }
+    }
 
-	/**
-	 * Extension point for other Bean implementations to provide an implementation bean for its participants.
-	 * For example @see com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean
-	 * @param rec the record that should be represented by an MBean
-	 * @param listType the status of the record
-	 * @return the MBean implementation of the participant
-	 */
-	protected LogRecordWrapper createParticipant(AbstractRecord rec, ParticipantStatus listType) {
-		return new LogRecordWrapper(this, rec, listType);
-	}
+    /**
+     * create MBean representations of the participants of this transaction
+     * @param recuids some transaction participants are represented in the ObjectStore
+     * - if this is the case then recuids contains a list of MBean wrappers representing them.
+     * Otherwise this list will be empty.
+     * @param list the records representing the participants
+     * @param listType indicates the type of the records in list (PREPARED, PENDING, FAILED, READONLY, HEURISTIC)
+     */
+    private void findParticipants(List<UidWrapper> recuids, RecordList list, ParticipantStatus listType) {
+        if (list != null) {
+            for (AbstractRecord rec = list.peekFront(); rec != null; rec = list.peekNext(rec)) {
+                LogRecordWrapper lw;
+                int i = recuids == null ? -1 : recuids.indexOf(new UidWrapper(ra.getUid(rec)));
 
-	/**
-	 * See if there is participant Bean corresponding to the given record
-	 * @param rec the record for the target participant
-	 * @return the bean corresponding to the requested record
-	 */
-	public LogRecordWrapper getParticipant(AbstractRecord rec) {
-		for (LogRecordWrapper w : participants)
-			if (w.getRecord().equals(rec))
-				return w;
+                if (i != -1) {
+                    OSEntryBean p = recuids.get(i).getMBean();
 
-		return null;
-	}
+                    if (p instanceof LogRecordWrapper) {
+                        lw = (LogRecordWrapper) p;
+                        lw.init(this, rec, listType);
+                    } else {
+                        if (tsLogger.logger.isTraceEnabled())
+                            tsLogger.logger.trace("participant record is not a LogRecordWrapper");
+                        lw = createParticipant(rec, listType);
+                    }
+                } else {
+                    lw = createParticipant(rec, listType);
+                }
 
-	/**
-	 * register this bean (and its participants) with the MBeanServer
-	 */
-	public void register() {
-		super.register();
+                lw.activate();
+                participants.add(lw);
+            }
+        }
+    }
 
-		for (LogRecordWrapper p : participants)
-			JMXServer.getAgent().registerMBean(p.getName(), p);
-	}
+    /**
+     * Extension point for other Bean implementations to provide an implementation bean for its participants.
+     * For example @see com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean
+     * @param rec the record that should be represented by an MBean
+     * @param listType the status of the record
+     * @return the MBean implementation of the participant
+     */
+    protected LogRecordWrapper createParticipant(AbstractRecord rec, ParticipantStatus listType) {
+        return new LogRecordWrapper(this, rec, listType);
+    }
 
-	/**
-	 * unregister this bean (and its participants) with the MBeanServer
-	 */
-	public void unregister() {
-		for (LogRecordWrapper p : participants)
-			JMXServer.getAgent().unregisterMBean(p.getName());
+    /**
+     * See if there is participant Bean corresponding to the given record
+     * @param rec the record for the target participant
+     * @return the bean corresponding to the requested record
+     */
+    public LogRecordWrapper getParticipant(AbstractRecord rec) {
+        for (LogRecordWrapper w : participants)
+            if (w.getRecord().equals(rec))
+                return w;
 
-		super.unregister();
-	}
+        return null;
+    }
 
-	public long getAgeInSeconds() {
-		return sminfo.getAgeInSeconds();
-	}
+    /**
+     * register this bean (and its participants) with the MBeanServer
+     */
+    public void register() {
+        super.register();
 
-	public String getCreationTime() {
-		return sminfo.getCreationTime();
-	}
+        for (LogRecordWrapper p : participants)
+            JMXServer.getAgent().registerMBean(p.getName(), p);
+    }
 
-	public boolean isParticipant() {
-		return false;
-	}
+    /**
+     * unregister this bean (and its participants) with the MBeanServer
+     */
+    public void unregister() {
+        for (LogRecordWrapper p : participants)
+            JMXServer.getAgent().unregisterMBean(p.getName());
 
-	/**
-	 * Request a change in status of a participant. For example if a record has a
-	 * heuristic status then this method could be used to move it back into the
-	 * prepared state so that the recovery system can replay phase 2 of the
-	 * committment protocol
-	 * @param logrec the record whose status is to be changed
-	 * @param newStatus the desired status
-	 * @return true if the status was changed
-	 */
-	public boolean setStatus(LogRecordWrapper logrec, ParticipantStatus newStatus) {
-		ParticipantStatus lt = logrec.getListType();
-		AbstractRecord targRecord = logrec.getRecord();
+        super.unregister();
+    }
 
-		RecordList oldList = ra.getRecords(lt);
-		RecordList newList = ra.getRecords(newStatus);
+    public long getAgeInSeconds() {
+        return sminfo.getAgeInSeconds();
+    }
 
-		if (lt.equals(ParticipantStatus.HEURISTIC) && !targRecord.forgetHeuristic()) {
-			return false;
-		}
+    public String getCreationTime() {
+        return sminfo.getCreationTime();
+    }
 
-		// move the record from currList to targList
-		if (oldList.remove(targRecord)) {
-			
-			if (newList.insert(targRecord)) {
-				if (lt.equals(ParticipantStatus.HEURISTIC)) {
-					switch (newStatus) {
-						case FAILED:
-							ra.clearHeuristicDecision(TwoPhaseOutcome.FINISH_ERROR);
-							break;
-						case PENDING:
-							ra.clearHeuristicDecision(TwoPhaseOutcome.NOT_PREPARED);
-							break;
-						case PREPARED:
-							ra.clearHeuristicDecision(TwoPhaseOutcome.PREPARE_OK);
-							break;
-						case READONLY:
-							ra.clearHeuristicDecision(TwoPhaseOutcome.PREPARE_READONLY);
-							break;
-						default:
-							break;
-					}
-				}
+    public boolean isParticipant() {
+        return false;
+    }
 
-				ra.doUpdateState();
-				
-				return true;
-			}
-		}
+    /**
+     * Request a change in status of a participant. For example if a record has a
+     * heuristic status then this method could be used to move it back into the
+     * prepared state so that the recovery system can replay phase 2 of the
+     * commitment protocol
+     * @param logrec the record whose status is to be changed
+     * @param newStatus the desired status
+     * @return true if the status was changed
+     */
+    public boolean setStatus(LogRecordWrapper logrec, ParticipantStatus newStatus) {
+        ParticipantStatus lt = logrec.getListType();
+        AbstractRecord targRecord = logrec.getRecord();
 
-		return false;
-	}
+        RecordList oldList = ra.getRecords(lt);
+        RecordList newList = ra.getRecords(newStatus);
 
-	/**
-	 *
-	 * @return the MBeans corresponding to the participants within this action
-	 */
-	public Collection<LogRecordWrapper> getParticipants() {
-		return Collections.unmodifiableCollection(participants);
-	}
+        if (lt.equals(ParticipantStatus.HEURISTIC) && !targRecord.forgetHeuristic()) {
+            return false;
+        }
 
-	/**
-	 * The ActionBean needs access to the participant lists maintained by an AtomicAction but these
-	 * lists are protected. Therefore define a simple extension class to get at these records:
-	 */
-	class AtomicActionWrapper extends AtomicAction implements ActionBeanWrapperInterface {
-		boolean activated;
+        // move the record from currList to targList
+        if (oldList.remove(targRecord)) {
 
-		public AtomicActionWrapper(UidWrapper w) {
-			super(w.getUid());
-		}
+            if (newList.insert(targRecord)) {
+                if (lt.equals(ParticipantStatus.HEURISTIC)) {
+                    switch (newStatus) {
+                        case FAILED:
+                            ra.clearHeuristicDecision(TwoPhaseOutcome.FINISH_ERROR);
+                            break;
+                        case PENDING:
+                            ra.clearHeuristicDecision(TwoPhaseOutcome.NOT_PREPARED);
+                            break;
+                        case PREPARED:
+                            ra.clearHeuristicDecision(TwoPhaseOutcome.PREPARE_OK);
+                            break;
+                        case READONLY:
+                            ra.clearHeuristicDecision(TwoPhaseOutcome.PREPARE_READONLY);
+                            break;
+                        default:
+                            break;
+                    }
+                }
 
-		public boolean activate() {
-			if (!activated)
-				activated = super.activate();
+                ra.doUpdateState();
 
-			return activated;
-		}
+                return true;
+            }
+        }
 
-		public void doUpdateState() {
-			updateState();
-		}
+        return false;
+    }
 
-		public Uid getUid(AbstractRecord rec) {
-			return get_uid();
-		}
-		
-		public StringBuilder toString(String prefix, StringBuilder sb) {
-			prefix += '\t';
-			return sb.append('\n').append(prefix).append(get_uid());
-		}
+    /**
+     *
+     * @return the MBeans corresponding to the participants within this action
+     */
+    public Collection<LogRecordWrapper> getParticipants() {
+        return Collections.unmodifiableCollection(participants);
+    }
 
-		public void clearHeuristicDecision(int newDecision) {
-			if (super.heuristicList.size() == 0)
-				setHeuristicDecision(newDecision);
-		}
+    /**
+     * The ActionBean needs access to the participant lists maintained by an AtomicAction but these
+     * lists are protected. Therefore define a simple extension class to get at these records:
+     */
+    public class GenericAtomicActionWrapper implements ActionBeanWrapperInterface {
+        boolean activated;
+        BasicAction action;
+        Map<String, RecordList> recs;
+        Method setHeuristicDecision;
+        Method updateState = null;
+        UidWrapper uidWrapper;
 
-		public RecordList getRecords(ParticipantStatus type) {
-			switch (type) {
-				default:
-				case PREPARED: return preparedList;
-				case FAILED: return failedList;
-				case HEURISTIC: return heuristicList;
-				case PENDING: return pendingList;
-				case READONLY: return readonlyList;
-			}
-		}
-	}
+        public GenericAtomicActionWrapper(String classType, UidWrapper w) {
+            uidWrapper = w;
+            recs = new HashMap<String, RecordList>();
 
+            if (classType == null)
+                classType = "com.arjuna.ats.arjuna.AtomicAction";
+
+            try {
+                Class cls = Class.forName(classType);
+                Class pTypes[] = new Class[1];
+                pTypes[0] = Uid.class;
+                Constructor ctor = cls.getConstructor(pTypes);
+                Object args[] = new Object[1];
+                args[0] = w.getUid();
+                action = (BasicAction) ctor.newInstance(args);
+
+                setHeuristicDecision = getMethod(action.getClass(), "setHeuristicDecision", int.class);
+                updateState = getMethod(action.getClass(), "updateState");
+
+                if (setHeuristicDecision != null)
+                    setHeuristicDecision.setAccessible(true);
+
+                if (updateState != null)
+                    updateState.setAccessible(true);
+
+            } catch (Exception e) {
+                action = null;
+
+                if (tsLogger.logger.isDebugEnabled())
+                    tsLogger.logger.debug("unable to create log wrapper for type " + w.getType() + ": error: " + e.getMessage());
+            }
+        }
+
+        public BasicAction getAction() {
+            return action;
+        }
+
+        public boolean activate() {
+            if (!activated && action != null) {
+                activated = action.activate();
+            }
+
+            return activated;
+        }
+
+        public void doUpdateState() {
+            if (updateState != null && action != null) {
+                try {
+                    updateState.invoke(action);
+                } catch (IllegalAccessException e) {
+                    if (tsLogger.logger.isDebugEnabled())
+                        tsLogger.logger.debug("failed to update heuristic for " + action.toString() + ": error: " + e.getMessage());
+                } catch (InvocationTargetException e) {
+                    if (tsLogger.logger.isDebugEnabled())
+                        tsLogger.logger.debug("failed to update heuristic for " + action.toString() + ": error: " + e.getMessage());
+                }
+            }
+        }
+
+        public Uid get_uid() {
+            return action != null ? action.get_uid() : uidWrapper.getUid();
+        }
+
+        public Uid getUid(AbstractRecord rec) {
+            return rec.order(); //get_uid();
+        }
+
+        public StringBuilder toString(String prefix, StringBuilder sb) {
+            prefix += '\t';
+            return sb.append('\n').append(prefix).append(get_uid());
+        }
+
+        public void clearHeuristicDecision(int newDecision) {
+            RecordList rl = getRecords("heuristicList");
+
+            if (setHeuristicDecision != null && rl != null && rl.size() == 0)
+                try {
+                    setHeuristicDecision.invoke(action, newDecision);
+                } catch (IllegalAccessException e) {
+                    if (tsLogger.logger.isDebugEnabled())
+                        tsLogger.logger.debug("failed to update heuristic for " + action.toString() + ": error: " + e.getMessage());
+                } catch (InvocationTargetException e) {
+                    if (tsLogger.logger.isDebugEnabled())
+                        tsLogger.logger.debug("failed to update heuristic for " + action.toString() + ": error: " + e.getMessage());
+                }
+        }
+
+        private Field getField(Class cl, String fn) {
+            try {
+                return cl.getDeclaredField(fn);
+            } catch (NoSuchFieldException e) {
+                return getField(cl.getSuperclass(), fn);
+            }
+        }
+
+        private Method getMethod(Class cl, String mn, Class<?>... parameterTypes) {
+            try {
+                if (cl == null)
+                    return null;
+                return cl.getDeclaredMethod(mn, parameterTypes);
+            } catch (NoSuchMethodException e) {
+                return getMethod(cl.getSuperclass(), mn, parameterTypes);
+            }
+        }
+
+        public RecordList getRecords(String ln) {
+            if (action == null)
+                return null;
+
+            if (recs.containsKey(ln))
+                return recs.get(ln);
+
+            Field f = getField(action.getClass(), ln);
+            f.setAccessible(true);
+            try {
+                RecordList rl = (RecordList) f.get(action);
+
+                if (rl != null)
+                    recs.put(ln, rl);
+
+                return rl;
+            } catch (IllegalAccessException e) {
+                return null;
+            }
+        }
+
+        public RecordList getRecords(ParticipantStatus type) {
+
+            switch (type) {
+                default:
+                case PREPARED: return getRecords("preparedList");
+                case FAILED: return getRecords("failedList");
+                case HEURISTIC: return getRecords("heuristicList");
+                case PENDING: return getRecords("pendingList");
+                case READONLY: return getRecords("readonlyList");
+            }
+        }
+    }
 }

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ActionBeanWrapperInterface.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ActionBeanWrapperInterface.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ActionBeanWrapperInterface.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -2,6 +2,7 @@
 
 import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
+import com.arjuna.ats.arjuna.coordinator.BasicAction;
 import com.arjuna.ats.arjuna.coordinator.RecordList;
 
 /**
@@ -14,6 +15,7 @@
 	Uid get_uid();
 	Uid getUid(AbstractRecord rec);
 	StringBuilder toString(String prefix, StringBuilder sb);
+    BasicAction getAction();
 
     void clearHeuristicDecision(int newDecision);
 }
\ No newline at end of file

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/LogRecordWrapper.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/LogRecordWrapper.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/LogRecordWrapper.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -65,11 +65,18 @@
 	}
 
 	public String setStatus(ParticipantStatus newState) {
+        if (getListType().equals(newState))
+            return "participant is prepared for recovery";
+
 		if (parent != null && parent.setStatus(this, newState)) {
 			listType = newState;
-			return "status change was successful";
+
+            if (newState == ParticipantStatus.PREPARED )
+			    return "participant recovery will be attempted during the next recovery pass";
+
+            return "participant status change was successful";
 		} else {
-			return "failed";
+			return "participant status change failed";
 		}
 	}
 

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowser.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowser.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowser.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -4,203 +4,248 @@
 import java.net.URL;
 import java.util.*;
 
+import com.arjuna.ats.arjuna.StateManager;
+import com.arjuna.ats.arjuna.common.arjPropertyManager;
 import com.arjuna.ats.arjuna.logging.tsLogger;
 import com.arjuna.ats.arjuna.common.Uid;
-import com.arjuna.ats.arjuna.coordinator.TxControl;
 import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
 import com.arjuna.ats.arjuna.objectstore.ObjectStoreIterator;
 import com.arjuna.ats.arjuna.objectstore.StoreManager;
 import com.arjuna.ats.arjuna.state.InputObjectState;
 import com.arjuna.ats.arjuna.tools.osb.util.JMXServer;
 
-import com.arjuna.ats.arjuna.common.*;
-
 /**
  * An MBean implementation for walking an ObjectStore and creating/deleting MBeans
  * that represent completing transactions (ie ones on which the user has called commit)
  */
 public class ObjStoreBrowser implements ObjStoreBrowserMBean {
-	private static final String STORE_MBEAN_NAME = "jboss.jta:type=ObjectStore";
-	private static final String OS_BEAN_PROPFILE = "osmbean.properties";
+    public static final String OBJ_STORE_BROWSER_HANDLERS = "com.arjuna.ats.arjuna.tools.osb.mbean.ObjStoreBrowserHandlers";
+    private static final String STORE_MBEAN_NAME = "jboss.jta:type=ObjectStore";
 
-    // define which object store types can be represented by mbeans
-	private Properties typeHandlers;
+    // defines a (default) map of object store types to the corresponding MBean for instrumentation.
+    // The format is OSType1=BeanType1,OSType2=BeanType2,etc
+    // Can be over-ridden by setting a system property called com.arjuna.ats.arjuna.tools.osb.mbean.ObjStoreBrowserHandlers
+    private static final String saaStateType = "com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.SubordinateAtomicAction";
+    private static final String saaBeanType = "com.arjuna.ats.internal.jta.tools.osb.mbean.jta.SubordinateActionBean";
+    private static final String defaultStateHandlers =
+            "com.arjuna.ats.arjuna.AtomicAction=com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean"
+//            + ",com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.SubordinateAtomicAction=com.arjuna.ats.internal.jta.tools.osb.mbean.jta.SubordinateActionBean"
+                    + ",com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ArjunaTransactionImpleWrapper=com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean";
 
-	private Map<String, List<UidWrapper>> allUids;
+    private Map<String, String> stateTypes = null; // defines which object store types will be instrumented
+    private Map<String, String> beanTypes = null;  // defines which bean types are used to represent object store types
+    private Map<String, List<UidWrapper>> allUids;
 
-	public static void main(String[] args) throws Exception {
-		InputStreamReader isr = new InputStreamReader(System.in);
-		BufferedReader br = new BufferedReader(isr);
-		String logDir = System.getProperty("objectstore.dir");
-		ObjStoreBrowser browser = new ObjStoreBrowser(logDir);
-
-		browser.start();
-
-		do {
-			System.out.println("> "); System.out.flush();
-			String[] req = br.readLine().split("\\s+");
-
-			if (req.length == 0 || "quit".equals(req[0]))
-				break;
-
-			browser.probe();
-
-			if ("dump".equals(req[0])) {
-				if (req.length == 1) {
-					System.out.println("Uid not found");
-				} else {
-					UidWrapper w = browser.findUid(req[1]);
-
-					if (w != null)
-						System.out.println("Attributes: " + w.toString("", new StringBuilder()));
-					else
-						System.out.println("Uid not found: " + req[1]);
-				}
-			} else if ("list".equals(req[0])) {
-				System.out.println(browser.dump(new StringBuilder()));
-			} //else if ("query".equals(req[0])) {
-			  //  browser.queryTest();
-		} while (true);
-	}
-
-	public static Properties loadProperties(String fname) {
-		Properties properties = new Properties();
-		URL url = ClassLoader.getSystemResource(fname);
-		try {
-			if (url != null)
-				properties.load(url.openStream());
-		} catch (IOException e) {
-		}
-
-		return properties;
-	}
-
     /**
      * Initialise the MBean
      */
-	public void start()
-	{
-		JMXServer.getAgent().registerMBean(STORE_MBEAN_NAME, this);
-	}
+    public void start()
+    {
+        JMXServer.getAgent().registerMBean(STORE_MBEAN_NAME, this);
+    }
 
     /**
      * Unregister all MBeans representing objects in the ObjectStore
      * represented by this MBean
      */
-	public void stop()
-	{
-		for (List<UidWrapper> uids : allUids.values()) {
-			for (Iterator<UidWrapper> i = uids.iterator(); i.hasNext(); ) {
-				UidWrapper w = i.next();
-				i.remove();
-				w.unregister();
-			}
-		}
+    public void stop()
+    {
+        for (List<UidWrapper> uids : allUids.values()) {
+            for (Iterator<UidWrapper> i = uids.iterator(); i.hasNext(); ) {
+                UidWrapper w = i.next();
+                i.remove();
+                w.unregister();
+            }
+        }
 
-		JMXServer.getAgent().unregisterMBean(STORE_MBEAN_NAME);
-	}
+        JMXServer.getAgent().unregisterMBean(STORE_MBEAN_NAME);
+    }
 
     /**
+     * This method is deprecated in favour of @setType
+     * The issue with this method is there is no mechanism for determining which class
+     * is responsible for a given OS type.
+     *
      * Define which object store types will registered as MBeans
      * @param types the list of ObjectStore types that can be represented
      * as MBeans
      */
-	public void setTypes(Map<String, String> types) {
-		for (Map.Entry<String, String> entry : types.entrySet()) {
-			if (tsLogger.logger.isTraceEnabled())
-				tsLogger.logger.trace("ObjStoreBrowser: adding type handler " + entry.getKey() + "," + entry.getValue());
-			typeHandlers.put(entry.getKey(), entry.getValue());
-		}
-	}
+    @Deprecated
+    public void setTypes(Map<String, String> types) {
+    }
 
-	private void init(String logDir) {
-		if (logDir != null)
-			arjPropertyManager.getObjectStoreEnvironmentBean().setObjectStoreDir(logDir);
+    /**
+     * Tell the browser which beans to use for particular Object Store Action type
+     * @param osTypeClassName
+     * @param beanTypeClassName
+     * @return
+     */
+    public boolean setType(String osTypeClassName, String beanTypeClassName) {
+        String typeName = getOSType(osTypeClassName);
 
-		if (tsLogger.logger.isTraceEnabled())
-			tsLogger.logger.trace("ObjectStoreDir: " + arjPropertyManager.getObjectStoreEnvironmentBean().getObjectStoreDir());
+        if (typeName != null) {
 
-		allUids = new HashMap<String, List<UidWrapper>> ();
-		typeHandlers = loadProperties(OS_BEAN_PROPFILE);
-        
-		if (typeHandlers.size() == 0)
-			typeHandlers = loadProperties("META-INF/" + OS_BEAN_PROPFILE);
-	}
+            if (typeName.startsWith("/"))
+                typeName = typeName.substring(1);
 
-	public ObjStoreBrowser() {
-		init(null);
-	}
+            stateTypes.put(typeName, osTypeClassName);
+            beanTypes.put(typeName, beanTypeClassName);
 
-	public ObjStoreBrowser(String logDir) {
-		init(logDir);
-	}
+            return true;
+        }
 
-	public StringBuilder dump(StringBuilder sb) {
-		for (Map.Entry<String, List<UidWrapper>> typeEntry : allUids.entrySet()) {
-			sb.append(typeEntry.getKey()).append('\n');
+        return false;
+    }
 
-			for (UidWrapper uid : typeEntry.getValue())
-				uid.toString("\t", sb);
-		}
+    private void initTypeHandlers(String handlers) {
+        for (String h : handlers.split(",")) {
+            String[] handler = h.split("=");
 
-		return sb;
-	}
+            if (handler.length == 2) {
+                String typeName = getOSType(handler[0]);
 
+                if (typeName != null) {
+
+                    if (typeName.startsWith("/"))
+                        typeName = typeName.substring(1);
+
+                    stateTypes.put(typeName, handler[0]);
+                    beanTypes.put(typeName, handler[1]);
+                }
+            }
+        }
+    }
+
+    private void init(String logDir) {
+        if (logDir != null)
+            arjPropertyManager.getObjectStoreEnvironmentBean().setObjectStoreDir(logDir);
+
+        if (tsLogger.logger.isTraceEnabled())
+            tsLogger.logger.trace("ObjectStoreDir: " + arjPropertyManager.getObjectStoreEnvironmentBean().getObjectStoreDir());
+
+        allUids = new HashMap<String, List<UidWrapper>> ();
+        stateTypes = new HashMap<String, String>();
+        beanTypes = new HashMap<String, String>();
+
+        initTypeHandlers(defaultStateHandlers);
+        initTypeHandlers(System.getProperty(OBJ_STORE_BROWSER_HANDLERS, ""));
+    }
+
+    public ObjStoreBrowser() {
+        init(null);
+    }
+
+    public ObjStoreBrowser(String logDir) {
+        init(logDir);
+    }
+
+    public StringBuilder dump(StringBuilder sb) {
+        for (Map.Entry<String, List<UidWrapper>> typeEntry : allUids.entrySet()) {
+            sb.append(typeEntry.getKey()).append('\n');
+
+            for (UidWrapper uid : typeEntry.getValue())
+                uid.toString("\t", sb);
+        }
+
+        return sb;
+    }
+
     /**
      * See if the given uid has previously been registered as an MBean
      * @param uid the unique id representing an ObjectStore entry
      * @return the MBean wrapper corresponding to the requested Uid (or null
      * if it hasn't been registered)
      */
-	public UidWrapper findUid(Uid uid) {
-		return findUid(uid.stringForm());
-	}
+    public UidWrapper findUid(Uid uid) {
+        return findUid(uid.stringForm());
+    }
 
-	public UidWrapper findUid(String uid) {
-		for (Map.Entry<String, List<UidWrapper>> typeEntry : allUids.entrySet())
-			for (UidWrapper w : typeEntry.getValue())
-				if (w.getUid().stringForm().equals(uid))
-					return w;
+    public UidWrapper findUid(String uid) {
+        for (Map.Entry<String, List<UidWrapper>> typeEntry : allUids.entrySet())
+            for (UidWrapper w : typeEntry.getValue())
+                if (w.getUid().stringForm().equals(uid))
+                    return w;
 
-		return null;
-	}
+        return null;
+    }
+    ;
+    private String getOSType(String classType) {
+        try {
+            Class cls = Class.forName(classType);
+            StateManager sm = (StateManager) cls.getConstructor().newInstance();
 
+            return sm.type();
+        } catch (Exception e) {
+            if (tsLogger.logger.isDebugEnabled())
+                tsLogger.logger.debug("Invalid class type in system property ObjStoreBrowserHandlers: " + classType);
+        }
+
+        return null;
+    }
     /**
      * See if any new MBeans need to be registered or if any existing MBeans no longer exist
      * as ObjectStore entries.
      */
-	public void probe() {
-		InputObjectState types = new InputObjectState();
+    public void probe() {
+        InputObjectState types = new InputObjectState();
 
-		try {
-			if (StoreManager.getRecoveryStore().allTypes(types)) {
-				String tname;
+        try {
+            if (StoreManager.getRecoveryStore().allTypes(types)) {
+                String tname;
 
-				do {
-					try {
-						tname = types.unpackString();
+                do {
+                    try {
+                        tname = types.unpackString();
 
-						if (tname.length() != 0) {
-							List<UidWrapper> uids = allUids.get(tname);
-							if (uids == null) {
-								uids = new ArrayList<UidWrapper> ();
-								allUids.put(tname, uids);
-							}
+                        if (tname.length() != 0) {
+                            List<UidWrapper> uids = allUids.get(tname);
 
-							if (typeHandlers.containsKey(tname))
-								updateMBeans(uids, System.currentTimeMillis(), true, tname, typeHandlers.getProperty(tname));
-						}
-					} catch (IOException e1) {
-						tname = "";
-					}
-				} while (tname.length() != 0);
-			}
-		} catch (ObjectStoreException e2) {
-			if (tsLogger.logger.isTraceEnabled())
-				tsLogger.logger.trace(e2.toString());
-		}
-	}
+                            if (uids == null) {
+                                uids = new ArrayList<UidWrapper> ();
+                                allUids.put(tname, uids);
+                            }
 
+                            if (beanTypes.containsKey(tname))
+                                updateMBeans(uids, System.currentTimeMillis(), true, tname);
+                        }
+                    } catch (IOException e1) {
+                        tname = "";
+                    }
+                } while (tname.length() != 0);
+            }
+        } catch (ObjectStoreException e2) {
+            if (tsLogger.logger.isTraceEnabled())
+                tsLogger.logger.trace(e2.toString());
+        }
+    }
+
+    public void viewSubordinateAtomicActions(boolean enable) {
+        if (enable) {
+            setType(saaStateType, saaBeanType);
+        } else {
+            String typeName = getOSType(saaStateType);
+
+            if (typeName != null) {
+
+                if (typeName.startsWith("/"))
+                    typeName = typeName.substring(1);
+
+                stateTypes.remove(typeName);
+                beanTypes.remove(typeName);
+
+                for (List<UidWrapper> uids : allUids.values()) {
+                    for (Iterator<UidWrapper> i = uids.iterator(); i.hasNext(); ) {
+                        UidWrapper w = i.next();
+                        if (saaStateType.equals(w.getClassName())) {
+                            i.remove();
+                            w.unregister();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Register new MBeans of the requested type (or unregister ones whose
      * corresponding ObjectStore entry has been removed)
@@ -209,47 +254,48 @@
      * the request type
      * @return the list of MBeans representing the requested ObjectStore type
      */
-	public List<UidWrapper> probe(String type, String beantype) {
-		if (!allUids.containsKey(type))
-			return null;
+    public List<UidWrapper> probe(String type, String beantype) {
+        if (!allUids.containsKey(type))
+            return null;
 
-		List<UidWrapper> uids = allUids.get(type);
+        List<UidWrapper> uids = allUids.get(type);
 
-		updateMBeans(uids, System.currentTimeMillis(), false, type, beantype);
+        updateMBeans(uids, System.currentTimeMillis(), false, type);
 
-		return uids;
-	}
+        return uids;
+    }
 
-	private void updateMBeans(List<UidWrapper> uids, long tstamp, boolean register, String type, String thandler) {
-		ObjectStoreIterator iter = new ObjectStoreIterator(StoreManager.getRecoveryStore(), type);
+    private void updateMBeans(List<UidWrapper> uids, long tstamp, boolean register, String type) {
+        ObjectStoreIterator iter = new ObjectStoreIterator(StoreManager.getRecoveryStore(), type);
 
-		while (true) {
-			Uid u = iter.iterate();
-			if (Uid.nullUid().equals(u))
-				break;
+        while (true) {
+            Uid u = iter.iterate();
+            if (u == null || Uid.nullUid().equals(u))
+                break;
 
-			UidWrapper w = new UidWrapper(this, thandler, type, u);
-			int i = uids.indexOf(w);
 
-			if (i == -1) {
-				w.setTimestamp(tstamp);
-				uids.add(w);
-				w.createMBean();
-				if (register)
-					w.register();
-			} else {
-				uids.get(i).setTimestamp(tstamp);
-			}
-		}
+            UidWrapper w = new UidWrapper(this, beanTypes.get(type), type, stateTypes.get(type), u);
+            int i = uids.indexOf(w);
 
-		for (Iterator<UidWrapper> i = uids.iterator(); i.hasNext(); ) {
-			UidWrapper w = i.next();
+            if (i == -1) {
+                w.setTimestamp(tstamp);
+                uids.add(w);
+                w.createMBean();
+                if (register)
+                    w.register();
+            } else {
+                uids.get(i).setTimestamp(tstamp);
+            }
+        }
 
-			if (w.getTimestamp() != tstamp) {
-				if (register)
-					w.unregister();
-				i.remove();
-			}
-		}
-	}
+        for (Iterator<UidWrapper> i = uids.iterator(); i.hasNext(); ) {
+            UidWrapper w = i.next();
+
+            if (w.getTimestamp() != tstamp) {
+                if (register)
+                    w.unregister();
+                i.remove();
+            }
+        }
+    }
 }

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowserMBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowserMBean.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/ObjStoreBrowserMBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -7,4 +7,7 @@
 public interface ObjStoreBrowserMBean extends ObjStoreItemMBean {
 	@MXBeanPropertyDescription("See if any new transactions have been created or completed")
 	void probe();
+	@MXBeanPropertyDescription("Enable/disable viewing of Subordinate Atomic Actions (afterwards use the probe operation to rescan the store):"
+            + " WARNING THIS OPERATION WILL TRIGGER A RECOVERY ATTEMPT (recovery is normally performed by the Recovery Manager). Use the text \"true\" to enable")
+	void viewSubordinateAtomicActions(boolean enable);
 }

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/UidWrapper.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/UidWrapper.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/tools/osb/mbean/UidWrapper.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -13,6 +13,7 @@
 	private String name;
 	private ObjStoreBrowser browser;
 	private String beantype;
+    private String className;
 	private String ostype;
 	private Uid uid;
 	private long tstamp;
@@ -23,19 +24,22 @@
 		this.name = "";
 		this.beantype = "";
 		this.ostype = "";
+        this.className = null;
 	}
 
 	public OSEntryBean getMBean() {
 		return mbean;
 	}
 
-	public UidWrapper(ObjStoreBrowser browser, String beantype, String ostype, Uid uid) {
+	public UidWrapper(ObjStoreBrowser browser, String beantype, String ostype, String className, Uid uid) {
 		this.browser = browser;
 		this.ostype = ostype;
 		this.beantype = beantype;
+        this.className = className;
 		this.uid = uid;
 		this.tstamp = 0L;
 		this.name = "jboss.jta:type=ObjectStore,itype=" + ostype + ",uid=" + uid.fileStringForm(); // + ",participant=false";
+
 	}
 
     /**
@@ -53,6 +57,10 @@
 		return name;
 	}
 
+	public String getClassName() {
+		return className;
+	}
+
 	void register() {
 		mbean.register();
 	}

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/abstractrecords/LastResourceRecord.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/abstractrecords/LastResourceRecord.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/classes/com/arjuna/ats/internal/arjuna/abstractrecords/LastResourceRecord.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -160,6 +160,8 @@
     @Override
     public String toString()
     {
+
+
         return "LastResourceRecord("+_lro+")";
     }
 

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/atomicaction/TxControlUnitTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/atomicaction/TxControlUnitTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/atomicaction/TxControlUnitTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -64,10 +64,10 @@
         
         assertEquals(TxControl.getMaintainHeuristics(), arjPropertyManager.getCoordinatorEnvironmentBean().isMaintainHeuristics());
         
-        String nodeName = "NodeName";
+        String nodeName = "1";
         
-        TxControl.setXANodeName(nodeName.getBytes());
+        TxControl.setXANodeName(nodeName);
         
-        assertEquals(new String(TxControl.getXANodeName()), nodeName);
+        assertTrue(TxControl.getXANodeName().equals(nodeName));
     }
 }

Modified: labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/tools/ObjStoreBrowserTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/tools/ObjStoreBrowserTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaCore/arjuna/tests/classes/com/hp/mwtests/ts/arjuna/tools/ObjStoreBrowserTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -73,12 +73,10 @@
 	 * @return An object that maintains MBeans representing completing transactions
 	 */
 	private ObjStoreBrowser createObjStoreBrowser() {
-		ObjStoreBrowser osb = new ObjStoreBrowser();
+        ObjStoreBrowser osb = new ObjStoreBrowser();
 
 		// define which object store types we are prepared to represent by mbeans
-		osb.setTypes( new HashMap<String, String>() {{
-			put("StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction", "com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean");
-		}});
+        osb.setType("com.arjuna.ats.arjuna.AtomicAction", "com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean");
 
 		return osb;
 	}
@@ -103,9 +101,6 @@
 	{
 		ObjStoreBrowser osb = new ObjStoreBrowser("os");
 
-		Properties p = ObjStoreBrowser.loadProperties("invalid property file");
-
-		assertTrue(p.size() == 0);
 		osb.start();
 		osb.probe();
 

Modified: labs/jbosstm/trunk/ArjunaJTA/INSTALL
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/INSTALL	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/INSTALL	2012-01-04 16:33:42 UTC (rev 37829)
@@ -1,6 +1,6 @@
 
-JBossTS 4.15 is designed for use standalone.
-It is also used by JBossAS 7 beta releases, but manual upgrading of the component inside JBossAS is not recommended.
+JBossTS 4.16 is designed for use standalone.
+It may also be used by JBossAS 7.1 releases, but manual upgrading of the component inside JBossAS is not recommended.
 Integration with JBossAS 6 or earlier is no longer supported.
 
 
@@ -10,12 +10,14 @@
 JMX Instrumentation
 -------------------
 
-With this release it is now possible to monitor the transaction Object
-Store using JMX. Monitoring the Object Store is useful for trouble
+It is possible to monitor the transaction Object Store using JMX.
+Monitoring the Object Store is useful for trouble
 shooting problems that occur when transactions are committed (it does
 not expose transactions prior to commit). The JMX instrumentation
-(of the ObjectStore) is a new feature and is not necessarily suitable
-for monitoring production systems.
+(of the ObjectStore) is a not fully integrated with the AS console and
+is therefore not necessarily suitable for monitoring production systems
+but can provide useful diagnostic information that is otherwise not
+available.
 
 In any compliant JMX browser (such as jconsole) there should appear an
 MBean with the name
@@ -24,29 +26,53 @@
 for other MBeans (for example in a JMX client such as jconsole the MBeans
 will be displayed in the form of a tree control). The various MBeans
 corresponding to this ObjectStore will have names prefixed by this
-'top level' MBean.
+'top level' MBean. The bean has two operations:
 
-Simply instantiate an object of type com.hp.mwtests.ts.arjuna.tools.ObjStoreBrowser
-and initialise it with a valid set of types for handling ObjectStore records. These types
-can be set using a setTypes() method via a properties file on the classpath.
+probe - searches for Object Store records and creates corresponding MBeans
+	or unregisters any beans that no longer have a corresponding record;
+viewSubordinateAtomicActions - toggles the ability to view Subordinate Atomic Actions.
+        Set the boolean parameter to the text "true"/"false" to enable/disable viewing
+        of these record types.
+        WARNING: Viewing a Subordinate Atomic Action will trigger a recovery scan
+	independently of the Transaction Recovery Service so please use with care
+	(for example if you know that the distributed transaction is not recovering
+	automatically).
+
+Each MBean corresponding to an transaction contains an operation for removing the
+record from the object store. After this operation the system will no longer have
+any knowledge of the transaction.
+
+Transactions contain participants such datasources. These have MBean representations in
+the JMX viewer. It sometimes happens that a particpant made a different transaction
+completion decision from the one the Transaction Manager expected - such a state is
+called a heuristic. Heuristics will never be completed automatically so the MBean
+contains an operation (called clearHeuristic) for moving the participant back into
+the "prepared" state. Once back in the prepared state the Recovery system will
+attempt to replay completion.
+
+[For embedded use simply instantiate an object of type
+com.hp.mwtests.ts.arjuna.tools.ObjStoreBrowser.
+If the default set of types is not adequate other types for handling ObjectStore records
+can be added either by setting a System property called
+ObjStoreBrowser.OBJ_STORE_BROWSER_HANDLERS or by calling the method setType() on the
+ObjStoreBrowser object.]
+
 Please refer to the unit tests in the src distribution for more details.
 
 Tools Deployment
 ----------------
 
-Transaction management is integrated into the admin console in the form of a JOPR plugin
-which is located in the install bin directory (jbossts-jopr-plugin.jar). Install it by copying
-to the admin console plugin directory ($JBOSS_HOME/common/deploy/admin-console.war/plugins).
-
 There is also a transaction statistics graphing tool which can run standalone or inside a
-jconsole tab (jconsole, a tool for managing JVMs, is distributed with the reference JDK):
+jconsole tab (jconsole, a tool for managing JVMs, is distributed with the reference JDK).
+Various transaction statistics are graphed in real time with each graph updated during each
+poll interval (4 seconds unless the interval is overridden on the jconsole command line).
 
-The tool depends on the JFree graphing library. Download and upack orson from http://www.jfree.org/orson
-Set the env variable ORSON_HOME to the directory where you plan to unpack the downloaded zip.
+The tool depends on the JFree graphing library. Download and upack Orson from http://www.jfree.org/orson
+and set the env variable ORSON_HOME to the directory where you plan to unpack the downloaded zip.
 If you intend to use the tool with jconsole you will also need to put the JDK tools and jconsole jars on
 the classpath:
 
-export CLASSPATH="$JDK_HOME/lib/tools.jar:$JDK_HOME/lib/jconsole.jar:$ORSON_HOME/orson-0.5.0.jar:$ORSON_HOME/lib/jfreechart-1.0.6.jar:$ORSON_HOME/lib/jcommon-1.0.10.jar:$TS_INSTALL_DIR/lib/jbossjta.jar>"
+export CLASSPATH="$JDK_HOME/lib/tools.jar:$JDK_HOME/lib/jconsole.jar:$ORSON_HOME/orson-0.5.0.jar:$ORSON_HOME/lib/jfreechart-1.0.6.jar:$ORSON_HOME/lib/jcommon-1.0.10.jar:$TS_INSTALL_DIR/lib/jbossjts.jar>"
 
 Standalone Usage:
 
@@ -56,4 +82,6 @@
 
 Usage with jconsole:
 
-jconsole -J-Djava.class.path="$CLASSPATH" -pluginpath $TS_INSTALL_DIR/lib/jbossjta.jar
\ No newline at end of file
+jconsole -J-Djava.class.path="$CLASSPATH" -pluginpath $TS_INSTALL_DIR/lib/jbossjts.jar
+
+The tool will automatically enable statistics gathering on startup. It is recommended that you disable statistics gathering prior to exit on the Settings tab in the GUI.

Modified: labs/jbosstm/trunk/ArjunaJTA/integration/pom.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/integration/pom.xml	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/integration/pom.xml	2012-01-04 16:33:42 UTC (rev 37829)
@@ -25,6 +25,17 @@
   <packaging>jar</packaging>
   <build>
     <sourceDirectory>../../atsintegration/classes</sourceDirectory>
+<!--
+    <testSourceDirectory>../../atsintegration/tests</testSourceDirectory>
+    <testResources>
+      <testResource>
+        <directory>../../atsintegration/tests/resources</directory>
+      </testResource>
+      <testResource>
+        <directory>../../atsintegration/tests/byteman-scripts</directory>
+      </testResource>
+    </testResources>
+-->
     <plugins>
       <plugin>
         <inherited>false</inherited>
@@ -108,5 +119,31 @@
       <artifactId>jboss-logging-processor</artifactId>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.8.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.byteman</groupId>
+      <artifactId>byteman</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.byteman</groupId>
+      <artifactId>byteman-install</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.byteman</groupId>
+      <artifactId>byteman-submit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.byteman</groupId>
+      <artifactId>byteman-bmunit</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/NodeNameXAResourceOrphanFilter.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/NodeNameXAResourceOrphanFilter.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/NodeNameXAResourceOrphanFilter.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -43,7 +43,7 @@
  */
 public class NodeNameXAResourceOrphanFilter implements XAResourceOrphanFilter
 {
-    private static final String RECOVER_ALL_NODES = "*";
+    public static final String RECOVER_ALL_NODES = "*";
 
     @Override
     public Vote checkXid(Xid xid)

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/RecoveryXids.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/RecoveryXids.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/RecoveryXids.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -135,15 +135,24 @@
 
         return _whenFirstSeen.containsKey(xidImple);
     }
-
-    public boolean isStale() {
-        long now = System.currentTimeMillis();
-        long threshold = _lastValidated+(2*safetyIntervalMillis);
-        long diff = now - threshold;
-        boolean result = diff > 0;
-        return result;
+    
+    public boolean remove (Xid xid)
+    {
+        XidImple xidImple = new XidImple(xid);
+        
+        if (_whenFirstSeen.containsKey(xidImple)) {
+        	_whenFirstSeen.remove(xidImple);
+        	_whenLastSeen.remove(xidImple);
+        	return true;
+        } else {
+        	return false;
+        }
     }
 
+	public boolean isEmpty() {
+		return _whenFirstSeen.isEmpty();
+	}
+
     /**
      * If supplied xids contains any values seen on prev scans, replace the existing
      * XAResource with the supplied one and return true. Otherwise, return false.
@@ -183,5 +192,7 @@
     private XAResource _xares;
     private long _lastValidated;
 
-    private static final int safetyIntervalMillis = 10000; // may eventually want to make this configurable?
+    // JBTM-916 removed final so 10000 is not inlined into source code until we make this configurable
+	// https://issues.jboss.org/browse/JBTM-842
+    private static int safetyIntervalMillis = 10000; // may eventually want to make this configurable?
 }
\ No newline at end of file

Added: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/SubordinateJTAXAResourceOrphanFilter.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/SubordinateJTAXAResourceOrphanFilter.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/SubordinateJTAXAResourceOrphanFilter.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,143 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and/or its affiliates,
+ * 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) 2010,
+ * @author JBoss, by Red Hat.
+ */
+package com.arjuna.ats.internal.jta.recovery.arjunacore;
+
+import java.io.IOException;
+import java.util.Stack;
+
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+import com.arjuna.ats.arjuna.objectstore.RecoveryStore;
+import com.arjuna.ats.arjuna.objectstore.StoreManager;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.ats.internal.arjuna.common.UidHelper;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.SubordinateAtomicAction;
+import com.arjuna.ats.jta.logging.jtaLogger;
+import com.arjuna.ats.jta.recovery.XAResourceOrphanFilter;
+import com.arjuna.ats.jta.utils.XAHelper;
+import com.arjuna.ats.jta.xa.XATxConverter;
+import com.arjuna.ats.jta.xa.XidImple;
+
+/**
+ * An XAResourceOrphanFilter which uses detects orphaned subordinate XA
+ * Resources.
+ */
+public class SubordinateJTAXAResourceOrphanFilter implements XAResourceOrphanFilter {
+	public static final int RECOVER_ALL_NODES = 0;
+
+	@Override
+	public Vote checkXid(Xid xid) {
+		String nodeName = XATxConverter.getSubordinateNodeName(new XidImple(xid).getXID());
+
+		if (jtaLogger.logger.isDebugEnabled()) {
+			jtaLogger.logger.debug("subordinate node name of " + xid + " is " + nodeName);
+		}
+
+		// It does have an XID
+		if (nodeName != null) {
+			if (transactionLog(xid, nodeName)) {
+				// it's owned by a logged transaction which
+				// will recover it top down in due course
+				return Vote.LEAVE_ALONE;
+			} else {
+				return Vote.ROLLBACK;
+			}
+		} else {
+			return Vote.ABSTAIN;
+		}
+	}
+
+	/**
+	 * Is there a log file for this transaction?
+	 * 
+	 * @param recoveredResourceXid
+	 *            the transaction to check.
+	 * 
+	 * @return <code>boolean</code>true if there is a log file,
+	 *         <code>false</code> if there isn't.
+	 */
+	private boolean transactionLog(Xid recoveredResourceXid, String recoveredResourceNodeName) {
+
+		XidImple theXid = new XidImple(recoveredResourceXid);
+		Uid u = theXid.getTransactionUid();
+
+		if (jtaLogger.logger.isDebugEnabled()) {
+			jtaLogger.logger.debug("Checking whether Xid " + theXid + " exists in ObjectStore.");
+		}
+
+		if (!u.equals(Uid.nullUid())) {
+			RecoveryStore recoveryStore = StoreManager.getRecoveryStore();
+			String transactionType = SubordinateAtomicAction.getType();
+
+			if (jtaLogger.logger.isDebugEnabled()) {
+				jtaLogger.logger.debug("Looking for " + u + " and " + transactionType);
+			}
+
+			InputObjectState states = new InputObjectState();
+			try {
+				if (recoveryStore.allObjUids(transactionType, states) && (states.notempty())) {
+					Stack values = new Stack();
+					boolean finished = false;
+
+					do {
+						Uid uid = null;
+
+						try {
+							uid = UidHelper.unpackFrom(states);
+						} catch (IOException ex) {
+							ex.printStackTrace();
+
+							finished = true;
+						}
+
+						if (uid.notEquals(Uid.nullUid())) {
+							SubordinateAtomicAction tx = new SubordinateAtomicAction(uid, true);
+							XidImple transactionXid = (XidImple) tx.getXid();
+							if (transactionXid.isSameTransaction(recoveredResourceXid)
+									&& recoveredResourceNodeName.equals(XATxConverter.getSubordinateNodeName(transactionXid.getXID()))) {
+								if (jtaLogger.logger.isDebugEnabled()) {
+									jtaLogger.logger.debug("Found record for " + theXid);
+								}
+								return true;
+							}
+						} else
+							finished = true;
+
+					} while (!finished);
+					if (jtaLogger.logger.isDebugEnabled()) {
+						jtaLogger.logger.debug("No record found for " + theXid);
+					}
+				} else {
+					jtaLogger.i18NLogger.info_recovery_notaxid(XAHelper.xidToString(recoveredResourceXid));
+				}
+			} catch (ObjectStoreException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		return false;
+	}
+}

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/XARecoveryModule.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/XARecoveryModule.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/XARecoveryModule.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -31,37 +31,46 @@
 
 package com.arjuna.ats.internal.jta.recovery.arjunacore;
 
-import com.arjuna.ats.arjuna.common.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
 import com.arjuna.ats.arjuna.objectstore.RecoveryStore;
+import com.arjuna.ats.arjuna.objectstore.StateStatus;
 import com.arjuna.ats.arjuna.objectstore.StoreManager;
-import com.arjuna.ats.arjuna.state.*;
-import com.arjuna.ats.arjuna.objectstore.StateStatus;
 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.resources.arjunacore.XAResourceRecord;
+import com.arjuna.ats.jta.common.jtaPropertyManager;
 import com.arjuna.ats.jta.logging.jtaLogger;
-import com.arjuna.ats.jta.common.jtaPropertyManager;
-import com.arjuna.ats.jta.recovery.*;
+import com.arjuna.ats.jta.recovery.SerializableXAResourceDeserializer;
+import com.arjuna.ats.jta.recovery.XARecoveryResource;
+import com.arjuna.ats.jta.recovery.XARecoveryResourceManager;
+import com.arjuna.ats.jta.recovery.XAResourceOrphanFilter;
+import com.arjuna.ats.jta.recovery.XAResourceRecovery;
+import com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper;
 import com.arjuna.ats.jta.utils.XAHelper;
 
-import java.util.*;
-import javax.transaction.xa.*;
-
-import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
-
-import java.io.IOException;
-import javax.transaction.xa.XAException;
-
 /**
  * Designed to be able to recover any XAResource.
  */
 
 public class XARecoveryModule implements RecoveryModule
 {
-    public XARecoveryModule()
+	public XARecoveryModule()
 	{
 		this(new com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryResourceManagerImple(),
                 "Local XARecoveryModule");
@@ -97,6 +106,14 @@
             _xaResourceOrphanFilters.remove(xaResourceOrphanFilter);
         }
     }
+    
+	public void addSerializableXAResourceDeserializer(SerializableXAResourceDeserializer serializableXAResourceDeserializer) {
+		_seriablizableXAResourceDeserializers.add(serializableXAResourceDeserializer);		
+	}
+	
+	public List<SerializableXAResourceDeserializer> getSeriablizableXAResourceDeserializers() {
+		return _seriablizableXAResourceDeserializers;
+	}
 
 
 	public void periodicWorkFirstPass()
@@ -179,6 +196,10 @@
 			bottomUpRecovery();
         }
 
+		// JBTM-895 updated so that retrieving an XAResource triggers garbage collection, this is required because garbage collecting
+		// The XA resources in a bottom up scenario (e.g. resourceInitiatedRecoveryForRecoveryHelpers) means that any xids that are
+		// not eligible for recovery after the first scan may be removed as stale and due to the undocumented _xidScans check above will not be
+		// reloaded
         if (_xidScans != null)
 		{
 			Enumeration<XAResource> keys = _xidScans.keys();
@@ -188,8 +209,12 @@
 				XAResource theKey = keys.nextElement();
 				RecoveryXids xids = _xidScans.get(theKey);
 
-				if (xids.contains(xid))
+				if (xids.remove(xid)) {
+					if (xids.isEmpty()) {
+						_xidScans.remove(theKey);
+					}
 					return theKey;
+				}
 			}
 		}
 
@@ -347,6 +372,11 @@
 		return true;
 	}
 
+	/**
+	 * 
+	 * JBTM-895 garbage collection is now done when we return XAResources {@see XARecoveryModule#getNewXAResource(XAResourceRecord)}
+	 * @see XARecoveryModule#getNewXAResource(XAResourceRecord)
+	 */
     private void bottomUpRecovery() {
 
         // scan using statically configured plugins;
@@ -354,16 +384,7 @@
         // scan using dynamically configured plugins:
         resourceInitiatedRecoveryForRecoveryHelpers();
 
-        // garbage collection:
-        if (_xidScans != null) {
-            Set<XAResource> keys = new HashSet<XAResource>(_xidScans.keySet());
-            for(XAResource theKey : keys) {
-                RecoveryXids recoveryXids = _xidScans.get(theKey);
-                if(recoveryXids.isStale()) {
-                    _xidScans.remove(theKey);
-                }
-            }
-        }
+        // JBTM-895 garbage collection is now done when we return XAResources {@see XARecoveryModule#getNewXAResource(XAResourceRecord)}
     }
 
 	/**
@@ -850,4 +871,6 @@
 
 	private String _logName = null;
 
+	private List<SerializableXAResourceDeserializer> _seriablizableXAResourceDeserializers = new ArrayList<SerializableXAResourceDeserializer>();
+
 }

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/XARecoveryResourceImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/XARecoveryResourceImple.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/recovery/arjunacore/XARecoveryResourceImple.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -56,6 +56,9 @@
 	super._theXAResource = res;
     }
 
+    /**
+     * @deprecated Only used by a test
+     */
     public final XAResource getXAResource ()
     {
 	return super._theXAResource;

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/resources/arjunacore/XAResourceRecord.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/resources/arjunacore/XAResourceRecord.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/resources/arjunacore/XAResourceRecord.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -31,43 +31,47 @@
 
 package com.arjuna.ats.internal.jta.resources.arjunacore;
 
-import com.arjuna.ats.internal.arjuna.common.ClassloadingUtility;
-import com.arjuna.ats.jta.recovery.*;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
 
-import com.arjuna.ats.jta.common.jtaPropertyManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
 
-import com.arjuna.ats.jta.xa.RecoverableXAConnection;
-import com.arjuna.ats.jta.xa.XidImple;
-import com.arjuna.ats.jta.utils.XAHelper;
-import com.arjuna.ats.jta.logging.*;
-import com.arjuna.ats.jta.resources.StartXAResource;
-import com.arjuna.ats.jta.resources.EndXAResource;
-
-import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple;
-import com.arjuna.ats.internal.jta.xa.TxInfo;
-import com.arjuna.ats.internal.jta.resources.XAResourceErrorHandler;
-import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
-
 import com.arjuna.ats.arjuna.ObjectType;
+import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
+import com.arjuna.ats.arjuna.coordinator.RecordType;
 import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
-import com.arjuna.ats.arjuna.coordinator.RecordType;
 import com.arjuna.ats.arjuna.coordinator.TxControl;
-import com.arjuna.ats.arjuna.common.*;
-import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
 import com.arjuna.ats.arjuna.recovery.RecoveryModule;
-import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.internal.arjuna.common.ClassloadingUtility;
+import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
+import com.arjuna.ats.internal.jta.resources.XAResourceErrorHandler;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple;
+import com.arjuna.ats.internal.jta.xa.TxInfo;
+import com.arjuna.ats.jta.common.jtaPropertyManager;
+import com.arjuna.ats.jta.logging.jtaLogger;
+import com.arjuna.ats.jta.recovery.SerializableXAResourceDeserializer;
+import com.arjuna.ats.jta.recovery.XARecoveryResource;
+import com.arjuna.ats.jta.resources.EndXAResource;
+import com.arjuna.ats.jta.resources.StartXAResource;
+import com.arjuna.ats.jta.utils.XAHelper;
+import com.arjuna.ats.jta.xa.RecoverableXAConnection;
+import com.arjuna.ats.jta.xa.XidImple;
 
-import java.util.Vector;
-import java.util.Enumeration;
-
-import java.io.*;
-
-import javax.transaction.xa.Xid;
-import javax.transaction.xa.XAResource;
-
-import javax.transaction.xa.XAException;
-
 /**
  * @author Mark Little (mark_little at hp.com)
  * @version $Id: XAResourceRecord.java 2342 2006-03-30 13:06:17Z $
@@ -826,6 +830,8 @@
                         o.close();
 
                         os.packBoolean(true);
+                        String name = _theXAResource.getClass().getName();
+                        os.packString(name);
 
                         os.packBytes(s.toByteArray());
                     }
@@ -892,12 +898,29 @@
 				{
 					try
 					{
+						// Read the classname of the serialized XAResource
+						String className = os.unpackString();
+						
 						byte[] b = os.unpackBytes();
 
 						ByteArrayInputStream s = new ByteArrayInputStream(b);
 						ObjectInputStream o = new ObjectInputStream(s);
+						
+						// Give the list of deserializers a chance to deserialize the record
+						boolean deserialized = false;
+						Iterator<SerializableXAResourceDeserializer> iterator = seriablizableXAResourceDeserializers.iterator();
+						while (iterator.hasNext()) {
+							SerializableXAResourceDeserializer proxyXAResourceDeserializer = iterator.next();
+							if (proxyXAResourceDeserializer.canDeserialze(className)) {
+								_theXAResource = proxyXAResourceDeserializer.deserialze(o);
+								deserialized = true;
+							}
+						}
 
-						_theXAResource = (XAResource) o.readObject();
+						// Give it a go ourselves
+						if (!deserialized) {
+							_theXAResource = (XAResource) o.readObject();
+						}
 						o.close();
 
 						if (jtaLogger.logger.isTraceEnabled()) {
@@ -1079,6 +1102,14 @@
 		_valid = true;
 		_theTransaction = null;
 		_recovered = true;
+		
+		for (RecoveryModule recoveryModule : RecoveryManager.manager().getModules()) {
+			if (recoveryModule instanceof XARecoveryModule) {
+				XARecoveryModule xaRecoveryModule = (XARecoveryModule) recoveryModule;
+				seriablizableXAResourceDeserializers.addAll(xaRecoveryModule.getSeriablizableXAResourceDeserializers());
+				break;
+			}
+		}
 	}
 
 	public XAResourceRecord(Uid u)
@@ -1207,6 +1238,8 @@
 
 	private static boolean _rollbackOptimization = false;
     private static boolean _assumedComplete = false;
+    
+	private List<SerializableXAResourceDeserializer> seriablizableXAResourceDeserializers = new ArrayList<SerializableXAResourceDeserializer>();
 
 	static
 	{

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/resources/arjunacore/XAResourceRecordWrappingPlugin.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/resources/arjunacore/XAResourceRecordWrappingPlugin.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/resources/arjunacore/XAResourceRecordWrappingPlugin.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -20,8 +20,12 @@
  */
 package com.arjuna.ats.internal.jta.resources.arjunacore;
 
+import java.io.IOException;
+
 import javax.transaction.xa.XAResource;
 
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+
 /**
  * Callback interface to allow customisable population of XAResourceRecord metadata.
  *
@@ -31,5 +35,7 @@
 {
     public void transcribeWrapperData(XAResourceRecord record);
 
-    public String getEISName(XAResource xaResource);
+    public Integer getEISName(XAResource xaResource) throws IOException, ObjectStoreException;
+
+	public String getEISName(Integer eisName);
 }

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/JTAActionBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/JTAActionBean.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/JTAActionBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -1,10 +1,11 @@
 package com.arjuna.ats.internal.jta.tools.osb.mbean.jta;
 
+import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
-import com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean;
-import com.arjuna.ats.arjuna.tools.osb.mbean.ParticipantStatus;
-import com.arjuna.ats.arjuna.tools.osb.mbean.LogRecordWrapper;
-import com.arjuna.ats.arjuna.tools.osb.mbean.UidWrapper;
+import com.arjuna.ats.arjuna.coordinator.BasicAction;
+import com.arjuna.ats.arjuna.coordinator.RecordList;
+import com.arjuna.ats.arjuna.tools.osb.mbean.*;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.SubordinateAtomicAction;
 
 /**
  * JTA specific version of an ActionBean that knows when a participant record
@@ -23,4 +24,56 @@
         else
             return super.createParticipant(rec, listType);
     }
+/*
+    protected ActionBeanWrapperInterface createWrapper(UidWrapper w) {
+        String cn = SubordinateAtomicAction.class.getCanonicalName();
+        return new GenericAtomicActionWrapper("com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.SubordinateAtomicAction", w);
+    }
+
+
+
+    class SubordinateAtomicActionWrapper extends SubordinateAtomicAction implements ActionBeanWrapperInterface {
+        boolean activated;
+
+        public SubordinateAtomicActionWrapper(UidWrapper w) {
+            super(w.getUid());
+        }
+
+        public boolean activate() {
+            if (!activated)
+                activated = super.activate();
+
+            return activated;
+        }
+
+        public void doUpdateState() {
+            updateState();
+        }
+
+        public Uid getUid(AbstractRecord rec) {
+            return get_uid();
+        }
+
+        public StringBuilder toString(String prefix, StringBuilder sb) {
+            prefix += '\t';
+            return sb.append('\n').append(prefix).append(get_uid());
+        }
+
+        public void clearHeuristicDecision(int newDecision) {
+            if (super.heuristicList.size() == 0)
+                setHeuristicDecision(newDecision);
+        }
+
+        public RecordList getRecords(ParticipantStatus type) {
+            switch (type) {
+                default:
+                case PREPARED: return preparedList;
+                case FAILED: return failedList;
+                case HEURISTIC: return heuristicList;
+                case PENDING: return pendingList;
+                case READONLY: return readonlyList;
+            }
+        }
+
+    }*/
 }

Added: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/SubordinateActionBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/SubordinateActionBean.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/SubordinateActionBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,44 @@
+package com.arjuna.ats.internal.jta.tools.osb.mbean.jta;
+
+import com.arjuna.ats.arjuna.coordinator.BasicAction;
+import com.arjuna.ats.arjuna.logging.tsLogger;
+import com.arjuna.ats.arjuna.tools.osb.mbean.UidWrapper;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.SubordinateAtomicAction;
+
+public class SubordinateActionBean extends JTAActionBean implements SubordinateActionBeanMBean {
+    public SubordinateActionBean(UidWrapper w) {
+        super(w);
+    }
+
+    public String getXid() {
+        try {
+            SubordinateAtomicAction sub = (SubordinateAtomicAction) ra.getAction();
+
+            return sub.getXid().toString();
+        } catch (ClassCastException e) {
+            if (tsLogger.logger.isDebugEnabled()) {
+                BasicAction ba = ra.getAction();
+
+    		    tsLogger.logger.debug("unable to cast " + ba.toString() + e.getMessage());
+            }
+
+            return e.getMessage();
+        }
+    }
+
+    public String getParentNodeName() {
+        try {
+            SubordinateAtomicAction sub = (SubordinateAtomicAction) ra.getAction();
+
+            return sub.getParentNodeName();
+        } catch (ClassCastException e) {
+            if (tsLogger.logger.isDebugEnabled()) {
+                BasicAction ba = ra.getAction();
+
+    		    tsLogger.logger.debug("unable to cast " + (ba == null ? "null" : ba.toString()) + ": " + e.getMessage());
+            }
+
+            return e.getMessage();
+        }
+    }
+}

Added: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/SubordinateActionBeanMBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/SubordinateActionBeanMBean.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/SubordinateActionBeanMBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,13 @@
+package com.arjuna.ats.internal.jta.tools.osb.mbean.jta;
+
+import com.arjuna.ats.arjuna.tools.osb.annotation.MXBeanDescription;
+import com.arjuna.ats.arjuna.tools.osb.annotation.MXBeanPropertyDescription;
+import com.arjuna.ats.arjuna.tools.osb.mbean.ActionBeanMBean;
+
+ at MXBeanDescription("Management view of a subordinate transaction")
+public interface SubordinateActionBeanMBean extends ActionBeanMBean {
+    @MXBeanPropertyDescription("A unique id for this transaction")
+	String getXid();
+    @MXBeanPropertyDescription("The (XA) node name assigned by the administrator of the server from which this transaction was propagated")
+	String getParentNodeName();
+}
\ No newline at end of file

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/XAResourceMBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/XAResourceMBean.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jta/XAResourceMBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -8,7 +8,7 @@
 public interface XAResourceMBean extends LogRecordWrapperMBean {
 	@MXBeanPropertyDescription("The java type that implements this XAResource")
 	String getClassName();
-	@MXBeanPropertyDescription("JNDI name of the JCA resource")
+	@MXBeanPropertyDescription("JCA product name")
 	String getEisProductName();
 	@MXBeanPropertyDescription("JNDI Name of the datasource")
 	String getJndiName();

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/TransactionImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/TransactionImple.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/TransactionImple.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -45,12 +45,14 @@
 import com.arjuna.ats.jta.exceptions.InvalidTerminationStateException;
 import com.arjuna.ats.jta.resources.LastResourceCommitOptimisation;
 import com.arjuna.ats.jta.utils.XAHelper;
+import com.arjuna.ats.jta.xa.XATxConverter;
 import com.arjuna.ats.jta.xa.XidImple;
 import com.arjuna.ats.jta.logging.*;
 import com.arjuna.ats.jta.xa.XAModifier;
 
 import com.arjuna.ats.arjuna.coordinator.*;
 import com.arjuna.ats.arjuna.common.*;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
 
 import javax.transaction.xa.*;
 
@@ -59,6 +61,7 @@
 import javax.transaction.RollbackException;
 import javax.transaction.Status;
 
+import java.io.IOException;
 import java.lang.IllegalStateException;
 
 import java.util.concurrent.ConcurrentHashMap;
@@ -1483,20 +1486,15 @@
 		return null;
 	}
 
-	private final Xid createXid(boolean branch, XAModifier theModifier, XAResource xaResource)
+	protected Xid createXid(boolean branch, XAModifier theModifier, XAResource xaResource) throws IOException, ObjectStoreException
 	{
-		Xid xid = baseXid();
-
-		if (xid != null)
-			return xid;
-
-        String eisName = null;
+        int eisName = 0;
         if(branch) {
             if(_xaResourceRecordWrappingPlugin != null) {
                 eisName = _xaResourceRecordWrappingPlugin.getEISName(xaResource);
             }
         }
-		xid = new XidImple(_theTransaction, branch, eisName);
+		Xid xid = new XidImple(_theTransaction.get_uid(), branch, eisName);
 
 		if (theModifier != null)
 		{
@@ -1597,7 +1595,7 @@
 
 	private static final Class LAST_RESOURCE_OPTIMISATION_INTERFACE;
 
-    private static final XAResourceRecordWrappingPlugin _xaResourceRecordWrappingPlugin;
+    protected static final XAResourceRecordWrappingPlugin _xaResourceRecordWrappingPlugin;
 
 	static
 	{

Added: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/SubordinateXidImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/SubordinateXidImple.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/SubordinateXidImple.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,69 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.internal.jta.transaction.arjunacore.jca;
+
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.internal.jta.xa.XID;
+import com.arjuna.ats.jta.xa.XATxConverter;
+import com.arjuna.ats.jta.xa.XidImple;
+
+public class SubordinateXidImple extends XidImple {
+	public SubordinateXidImple(Xid xid) {
+		super(xid);
+	}
+
+	/**
+	 * Test equality as being part of the same global transaction
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if (_theXid.formatID != XATxConverter.FORMAT_ID) {
+			return super.equals(obj);
+		}
+		boolean toReturn = false;
+		if (obj instanceof SubordinateXidImple) {
+			toReturn = isSameTransaction(((SubordinateXidImple) obj));
+		}
+		return toReturn;
+	}
+
+	/**
+	 * Generate the hash code for the xid, subordinates are diffed on the gtrid
+	 * only.
+	 * 
+	 * @param xid
+	 *            The xid.
+	 * @return The hash code.
+	 */
+	@Override
+	protected int getHash(final XID xid) {
+		if (xid == null) {
+			return 0;
+		}
+		if (_theXid.formatID != XATxConverter.FORMAT_ID) {
+			return super.getHash(xid);
+		}
+		return generateHash(xid.formatID, xid.data, 0, xid.gtrid_length);
+	}
+
+}

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/TransactionImporterImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/TransactionImporterImple.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/TransactionImporterImple.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -31,13 +31,17 @@
 
 package com.arjuna.ats.internal.jta.transaction.arjunacore.jca;
 
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-import javax.transaction.xa.*;
+import javax.transaction.SystemException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
 
 import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.TransactionImple;
-import com.arjuna.ats.jta.xa.XidImple;
 
 public class TransactionImporterImple implements TransactionImporter
 {
@@ -86,13 +90,13 @@
 		 * Check to see if we haven't already imported this thing.
 		 */
 
-		SubordinateTransaction imported = getImportedTransaction(xid);
+		TransactionImple imported = (TransactionImple) getImportedTransaction(xid);
 
 		if (imported == null)
 		{
 			imported = new TransactionImple(timeout, xid);
-
-			_transactions.put(new XidImple(xid), imported);
+			
+			_transactions.put(new SubordinateXidImple(imported.baseXid()), imported);
 		}
 
 		return imported;
@@ -130,8 +134,9 @@
 
 		if (tx == null)
 		{
-			_transactions.put(recovered.baseXid(), recovered);
 
+			Xid baseXid = recovered.baseXid();
+			_transactions.put(new SubordinateXidImple(baseXid), recovered);
 			recovered.recordTransaction();
 
 			return recovered;
@@ -162,10 +167,20 @@
 		if (xid == null)
 			throw new IllegalArgumentException();
 
-		SubordinateTransaction tx = _transactions.get(new XidImple(xid));
+		SubordinateTransaction tx = _transactions.get(new SubordinateXidImple(xid));
 
 		if (tx == null)
 			return null;
+		
+		// https://issues.jboss.org/browse/JBTM-927
+		try {
+			if (tx.getStatus() == javax.transaction.Status.STATUS_ROLLEDBACK) {
+				throw new XAException(XAException.XA_RBROLLBACK);
+			}
+		} catch (SystemException e) {
+			e.printStackTrace();
+			throw new XAException(XAException.XA_RBROLLBACK);
+		}
 
 		if (!tx.activated())
 		{
@@ -192,9 +207,21 @@
 		if (xid == null)
 			throw new IllegalArgumentException();
 
-		_transactions.remove(new XidImple(xid));
+		_transactions.remove(new SubordinateXidImple(xid));
 	}
+	
+	public Set<Xid> getInflightXids(String parentNodeName) {
+		Iterator<TransactionImple> iterator = _transactions.values().iterator();
+		Set<Xid> toReturn = new HashSet<Xid>();
+		while (iterator.hasNext()) {
+			TransactionImple next = iterator.next();
+			if (next.getParentNodeName().equals(parentNodeName)) {
+				toReturn.add(next.baseXid());
+			}
+		}
+		return toReturn;
+	}
 
-	private static ConcurrentHashMap<Xid, SubordinateTransaction> _transactions = new ConcurrentHashMap<Xid, SubordinateTransaction>();
+	private static ConcurrentHashMap<SubordinateXidImple, TransactionImple> _transactions = new ConcurrentHashMap<SubordinateXidImple, TransactionImple>();
+}
 
-}
\ No newline at end of file

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/XATerminatorImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/XATerminatorImple.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/jca/XATerminatorImple.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -32,20 +32,36 @@
 package com.arjuna.ats.internal.jta.transaction.arjunacore.jca;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Set;
 import java.util.Stack;
 
-import javax.transaction.*;
-import javax.transaction.xa.*;
+import javax.transaction.HeuristicCommitException;
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
 
 import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
 import com.arjuna.ats.arjuna.objectstore.RecoveryStore;
 import com.arjuna.ats.arjuna.objectstore.StoreManager;
 import com.arjuna.ats.arjuna.state.InputObjectState;
 import com.arjuna.ats.internal.arjuna.common.UidHelper;
+import com.arjuna.ats.internal.jta.recovery.arjunacore.NodeNameXAResourceOrphanFilter;
 import com.arjuna.ats.internal.jta.resources.spi.XATerminatorExtensions;
 import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.SubordinateAtomicAction;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.TransactionImple;
+import com.arjuna.ats.internal.jta.xa.XID;
 import com.arjuna.ats.jta.exceptions.UnexpectedConditionException;
+import com.arjuna.ats.jta.logging.jtaLogger;
+import com.arjuna.ats.jta.xa.XATxConverter;
+import com.arjuna.ats.jta.xa.XidImple;
 
 /**
  * The XATerminator implementation.
@@ -200,10 +216,19 @@
 
     public int prepare (Xid xid) throws XAException
     {
+
+    	// JBTM-927 this can happen if the transaction has been rolled back by the TransactionReaper
+		SubordinateTransaction tx = null;
+		try {
+			tx = SubordinationManager.getTransactionImporter().getImportedTransaction(xid);
+		} catch (XAException xae) {
+			if (xae.errorCode == XAException.XA_RBROLLBACK) {
+				SubordinationManager.getTransactionImporter().removeImportedTransaction(xid);
+			}
+			throw xae;
+		}
         try
         {
-            SubordinateTransaction tx = SubordinationManager
-                    .getTransactionImporter().getImportedTransaction(xid);
 
             if (tx == null)
                 throw new XAException(XAException.XAER_INVAL);
@@ -313,7 +338,29 @@
         }
 
         // if we are here, then check the object store
+        return doRecover(null, null);
+    }
+    
+    /**
+     * Return a list of indoubt transactions. This may include those
+     * transactions that are currently in-flight and running 2PC and do not need
+     * recovery invoked on them.
+     * 
+     * @param nodeName
+     * 				Only recover transactions for this node (unless set to NodeNameXAResourceOrphanFilter.RECOVER_ALL_NODES)
+     * @throws XAException
+     *             thrown if any error occurs.
+     * @return a list of potentially indoubt transactions or <code>null</code>.
+     */
 
+    public Xid[] doRecover (Xid xid, String parentNodeName) throws XAException
+    {
+        /*
+         * Requires going through the objectstore for the states of imported
+         * transactions. Our own crash recovery takes care of transactions
+         * imported via CORBA, Web Services etc.
+         */
+
         Xid[] indoubt = null;
 
         try
@@ -326,7 +373,7 @@
             if (recoveryStore.allObjUids(SubordinateAtomicAction.getType(), states)
                     && (states.notempty()))
             {
-                Stack values = new Stack();
+                Stack<Xid> values = new Stack<Xid>();
                 boolean finished = false;
 
                 do
@@ -346,19 +393,53 @@
 
                     if (uid.notEquals(Uid.nullUid()))
                     {
-                        Transaction tx = SubordinationManager
-                                .getTransactionImporter().recoverTransaction(
-                                        uid);
+						if (parentNodeName != null) {
+							SubordinateAtomicAction saa = new SubordinateAtomicAction(uid, true);
+							XidImple loadedXid = (XidImple) saa.getXid();
+							if (loadedXid.getFormatId() == XATxConverter.FORMAT_ID) {
+								String loadedXidSubordinateNodeName = XATxConverter.getSubordinateNodeName(loadedXid.getXID());
+								if (TxControl.getXANodeName().equals(loadedXidSubordinateNodeName)) {
+									if (parentNodeName.equals(saa.getParentNodeName())) {
+										if (jtaLogger.logger.isDebugEnabled()) {
+											jtaLogger.logger.debug("Found record for " + saa);
+										}
+//										TransactionImple tx = (TransactionImple) SubordinationManager.getTransactionImporter().recoverTransaction(uid);
 
-                        if (tx != null)
-                            values.push(tx);
+										values.push(loadedXid);
+									}
+								}
+							}
+
+						} else if (xid == null) {
+							TransactionImple tx = (TransactionImple) SubordinationManager.getTransactionImporter().recoverTransaction(uid);
+
+							if (tx != null)
+								values.push(tx.baseXid());
+						} else {
+							SubordinateAtomicAction saa = new SubordinateAtomicAction(uid, true);
+							XidImple loadedXid = (XidImple) saa.getXid();
+							if (loadedXid.getFormatId() == XATxConverter.FORMAT_ID) {
+								String loadedXidSubordinateNodeName = XATxConverter.getSubordinateNodeName(loadedXid.getXID());
+								if (XATxConverter.getSubordinateNodeName(new XidImple(xid).getXID()).equals(loadedXidSubordinateNodeName)) {
+									if (Arrays.equals(loadedXid.getGlobalTransactionId(), xid.getGlobalTransactionId())) {
+										if (jtaLogger.logger.isDebugEnabled()) {
+											jtaLogger.logger.debug("Found record for " + saa);
+										}
+										TransactionImple tx = (TransactionImple) SubordinationManager.getTransactionImporter().recoverTransaction(uid);
+
+										values.push(loadedXid);
+									}
+								}
+							}
+						}
+
                     }
                     else
                         finished = true;
 
                 }
                 while (!finished);
-
+                
                 if (values.size() > 0)
                 {
                     int index = 0;
@@ -367,10 +448,7 @@
 
                     while (!values.empty())
                     {
-                        com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.TransactionImple tx = (com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.TransactionImple) values
-                                .pop();
-
-                        indoubt[index] = tx.baseXid();
+                        indoubt[index] = values.pop();
                         index++;
                     }
                 }
@@ -395,10 +473,20 @@
 
     public void rollback (Xid xid) throws XAException
     {
+		// JBTM-927 this can happen if the transaction has been rolled back by
+		// the TransactionReaper
+		SubordinateTransaction tx = null;
+		try {
+			tx = SubordinationManager.getTransactionImporter().getImportedTransaction(xid);
+		} catch (XAException xae) {
+			if (xae.errorCode == XAException.XA_RBROLLBACK) {
+				// do nothing as already rolled back
+				return;
+			}
+			throw xae;
+		}
         try
         {
-            SubordinateTransaction tx = SubordinationManager
-                    .getTransactionImporter().getImportedTransaction(xid);
 
             if (tx == null)
                 throw new XAException(XAException.XAER_INVAL);

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/SubordinateAtomicAction.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/SubordinateAtomicAction.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/SubordinateAtomicAction.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -28,6 +28,8 @@
 
 package com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate;
 
+import javax.transaction.Status;
+
 import com.arjuna.ats.arjuna.AtomicAction;
 import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.ActionStatus;
@@ -121,6 +123,11 @@
 	public int doPrepare ()
 	{
         int status = super.status();
+        
+        // JBTM-927 it is possible this transaction has been aborted by the TransactionReaper
+        if (status == ActionStatus.ABORTED) {
+        	return TwoPhaseOutcome.PREPARE_NOTOK;
+        }
 
         // In JTA spec, beforeCompletions are run on commit attempts only, not rollbacks.
         // We attempt to mimic that here, even though we are outside the scope of the spec.
@@ -141,6 +148,7 @@
 		else
 		{
 			super.phase2Abort(true);
+			super.afterCompletion(Status.STATUS_ROLLEDBACK);
 
 			return TwoPhaseOutcome.PREPARE_NOTOK;
 		}
@@ -175,6 +183,8 @@
 
 		super.afterCompletion(toReturn);
 
+		TransactionReaper.transactionReaper().remove(this);
+
 		return toReturn;
 	}
 
@@ -207,6 +217,8 @@
 
 		super.afterCompletion(toReturn);
 
+		TransactionReaper.transactionReaper().remove(this);
+
 		return toReturn;
 	}
 
@@ -230,10 +242,15 @@
 	    }
 
 	    afterCompletion(status);
+
+		TransactionReaper.transactionReaper().remove(this);
 	    
 	    return status;
 	}
 
+	/**
+	 * @deprecated Only called via tests
+	 */
 	public void doForget ()
 	{
 		super.forgetHeuristics();

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/TransactionImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/TransactionImple.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/TransactionImple.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -33,22 +33,28 @@
 
 import com.arjuna.ats.arjuna.coordinator.ActionStatus;
 import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
 
 import com.arjuna.ats.internal.jta.transaction.arjunacore.AtomicAction;
 import com.arjuna.ats.jta.exceptions.InvalidTerminationStateException;
 import com.arjuna.ats.jta.exceptions.UnexpectedConditionException;
 import com.arjuna.ats.jta.logging.*;
+import com.arjuna.ats.jta.xa.XAModifier;
+import com.arjuna.ats.jta.xa.XATxConverter;
+import com.arjuna.ats.jta.xa.XidImple;
 
+import java.io.IOException;
 import java.lang.IllegalStateException;
 
 import javax.transaction.*;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
 
 // https://jira.jboss.org/jira/browse/JBTM-384
 
 public class TransactionImple extends
 		com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple
 {
-
 	/**
 	 * Create a new transaction with the specified timeout.
 	 */
@@ -204,7 +210,8 @@
                 jtaLogger.i18NLogger.warn_transaction_arjunacore_endsuspendfailed1();
 			}
 
-			int res = subAct.doRollback();
+			// JBTM-927 the transaction reaper may have aborted this transaction already
+			int res = subAct.status() == ActionStatus.ABORTED ? ActionStatus.ABORTED : subAct.doRollback();
 
 			switch (res)
 			{
@@ -234,6 +241,12 @@
 		}
 	}
 
+	/**
+	 * 
+	 * @throws IllegalStateException
+	 * 
+	 * @deprecated Only called from a test
+	 */
 	public void doForget () throws IllegalStateException
 	{
 		try
@@ -356,4 +369,36 @@
     	return true;
     }
 
+	@Override
+	protected Xid createXid(boolean branch, XAModifier theModifier, XAResource xaResource) throws IOException, ObjectStoreException
+	{
+		Xid xid = baseXid();
+
+		// We can have subordinate XIDs that can be editted
+		if (xid.getFormatId() != XATxConverter.FORMAT_ID)
+			return xid;
+
+		Integer eisName = null;
+        if(branch) {
+            if(_xaResourceRecordWrappingPlugin != null) {
+                eisName = _xaResourceRecordWrappingPlugin.getEISName(xaResource);
+            }
+        }
+		xid = new XidImple(xid, branch, eisName);
+
+		if (theModifier != null)
+		{
+			try
+			{
+				xid = theModifier.createXid((XidImple) xid);
+			}
+			catch (Exception e)
+			{
+				e.printStackTrace();
+			}
+		}
+
+		return xid;
+	}
+
 }

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/jca/SubordinateAtomicAction.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/jca/SubordinateAtomicAction.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/jca/SubordinateAtomicAction.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -33,8 +33,15 @@
 import javax.transaction.xa.Xid;
 
 import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+import com.arjuna.ats.arjuna.objectstore.StoreManager;
 import com.arjuna.ats.arjuna.state.InputObjectState;
 import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.arjuna.utils.Utility;
+import com.arjuna.ats.internal.arjuna.Header;
+import com.arjuna.ats.internal.jta.xa.XID;
+import com.arjuna.ats.jta.xa.XATxConverter;
 import com.arjuna.ats.jta.xa.XidImple;
 
 /**
@@ -50,6 +57,9 @@
 		com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.SubordinateAtomicAction
 {
 
+	/**
+	 * @deprecated This is only used by test code
+	 */
 	public SubordinateAtomicAction ()
 	{
 		super();  // does start for us
@@ -65,11 +75,41 @@
 		_activated = activate(); // if this fails, we'll retry later.
 	}
 	
+	public SubordinateAtomicAction(Uid actId, boolean peekXidOnly) throws ObjectStoreException, IOException {
+		super(actId);
+		if (peekXidOnly) {
+			InputObjectState os = StoreManager.getParticipantStore().read_committed(objectUid, type());
+			unpackHeader(os, new Header());
+			boolean haveXid = os.unpackBoolean();
+
+			if (haveXid) {
+				_theXid = new XidImple();
+
+				((XidImple) _theXid).unpackFrom(os);
+				_parentNodeName = os.unpackString();
+			}
+		} else {
+			_activated = activate();
+		}
+	}
+	
 	public SubordinateAtomicAction (int timeout, Xid xid)
 	{
 		super(timeout); // implicit start (done in base class)
 		
-		_theXid = new XidImple(xid);
+		if (xid != null && xid.getFormatId() == XATxConverter.FORMAT_ID) {
+			XidImple toImport = new XidImple(xid);
+			XID toCheck = toImport.getXID();
+			_parentNodeName = XATxConverter.getSubordinateNodeName(toCheck);
+			if (_parentNodeName == null) {
+				_parentNodeName = XATxConverter.getNodeName(toCheck);
+			}
+			XATxConverter.setSubordinateNodeName(toImport.getXID(), TxControl.getXANodeName());
+			_theXid = new XidImple(toImport);
+		} else {
+			_theXid = new XidImple(xid);
+		}
+		
 		_activated = true;
 	}
 	
@@ -99,16 +139,24 @@
 	    
 		return _theXid;
 	}
+	
+	public String getParentNodeName() {
+		return _parentNodeName;
+	}
 
 	public boolean save_state (OutputObjectState os, int t)
 	{
 	    try
 	    {
+	        // pack the header first for the benefit of the tooling
+	        packHeader(os, new Header(get_uid(), Utility.getProcessUid()));
+
 	        if (_theXid != null)
 	        {
 	            os.packBoolean(true);
 
 	            ((XidImple) _theXid).packInto(os);
+	            os.packString(_parentNodeName);
 	        }
 	        else
 	            os.packBoolean(false);
@@ -127,6 +175,8 @@
 	    
 		try
 		{
+			unpackHeader(os, new Header());
+
 			boolean haveXid = os.unpackBoolean();
 			
 			if (haveXid)
@@ -134,6 +184,7 @@
 				_theXid = new XidImple();
 				
 				((XidImple) _theXid).unpackFrom(os);
+				_parentNodeName = os.unpackString();
 			}
 		}
 		catch (IOException ex)
@@ -150,5 +201,6 @@
     }
 
 	private Xid _theXid;
+	private String _parentNodeName;
     private boolean _activated;
 }

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/jca/TransactionImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/jca/TransactionImple.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/transaction/arjunacore/subordinate/jca/TransactionImple.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -44,6 +44,8 @@
 
 	/**
 	 * Create a new transaction with the specified timeout.
+	 * 
+	 * @deprecated Only used by tests
 	 */
 
 	public TransactionImple(int timeout)
@@ -71,6 +73,10 @@
 
 		// don't put it into list here: it may already be there!
 	}
+	
+	public String getParentNodeName() {
+		return ((SubordinateAtomicAction)_theTransaction).getParentNodeName();
+	}
 
 	public final void recordTransaction()
 	{

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/utils/XAUtils.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/utils/XAUtils.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/internal/jta/utils/XAUtils.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -31,10 +31,12 @@
 
 package com.arjuna.ats.internal.jta.utils;
 
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.jta.xa.XATxConverter;
 import com.arjuna.ats.jta.xa.XidImple;
 
-import javax.transaction.xa.*;
-
 /**
  * @author Mark Little (mark.little at arjuna.com)
  * @version $Id: XAUtils.java 2342 2006-03-30 13:06:17Z  $
@@ -82,7 +84,7 @@
         } else {
             xidImple = new XidImple(xid);
         }
-        return xidImple.getNodeName();
+        return XATxConverter.getNodeName(xidImple.getXID());
 	}
 
 	private static final String ORACLE = "oracle";

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/common/JTAEnvironmentBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/common/JTAEnvironmentBean.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/common/JTAEnvironmentBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -20,21 +20,24 @@
  */
 package com.arjuna.ats.jta.common;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.transaction.TransactionManager;
+import javax.transaction.TransactionSynchronizationRegistry;
+import javax.transaction.UserTransaction;
+
+import com.arjuna.ats.arjuna.logging.tsLogger;
 import com.arjuna.ats.internal.arjuna.common.ClassloadingUtility;
 import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecordWrappingPlugin;
 import com.arjuna.ats.jta.recovery.XAResourceOrphanFilter;
 import com.arjuna.ats.jta.recovery.XAResourceRecovery;
 import com.arjuna.ats.jta.resources.XAResourceMap;
+import com.arjuna.common.internal.util.propertyservice.ConcatenationPrefix;
+import com.arjuna.common.internal.util.propertyservice.FullPropertyName;
 import com.arjuna.common.internal.util.propertyservice.PropertyPrefix;
-import com.arjuna.common.internal.util.propertyservice.FullPropertyName;
-import com.arjuna.common.internal.util.propertyservice.ConcatenationPrefix;
-import javax.transaction.TransactionManager;
-import javax.transaction.TransactionSynchronizationRegistry;
-import javax.transaction.UserTransaction;
 
-import java.util.List;
-import java.util.ArrayList;
-
 /**
  * A JavaBean containing configuration properties for the JTA subsystem.
  *
@@ -368,6 +371,8 @@
        return new ArrayList<String>(xaRecoveryNodes);
     }
 
+
+	
     /**
      * Sets the node identifiers for which recovery will be performed.
      * The provided list will be copied, not retained.

Added: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/recovery/SerializableXAResourceDeserializer.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/recovery/SerializableXAResourceDeserializer.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/recovery/SerializableXAResourceDeserializer.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,61 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.recovery;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+import javax.transaction.xa.XAResource;
+
+/**
+ * This is an additional recovery helper that allows clients of the transaction
+ * manager to provide a deserializer for their Serializable XAResources. We need
+ * this as otherwise the transaction manager may not be able to see the
+ * transports classes, for instance in an application server environment.
+ */
+public interface SerializableXAResourceDeserializer {
+
+	/**
+	 * Can this {@link SerializableXAResourceDeserializer} handle the specified
+	 * classname.
+	 * 
+	 * @param className
+	 *            The name of the class to deserialize.
+	 * 
+	 * @return A flag to indicate where the deserializer is aware of the
+	 *         Serializable XAResource.
+	 */
+	public boolean canDeserialze(String className);
+
+	/**
+	 * Deserialize the XAResource.
+	 * 
+	 * @param ois
+	 *            The input stream to read from.
+	 * @return An {@link XAResource}
+	 * @throws IOException
+	 *             If the {@link ObjectInputStream.readObject()} fails.
+	 * @throws ClassNotFoundException
+	 *             If the {@link ObjectInputStream.readObject()} fails.
+	 */
+	public XAResource deserialze(ObjectInputStream ois) throws IOException, ClassNotFoundException;
+}

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/xa/XATxConverter.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/xa/XATxConverter.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/xa/XATxConverter.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -31,13 +31,17 @@
 
 package com.arjuna.ats.jta.xa;
 
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import javax.transaction.xa.Xid;
+
 import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecordWrappingPlugin;
 import com.arjuna.ats.internal.jta.xa.XID;
+import com.arjuna.ats.jta.common.jtaPropertyManager;
 
-import javax.transaction.xa.Xid;
-import java.io.UnsupportedEncodingException;
-
 /**
  * @author Mark Little (mark.little at arjuna.com)
  * @version $Id: XATxConverter.java 2342 2006-03-30 13:06:17Z  $
@@ -46,9 +50,15 @@
 
 public class XATxConverter
 {
-    public static final int FORMAT_ID = 131076; // different from JTS ones.
+	private static XAResourceRecordWrappingPlugin xaResourceRecordWrappingPlugin;
+    public static final int FORMAT_ID = 131077; // different from JTS ones.
 
-    static XID getXid (Uid uid, boolean branch, String eisName) throws IllegalStateException
+
+	static {
+        xaResourceRecordWrappingPlugin = jtaPropertyManager.getJTAEnvironmentBean().getXAResourceRecordWrappingPlugin();
+	}
+	
+    static XID getXid (Uid uid, boolean branch, Integer eisName) throws IllegalStateException
     {
         if (branch)
             return getXid(uid, new Uid(), FORMAT_ID, eisName);
@@ -67,7 +77,7 @@
         return new XidImple(xid);
     }
 
-    private static XID getXid(Uid uid, Uid branch, int formatId, String eisNameString) throws IllegalStateException
+    private static XID getXid(Uid uid, Uid branch, int formatId, Integer eisName) throws IllegalStateException
     {
         if (uid == null) {
     	    throw new IllegalStateException();
@@ -79,23 +89,20 @@
         // gtrid is uid byte form followed by as many chars of the node name as will fit.
         byte[] gtridUid = uid.getBytes();
 
-        if (gtridUid.length > XID.MAXBQUALSIZE) {
+        if (gtridUid.length > XID.MAXGTRIDSIZE) {
             throw new IllegalStateException(); // Uid is too long!!!!
         }
 
-        int spareGtridBytes = XID.MAXGTRIDSIZE - gtridUid.length;
-        byte[] nodeName = TxControl.getXANodeName();
-        int nodeNameLengthToUse =  nodeName.length;
-        if( nodeName.length > spareGtridBytes) {
-            nodeNameLengthToUse = spareGtridBytes;
-        }
+        String nodeName = TxControl.getXANodeName();
+        int nodeNameLengthToUse =  nodeName.getBytes().length;
         xid.gtrid_length = gtridUid.length+nodeNameLengthToUse;
 
         // src, srcPos, dest, destPos, length
         System.arraycopy(gtridUid, 0, xid.data, 0, gtridUid.length);
-        System.arraycopy(nodeName, 0, xid.data, gtridUid.length, nodeNameLengthToUse);
+        System.arraycopy(nodeName.getBytes(), 0, xid.data, gtridUid.length, nodeNameLengthToUse);
 
         
+        
         if (branch.notEquals(Uid.nullUid()))
 		{
             // bqual is uid byte form plus EIS name.
@@ -105,23 +112,14 @@
                 throw new IllegalStateException(); // Uid is too long!!!!
             }
 
-            int spareBqualBytes = XID.MAXBQUALSIZE - bqualUid.length;
-            byte[] eisName;
-            try {
-                // caution: we may truncate the byte[] to fit, so double byte encodings are best avoided.
-                eisName = (eisNameString == null ? new byte[0] : eisNameString.getBytes("US-ASCII"));
-            } catch(UnsupportedEncodingException e) {
-                eisName = new byte[0];
-            }
-            int eisNameLengthToUse = eisName.length;
-            if( eisName.length > spareBqualBytes) {
-                eisNameLengthToUse = spareBqualBytes;
-            }
-            xid.bqual_length = bqualUid.length+eisNameLengthToUse;
+            int spareBqualBytes = XID.MAXBQUALSIZE - (bqualUid.length + 4);
+           
+            xid.bqual_length = bqualUid.length+4+4;
 
             // src, srcPos, dest, destPos, length
-            System.arraycopy (bqualUid, 0, xid.data, xid.gtrid_length, bqualUid.length);
-            System.arraycopy (eisName, 0, xid.data, xid.gtrid_length+bqualUid.length, eisNameLengthToUse);
+            int offset = xid.gtrid_length;
+            System.arraycopy (bqualUid, 0, xid.data, offset, bqualUid.length);
+            setEisName(xid, eisName);
         }
 		else
 		{
@@ -151,27 +149,75 @@
         return tx;
     }
 
-    public static String getNodeName(XID xid)
-    {
-        // don't check the formatId - it may differ e.g. JTA vs. JTS.
-        
-        // the node name follows the Uid with no separator, so the only
-        // way to tell where it starts is to figure out how long the Uid is.
-        Uid uid = getUid(xid);
-        int uidLength = uid.getBytes().length;
-        int nameLength = xid.gtrid_length-uidLength;
-        byte[] nodeName = new byte[nameLength];
-        System.arraycopy(xid.data, uidLength, nodeName, 0, nodeName.length);
-        
-        try {
-            return new String(nodeName, "US-ASCII");
-        } catch(UnsupportedEncodingException e) {
-            // should never happen, we use a required charset.
-            return "<failed to get nodename>";
-        }
-    }
+	public static String getNodeName(XID xid) {
+		// Arjuna.XID()
+		// don't check the formatId - it may differ e.g. JTA vs. JTS.
+		if (xid.formatID != FORMAT_ID && xid.formatID != 131072
+				&& xid.formatID != 131080) {
+			return null;
+		}
 
-    private static Uid getBranchUid(XID xid)
+		// the node name follows the Uid with no separator, so the only
+		// way to tell where it starts is to figure out how long the Uid is.
+		int offset = Uid.UID_SIZE;
+
+		return new String(Arrays.copyOfRange(xid.data, offset, xid.gtrid_length));
+	}
+
+	public static void setSubordinateNodeName(XID theXid, String xaNodeName) {
+		if (theXid == null || theXid.formatID != FORMAT_ID) {
+			return;
+		}
+		int length = 0;
+		if (xaNodeName != null) {
+			length = xaNodeName.length();
+		}
+		int offset = theXid.gtrid_length + Uid.UID_SIZE + 4;
+		theXid.data[offset++] = (byte) (length >>> 24);
+		theXid.data[offset++] = (byte) (length >>> 16);
+		theXid.data[offset++] = (byte) (length >>> 8);
+		theXid.data[offset++] = (byte) (length >>> 0);
+		if (length > 0) {
+			byte[] nameAsBytes = xaNodeName.getBytes();
+			System.arraycopy(nameAsBytes, 0, theXid.data, offset, length);
+		}
+		for (int i = offset+length; i < theXid.bqual_length; i++) {
+			theXid.data[i] = 0;
+		}
+	}
+	public static String getSubordinateNodeName(XID xid) {
+		// Arjuna.XID()
+		// don't check the formatId - it may differ e.g. JTA vs. JTS.
+		if (xid.formatID != FORMAT_ID) {
+			return null;
+		}
+
+		// the node name follows the Uid with no separator, so the only
+		// way to tell where it starts is to figure out how long the Uid is.
+		int offset = xid.gtrid_length + Uid.UID_SIZE + 4;
+
+		int length = (xid.data[offset++] << 24)
+				+ ((xid.data[offset++] & 0xFF) << 16)
+				+ ((xid.data[offset++] & 0xFF) << 8)
+				+ (xid.data[offset++] & 0xFF);
+		if (length > 0) {
+			return new String(Arrays.copyOfRange(xid.data, offset, offset+length));
+		} else {
+			return null;
+		}
+	}
+
+
+	public static void setBranchUID(XID xid, Uid uid) {
+		if (xid == null || xid.formatID != FORMAT_ID) {
+			return;
+		}
+
+		byte[] bqual = uid.getBytes();
+		System.arraycopy(bqual, 0, xid.data, xid.gtrid_length, Uid.UID_SIZE);
+	}
+	
+    public static Uid getBranchUid(XID xid)
     {
         if (xid == null || xid.formatID != FORMAT_ID) {
             return Uid.nullUid();
@@ -186,29 +232,35 @@
         return tx;
     }
 
-    private static String getEISName(XID xid)
+
+	public static void setEisName(XID theXid, Integer eisName) {
+		if (theXid == null || theXid.formatID != FORMAT_ID) {
+			return;
+		}
+		if (eisName == null) {
+			eisName = 0;
+		}
+		int offset = theXid.gtrid_length + Uid.UID_SIZE;
+		theXid.data[offset + 0] = (byte) (eisName >>> 24);
+		theXid.data[offset + 1] = (byte) (eisName >>> 16);
+		theXid.data[offset + 2] = (byte) (eisName >>> 8);
+		theXid.data[offset + 3] = (byte) (eisName >>> 0);
+	}
+	
+    public static Integer getEISName(XID xid)
     {
         if(xid == null || xid.formatID != FORMAT_ID) {
-            return "unknown eis name";
+            return -1;
         }
 
-        Uid uid = getUid(xid);
-        int uidLength = uid.getBytes().length;
-        int nameLength = xid.bqual_length-uidLength;
+		// the node name follows the Uid with no separator, so the only
+		// way to tell where it starts is to figure out how long the Uid is.
+		int offset = xid.gtrid_length + Uid.UID_SIZE;
 
-        if(nameLength == 0) {
-            return "unknown eis name";
-        }
-
-        byte[] eisName = new byte[nameLength];
-        System.arraycopy(xid.data, xid.gtrid_length+uidLength, eisName, 0, eisName.length);
-
-        try {
-            return new String(eisName, "US-ASCII");
-        } catch(UnsupportedEncodingException e) {
-            // should never happen, we use a required charset.
-            return "<failed to get eisName>";
-        }
+		return (xid.data[offset + 0] << 24)
+				+ ((xid.data[offset + 1] & 0xFF) << 16)
+				+ ((xid.data[offset + 2] & 0xFF) << 8)
+				+ (xid.data[offset + 3] & 0xFF);
     }
 
     public static String getXIDString(XID xid)
@@ -227,9 +279,15 @@
         stringBuilder.append(", node_name=");
         stringBuilder.append(getNodeName(xid));
         stringBuilder.append(", branch_uid=");
-        stringBuilder.append(getBranchUid(xid));
+        stringBuilder.append(getBranchUid(xid));;
+        stringBuilder.append(", subordinatenodename=");
+        stringBuilder.append(getSubordinateNodeName(xid));
         stringBuilder.append(", eis_name=");
-        stringBuilder.append(getEISName(xid));
+        if (xaResourceRecordWrappingPlugin != null) {
+        	stringBuilder.append(xaResourceRecordWrappingPlugin.getEISName(getEISName(xid)));
+        } else {
+        	stringBuilder.append(getEISName(xid));
+        }
         stringBuilder.append(" >");
 
         return stringBuilder.toString();

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/xa/XidImple.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/xa/XidImple.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/classes/com/arjuna/ats/jta/xa/XidImple.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -35,6 +35,7 @@
 import com.arjuna.ats.jta.logging.jtaLogger;
 
 import com.arjuna.ats.arjuna.common.*;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
 import com.arjuna.ats.arjuna.AtomicAction;
 import com.arjuna.ats.arjuna.state.*;
 
@@ -46,88 +47,80 @@
 
 /**
  * Implementation of javax.transaction.xa.Xid.
- *
+ * 
  * @author Mark Little (mark at arjuna.com)
- * @version $Id: XidImple.java 2342 2006-03-30 13:06:17Z  $
+ * @version $Id: XidImple.java 2342 2006-03-30 13:06:17Z $
  * @since JTS 1.2.4.
  */
 
-public class XidImple implements javax.transaction.xa.Xid, Serializable
-{
+public class XidImple implements javax.transaction.xa.Xid, Serializable {
 	private static final long serialVersionUID = -8922505475867377266L;
 
-	public XidImple ()
-	{
+	public XidImple() {
 		_theXid = null;
-		hashCode = getHash(_theXid) ;
+		hashCode = getHash(_theXid);
 	}
 
-	public XidImple (Xid xid)
-	{
+	public XidImple(Xid xid) {
 		_theXid = null;
 
 		copy(xid);
+		
 		hashCode = getHash(_theXid) ;
 	}
 
-	public XidImple (AtomicAction c)
-	{
+	public XidImple(AtomicAction c) {
 		this(c.get_uid(), false, null);
 	}
 
-	public XidImple (AtomicAction c, boolean branch, String eisName)
-	{
-		this(c.get_uid(), branch, eisName);
+	public XidImple(Xid xid, boolean branch, Integer eisName) {
+		this(xid);
+		if (branch) {
+			XATxConverter.setBranchUID(_theXid, new Uid());
+		}
+		XATxConverter.setEisName(_theXid, eisName);
 	}
 
-	public XidImple (Uid id)
-	{
+	/**
+	 * @deprecated This is only used by test code
+	 */
+	public XidImple(Uid id) {
 		this(id, false, null);
 	}
 
-	public XidImple (Uid id, boolean branch, String eisName)
-	{
-		try
-		{
+	public XidImple(Uid id, boolean branch, Integer eisName) {
+		try {
 			_theXid = XATxConverter.getXid(id, branch, eisName);
-		}
-		catch (Exception e)
-		{
+		} catch (Exception e) {
 			_theXid = null;
+			e.printStackTrace();
 
 			// abort or throw exception?
 		}
-		hashCode = getHash(_theXid) ;
+		hashCode = getHash(_theXid);
 	}
 
-	public XidImple (XID x)
-	{
+	public XidImple(XID x) {
 		_theXid = x;
-		hashCode = getHash(_theXid) ;
+		hashCode = getHash(_theXid);
 	}
 
-	public final boolean isSameTransaction (Xid xid)
-	{
+	public final boolean isSameTransaction(Xid xid) {
 		if (xid == null)
 			return false;
 
-		if (xid instanceof XidImple)
-		{
+		if (xid instanceof XidImple) {
 			return _theXid.isSameTransaction(((XidImple) xid)._theXid);
 		}
 
-		if (getFormatId() == xid.getFormatId())
-		{
+		if (getFormatId() == xid.getFormatId()) {
 			byte[] gtx = xid.getGlobalTransactionId();
 
-			if (_theXid.gtrid_length == gtx.length)
-			{
+			if (_theXid.gtrid_length == gtx.length) {
 				if (equals(xid))
 					return true;
-				else
-				{
-					for (int i = 0; i < _theXid.gtrid_length; i++)
-					{
+				else {
+					for (int i = 0; i < _theXid.gtrid_length; i++) {
 						if (_theXid.data[i] != gtx[i])
 							return false;
 					}
@@ -140,13 +133,10 @@
 		return false;
 	}
 
-	public int getFormatId ()
-	{
-		if (_theXid != null)
-		{
+	public int getFormatId() {
+		if (_theXid != null) {
 			return _theXid.formatID;
-		}
-		else
+		} else
 			return -1;
 	}
 
@@ -155,108 +145,87 @@
 	 * the order we packed it!
 	 */
 
-	public byte[] getGlobalTransactionId ()
-	{
-		if (_theXid != null)
-		{
+	public byte[] getGlobalTransactionId() {
+		if (_theXid != null) {
 			byte b[] = new byte[_theXid.gtrid_length];
 
 			System.arraycopy(_theXid.data, 0, b, 0, b.length);
 
 			return b;
-		}
-		else
+		} else
 			return null;
 	}
 
-	public byte[] getBranchQualifier ()
-	{
-		if (_theXid != null)
-		{
+	public byte[] getBranchQualifier() {
+		if (_theXid != null) {
 			byte b[] = new byte[_theXid.bqual_length];
 
 			System.arraycopy(_theXid.data, _theXid.gtrid_length, b, 0, b.length);
 
 			return b;
-		}
-		else
+		} else
 			return null;
 	}
 
-    public final Uid getTransactionUid() {
-        return XATxConverter.getUid(_theXid);
-    }
+	public final Uid getTransactionUid() {
+		return XATxConverter.getUid(_theXid);
+	}
 
-    public final String getNodeName() {
-        return XATxConverter.getNodeName(_theXid);
-    }
-
-	public final XID getXID ()
-	{
+	public final XID getXID() {
 		return _theXid;
 	}
 
-	public final void copy (Xid xid)
-	{
+	public final void copy(Xid xid) {
 		_theXid = new XID();
 
-		if (xid != null)
-		{
+		if (xid != null) {
 			if (xid instanceof XidImple)
 				_theXid.copy(((XidImple) xid)._theXid);
-			else
-			{
+			else {
 				_theXid.formatID = xid.getFormatId();
 
 				byte[] gtx = xid.getGlobalTransactionId();
 				byte[] bql = xid.getBranchQualifier();
-                final int bqlength = (bql == null ? 0 : bql.length) ;
+				final int bqlength = (bql == null ? 0 : bql.length);
 
 				_theXid.gtrid_length = gtx.length;
 				_theXid.bqual_length = bqlength;
 
 				System.arraycopy(gtx, 0, _theXid.data, 0, gtx.length);
-                if (bqlength > 0)
-                {
-                    System.arraycopy(bql, 0, _theXid.data, gtx.length, bql.length);
-                }
+				if (bqlength > 0) {
+					System.arraycopy(bql, 0, _theXid.data, gtx.length,
+							bql.length);
+				}
 			}
 		}
 	}
 
-	public boolean equals (Xid xid)
-	{
+	public boolean equals(Xid xid) {
 		if (xid == null)
 			return false;
 
 		if (xid == this)
 			return true;
-		else
-		{
+		else {
 			if (xid instanceof XidImple)
 				return ((XidImple) xid)._theXid.equals(_theXid);
-			else
-			{
-				if (xid.getFormatId() == _theXid.formatID)
-				{
+			else {
+				if (xid.getFormatId() == _theXid.formatID) {
 					byte[] gtx = xid.getGlobalTransactionId();
 					byte[] bql = xid.getBranchQualifier();
-                    final int bqlength = (bql == null ? 0 : bql.length) ;
+					final int bqlength = (bql == null ? 0 : bql.length);
 
 					if ((_theXid.gtrid_length == gtx.length)
-							&& (_theXid.bqual_length == bqlength))
-					{
+							&& (_theXid.bqual_length == bqlength)) {
 						int i;
 
-						for (i = 0; i < _theXid.gtrid_length; i++)
-						{
+						for (i = 0; i < _theXid.gtrid_length; i++) {
 							if (_theXid.data[i] != gtx[i])
 								return false;
 						}
 
 						for (i = _theXid.gtrid_length; i < _theXid.gtrid_length
-								+ _theXid.bqual_length; i++)
-						{
+								+ _theXid.bqual_length; i++) {
 							if (_theXid.data[i] != bql[i])
 								return false;
 						}
@@ -270,21 +239,17 @@
 		return false;
 	}
 
-	public final boolean packInto (OutputObjectState os)
-	{
+	public final boolean packInto(OutputObjectState os) {
 		boolean result = false;
 
-		try
-		{
+		try {
 			os.packInt(_theXid.formatID);
 			os.packInt(_theXid.gtrid_length);
 			os.packInt(_theXid.bqual_length);
 			os.packBytes(_theXid.data);
 
 			result = true;
-		}
-		catch (Exception e)
-		{
+		} catch (Exception e) {
 			e.printStackTrace();
 
 			result = false;
@@ -293,12 +258,10 @@
 		return result;
 	}
 
-	public final boolean unpackFrom (InputObjectState os)
-	{
+	public final boolean unpackFrom(InputObjectState os) {
 		boolean result = false;
 
-		try
-		{
+		try {
 			if (_theXid == null)
 				_theXid = new XID();
 
@@ -310,29 +273,23 @@
 			hashCode = getHash(_theXid);
 
 			result = true;
-		}
-		catch (Exception e)
-		{
+		} catch (Exception e) {
 			result = false;
 		}
 
 		return result;
 	}
 
-	public static final void pack (OutputObjectState os, Xid xid)
-			throws IOException
-	{
-		if (xid instanceof XidImple)
-		{
+	public static final void pack(OutputObjectState os, Xid xid)
+			throws IOException {
+		if (xid instanceof XidImple) {
 			XidImple x = (XidImple) xid;
 
 			os.packBoolean(true);
 
 			if (!x.packInto(os))
-				throw new IOException( jtaLogger.i18NLogger.get_xid_packerror() );
-		}
-		else
-		{
+				throw new IOException(jtaLogger.i18NLogger.get_xid_packerror());
+		} else {
 			os.packBoolean(false);
 
 			ByteArrayOutputStream s = new ByteArrayOutputStream();
@@ -345,20 +302,15 @@
 		}
 	}
 
-	public static final Xid unpack (InputObjectState os) throws IOException
-	{
-		if (os.unpackBoolean())
-		{
+	public static final Xid unpack(InputObjectState os) throws IOException {
+		if (os.unpackBoolean()) {
 			XidImple x = new XidImple();
 
 			x.unpackFrom(os);
 
 			return x;
-		}
-		else
-		{
-			try
-			{
+		} else {
+			try {
 				byte[] b = os.unpackBytes();
 
 				ByteArrayInputStream s = new ByteArrayInputStream(b);
@@ -367,18 +319,15 @@
 				Xid x = (Xid) o.readObject();
 
 				return x;
+			} catch (Exception e) {
+				IOException ioException = new IOException(e.toString());
+				ioException.initCause(e);
+				throw ioException;
 			}
-			catch (Exception e)
-			{
-                IOException ioException = new IOException(e.toString());
-                ioException.initCause(e);
-                throw ioException;
-			}
 		}
 	}
 
-	public String toString ()
-	{
+	public String toString() {
 		if (_theXid != null)
 			return _theXid.toString();
 		else
@@ -387,57 +336,60 @@
 
 	/**
 	 * Is the specified object equal to this one?
-	 * @param obj The object to test.
+	 * 
+	 * @param obj
+	 *            The object to test.
 	 * @return true if they are equal, false otherwise.
 	 */
-    public boolean equals(final Object obj)
-    {
-        if (obj instanceof Xid)
-        {
-        	return equals((Xid)obj) ;
-        }
-        return false ;
-    }
+	public boolean equals(final Object obj) {
+		if (obj instanceof Xid) {
+			return equals((Xid) obj);
+		}
+		return false;
+	}
 
-    /**
-     * Return the hash code for this Xid.
-     * @return the hash code.
-     */
-    public int hashCode()
-    {
-        return hashCode ;
-    }
+	/**
+	 * Return the hash code for this Xid.
+	 * 
+	 * @return the hash code.
+	 */
+	public int hashCode() {
+		return hashCode;
+	}
 
-    /**
-     * Generate the hash code for the xid.
-     * @param xid The xid.
-     * @return The hash code.
-     */
-    private static int getHash(final XID xid)
-    {
-    	if (xid == null)
-    	{
-    		return 0 ;
-    	}
-    	final int hash = generateHash(xid.formatID, xid.data, 0, xid.gtrid_length) ;
-        return generateHash(hash, xid.data, xid.gtrid_length, xid.bqual_length) ;
-    }
+	/**
+	 * Generate the hash code for the xid.
+	 * 
+	 * @param xid
+	 *            The xid.
+	 * @return The hash code.
+	 */
+	protected int getHash(final XID xid) {
+		if (xid == null) {
+			return 0;
+		}
+		final int hash = generateHash(xid.formatID, xid.data, 0,
+				xid.gtrid_length);
+		return generateHash(hash, xid.data, xid.gtrid_length, xid.bqual_length);
+	}
 
 	/**
 	 * Generate a hash code for the specified bytes.
-	 * @param hash The initial hash.
-	 * @param bytes The bytes to include in the hash.
+	 * 
+	 * @param hash
+	 *            The initial hash.
+	 * @param bytes
+	 *            The bytes to include in the hash.
 	 * @return The new hash code.
 	 */
-    private static int generateHash(int hash, final byte[] bytes, final int start, final int length)
-    {
-        for(int count = start ; count < length ; count++)
-        {
-            hash = 31 * hash + bytes[count] ;
-        }
-        return hash ;
-    }
+	protected static int generateHash(int hash, final byte[] bytes,
+			final int start, final int length) {
+		for (int count = start; count < length; count++) {
+			hash = 31 * hash + bytes[count];
+		}
+		return hash;
+	}
 
-	private XID _theXid;
-    private int hashCode ;
+	protected XID _theXid;
+	private int hashCode;
 }

Added: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/XATxConverterTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/XATxConverterTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/XATxConverterTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,68 @@
+package com.arjuna.ats.jta.xa;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import javax.transaction.xa.Xid;
+
+import org.junit.Test;
+
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.common.arjPropertyManager;
+
+public class XATxConverterTest {
+
+	@Test
+	public void testXAConverter() throws CoreEnvironmentBeanException {
+		Uid uid = new Uid();
+		boolean branch = true;
+		Integer eisName = 1;
+		arjPropertyManager.getCoreEnvironmentBean().setNodeIdentifier("1");
+
+		XidImple rootXid = new XidImple(uid, branch, eisName);
+		{
+			assertEquals(XATxConverter.getNodeName(rootXid.getXID()), "1");
+			assertEquals(XATxConverter.getEISName(rootXid.getXID()), eisName);
+			assertEquals(XATxConverter.getSubordinateNodeName(rootXid.getXID()), null);
+		}
+
+		// TxControl.setXANodeName(2);
+		XATxConverter.setSubordinateNodeName(rootXid.getXID(), "1");
+		XidImple subordinateXid = new XidImple(rootXid);
+		{
+			assertEquals(XATxConverter.getNodeName(subordinateXid.getXID()), "1");
+			assertEquals(XATxConverter.getEISName(subordinateXid.getXID()), eisName);
+			assertEquals(XATxConverter.getSubordinateNodeName(subordinateXid.getXID()), "1");
+		}
+	}
+
+	@Test
+	public void testForeignXID() {
+		XidImple foreignXidImple = new XidImple(new MyForeignXID());
+
+		assertEquals(XATxConverter.getNodeName(foreignXidImple.getXID()), null);
+		assertTrue(XATxConverter.getEISName(foreignXidImple.getXID()) == -1);
+		assertEquals(XATxConverter.getSubordinateNodeName(foreignXidImple.getXID()), null);
+	}
+
+	private class MyForeignXID implements Xid {
+
+		@Override
+		public int getFormatId() {
+			// TODO Auto-generated method stub
+			return 1;
+		}
+
+		@Override
+		public byte[] getGlobalTransactionId() {
+			return "foo".getBytes();
+		}
+
+		@Override
+		public byte[] getBranchQualifier() {
+			return "bar".getBytes();
+		}
+
+	}
+}


Property changes on: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/arjuna/ats/jta/xa/XATxConverterTest.java
___________________________________________________________________
Added: svn:executable
   + *

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/common/RecoveryXAResource.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/common/RecoveryXAResource.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/common/RecoveryXAResource.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -51,11 +51,9 @@
 	
 	    xids[0] = new XidImple(a);
 
-	    byte[] c = com.arjuna.ats.arjuna.coordinator.TxControl.getXANodeName();
+	    String c = com.arjuna.ats.arjuna.coordinator.TxControl.getXANodeName();
 	
-	    byte[] b = new byte[1];
-	    
-	    b[0] = 'c';
+	    String b = "2";
 	
 	    com.arjuna.ats.arjuna.coordinator.TxControl.setXANodeName(b);
 	

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/nested/SimpleNestedTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/nested/SimpleNestedTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/nested/SimpleNestedTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -1,20 +1,20 @@
 /*
  * JBoss, Home of Professional Open Source
- * Copyright 2006, Red Hat Middleware LLC, and individual contributors 
- * as indicated by the @author tags. 
+ * 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. 
+ * 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 
+ * 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, 
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  * MA  02110-1301, USA.
- * 
+ *
  * (C) 2005-2006,
  * @author JBoss Inc.
  */
@@ -66,5 +66,6 @@
 
         transactionManager.commit();
     }
+
     // testDisabled moved to its own class, as it needs separate jvm to allow different property value in static init.
 }

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/RecoveryManagerTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/RecoveryManagerTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/RecoveryManagerTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -50,5 +50,7 @@
 
         manager.scan();
         manager.scan();
+        System.clearProperty("com.arjuna.ats.jta.xaRecoveryNode");
+        System.clearProperty("XAResourceRecovery1");
     }
 }

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/RecoveryTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/RecoveryTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/RecoveryTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -48,12 +48,10 @@
     public void test()
     {
         Vector xaRecoveryNodes = new Vector();
-        Uid bogusNodeName = new Uid();
+        xaRecoveryNodes.add("2");
 
-        xaRecoveryNodes.add(bogusNodeName.stringForm());
+        System.err.println("Bogus XA node name: "+"2");
 
-        System.err.println("Bogus XA node name: "+bogusNodeName);
-
         XidImple xid = new XidImple(new Uid());
         String nodeName = XAUtils.getXANodeName(xid);
 

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/XARecoveryModuleUnitTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/XARecoveryModuleUnitTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/XARecoveryModuleUnitTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -44,7 +44,7 @@
 import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
 import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
-import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.TransactionImple;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.TransactionImple;
 import com.arjuna.ats.jta.common.jtaPropertyManager;
 import com.hp.mwtests.ts.jta.common.RecoveryXAResource;
 

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/XAResourceOrphanFilterTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/XAResourceOrphanFilterTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/recovery/XAResourceOrphanFilterTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -24,6 +24,7 @@
 import com.arjuna.ats.arjuna.coordinator.TxControl;
 import com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter;
 import com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter;
+import com.arjuna.ats.internal.jta.recovery.arjunacore.NodeNameXAResourceOrphanFilter;
 import com.arjuna.ats.jta.common.jtaPropertyManager;
 import com.arjuna.ats.jta.recovery.XAResourceOrphanFilter;
 import com.arjuna.ats.jta.xa.XATxConverter;
@@ -53,23 +54,23 @@
         assertEquals(XAResourceOrphanFilter.Vote.ABSTAIN, orphanFilter.checkXid(notJTAFormatId));
 
         List<String> recoveryNodes = new LinkedList<String>();
-        recoveryNodes.add("AA");
+        recoveryNodes.add("1");
         jtaPropertyManager.getJTAEnvironmentBean().setXaRecoveryNodes(recoveryNodes);
 
-        byte[] notRecoverableNodeName = new byte[] { 'A', 'B' };
+        String notRecoverableNodeName ="2";
         TxControl.setXANodeName(notRecoverableNodeName);
         Xid jtaNotRecoverableNodeName = XATxConverter.getXid(new Uid(), false, XATxConverter.FORMAT_ID);
 
         assertEquals(XAResourceOrphanFilter.Vote.ABSTAIN, orphanFilter.checkXid(jtaNotRecoverableNodeName));
 
-        byte[] recoverableNodeName = new byte[] { 'A', 'A' };
+        String recoverableNodeName ="1";
         TxControl.setXANodeName(recoverableNodeName);
         Xid jtaRecoverableNodeName = XATxConverter.getXid(new Uid(), false, XATxConverter.FORMAT_ID);
 
         assertEquals(XAResourceOrphanFilter.Vote.ROLLBACK, orphanFilter.checkXid(jtaRecoverableNodeName));
 
         recoveryNodes.clear();
-        recoveryNodes.add("*");
+        recoveryNodes.add(NodeNameXAResourceOrphanFilter.RECOVER_ALL_NODES);
         jtaPropertyManager.getJTAEnvironmentBean().setXaRecoveryNodes(recoveryNodes);
 
         assertEquals(XAResourceOrphanFilter.Vote.ROLLBACK, orphanFilter.checkXid(jtaNotRecoverableNodeName));

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/tools/ObjStoreBrowserTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/tools/ObjStoreBrowserTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/tools/ObjStoreBrowserTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -42,10 +42,7 @@
 	private ObjStoreBrowser createObjStoreBrowser() {
 		ObjStoreBrowser osb = new ObjStoreBrowser();
 
-		osb.setTypes( new HashMap<String, String>() {{
-			put("StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple", "com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean");
-			put("StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction", "com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean");
-		}});
+		osb.setType("com.arjuna.ats.arjuna.AtomicAction", "com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean");
 
 		return osb;
 	}

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/xa/XAUtilsUnitTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/xa/XAUtilsUnitTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/xa/XAUtilsUnitTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -39,6 +39,7 @@
 import org.junit.Test;
 
 import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
 import com.arjuna.ats.internal.jta.resources.XAResourceErrorHandler;
 import com.arjuna.ats.internal.jta.utils.XAUtils;
 import com.arjuna.ats.jta.common.jtaPropertyManager;
@@ -72,7 +73,7 @@
         
         assertFalse(XAUtils.mustEndSuspendedRMs(xa));
         assertTrue(XAUtils.canOptimizeDelist(xa));      
-        assertTrue(XAUtils.getXANodeName(new XidImple(new Uid())) != null);
+        assertEquals(XAUtils.getXANodeName(new XidImple(new Uid())), TxControl.getXANodeName());
     }
     
     @Test

Modified: labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/xa/xidcheck.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/xa/xidcheck.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTA/jta/tests/classes/com/hp/mwtests/ts/jta/xa/xidcheck.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -33,9 +33,11 @@
 
 import com.arjuna.ats.arjuna.AtomicAction;
 import com.arjuna.ats.arjuna.common.*;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
 import com.arjuna.ats.arjuna.state.InputObjectState;
 import com.arjuna.ats.arjuna.state.OutputObjectState;
 import com.arjuna.ats.internal.jta.xa.XID;
+import com.arjuna.ats.jta.xa.XATxConverter;
 import com.arjuna.ats.jta.xa.XidImple;
 
 import org.junit.Test;
@@ -82,7 +84,7 @@
         
         assertEquals(xid1.getTransactionUid(), A.get_uid());
         
-        assertTrue(xid1.getNodeName() != null);
+        assertTrue(XATxConverter.getNodeName(xid1.getXID()).equals(TxControl.getXANodeName()));
         
         assertTrue(xid1.getXID() != null);
         

Modified: labs/jbosstm/trunk/ArjunaJTS/INSTALL
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/INSTALL	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/INSTALL	2012-01-04 16:33:42 UTC (rev 37829)
@@ -1,7 +1,6 @@
 
-JBossTS 4.15 is designed for use standalone.
-The JTA is also used by JBossAS 7 beta releases, but manual upgrading of the component inside JBossAS is not recommended.
- JTS and XTS are not currently available in AS7.
+JBossTS 4.16 is designed for use standalone.
+It may also be used by JBossAS 7.1 releases, but manual upgrading of the component inside JBossAS is not recommended.
 Integration with JBossAS 6 or earlier is no longer supported.
 
 
@@ -11,12 +10,14 @@
 JMX Instrumentation
 -------------------
 
-With this release it is now possible to monitor the transaction Object
-Store using JMX. Monitoring the Object Store is useful for trouble
+It is possible to monitor the transaction Object Store using JMX.
+Monitoring the Object Store is useful for trouble
 shooting problems that occur when transactions are committed (it does
 not expose transactions prior to commit). The JMX instrumentation
-(of the ObjectStore) is a new feature and is not necessarily suitable
-for monitoring production systems.
+(of the ObjectStore) is a not fully integrated with the AS console and
+is therefore not necessarily suitable for monitoring production systems
+but can provide useful diagnostic information that is otherwise not
+available.
 
 In any compliant JMX browser (such as jconsole) there should appear an
 MBean with the name
@@ -25,20 +26,42 @@
 for other MBeans (for example in a JMX client such as jconsole the MBeans
 will be displayed in the form of a tree control). The various MBeans
 corresponding to this ObjectStore will have names prefixed by this
-'top level' MBean.
+'top level' MBean. The bean has two operations:
 
-Simply instantiate an object of type com.hp.mwtests.ts.arjuna.tools.ObjStoreBrowser
-and initialise it with a valid set of types for handling ObjectStore records. These types
-can be set using a setTypes() method via a properties file on the classpath.
+probe - searches for Object Store records and creates corresponding MBeans
+	or unregisters any beans that no longer have a corresponding record;
+viewSubordinateAtomicActions - toggles the ability to view Subordinate Atomic Actions.
+        Set the boolean parameter to the text "true"/"false" to enable/disable viewing
+        of these record types.
+        WARNING: Viewing a Subordinate Atomic Action will trigger a recovery scan
+	independently of the Transaction Recovery Service so please use with care
+	(for example if you know that the distributed transaction is not recovering
+	automatically).
+
+Each MBean corresponding to an transaction contains an operation for removing the
+record from the object store. After this operation the system will no longer have
+any knowledge of the transaction.
+
+Transactions contain participants such datasources. These have MBean representations in
+the JMX viewer. It sometimes happens that a particpant made a different transaction
+completion decision from the one the Transaction Manager expected - such a state is
+called a heuristic. Heuristics will never be completed automatically so the MBean
+contains an operation (called clearHeuristic) for moving the participant back into
+the "prepared" state. Once back in the prepared state the Recovery system will
+attempt to replay completion.
+
+[For embedded use simply instantiate an object of type
+com.hp.mwtests.ts.arjuna.tools.ObjStoreBrowser.
+If the default set of types is not adequate other types for handling ObjectStore records
+can be added either by setting a System property called
+ObjStoreBrowser.OBJ_STORE_BROWSER_HANDLERS or by calling the method setType() on the
+ObjStoreBrowser object.]
+
 Please refer to the unit tests in the src distribution for more details.
 
 Tools Deployment
 ----------------
 
-Transaction management is integrated into the admin console in the form of a JOPR plugin
-which is located in the install bin directory (jbossts-jopr-plugin.jar). Install it by copying
-to the admin console plugin directory ($JBOSS_HOME/common/deploy/admin-console.war/plugins).
-
 There is also a transaction statistics graphing tool which can run standalone or inside a
 jconsole tab (jconsole, a tool for managing JVMs, is distributed with the reference JDK).
 Various transaction statistics are graphed in real time with each graph updated during each

Modified: labs/jbosstm/trunk/ArjunaJTS/integration/pom.xml
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/integration/pom.xml	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/integration/pom.xml	2012-01-04 16:33:42 UTC (rev 37829)
@@ -25,6 +25,17 @@
   <packaging>jar</packaging>
   <build>
     <sourceDirectory>../../atsintegration/classes</sourceDirectory>
+<!--
+    <testSourceDirectory>../../atsintegration/tests</testSourceDirectory>
+    <testResources>
+      <testResource>
+        <directory>../../atsintegration/tests/resources</directory>
+      </testResource>
+      <testResource>
+        <directory>../../atsintegration/tests/byteman-scripts</directory>
+      </testResource>
+    </testResources>
+-->
     <plugins>
       <plugin>
         <inherited>false</inherited>

Modified: labs/jbosstm/trunk/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jts/ArjunaTransactionImpleWrapper.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jts/ArjunaTransactionImpleWrapper.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jts/ArjunaTransactionImpleWrapper.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -2,6 +2,7 @@
 
 import com.arjuna.ats.arjuna.common.Uid;
 import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
+import com.arjuna.ats.arjuna.coordinator.BasicAction;
 import com.arjuna.ats.arjuna.coordinator.RecordList;
 import com.arjuna.ats.arjuna.tools.osb.mbean.*;
 import com.arjuna.ats.internal.jts.orbspecific.coordinator.ArjunaTransactionImple;
@@ -16,6 +17,9 @@
     ActionBean action;
     boolean activated;
 
+    public ArjunaTransactionImpleWrapper () {
+        super(Uid.nullUid());
+    }
     public ArjunaTransactionImpleWrapper (ActionBean action, UidWrapper w) {
         super(w.getUid());
         this.action = action;
@@ -60,6 +64,10 @@
         return sb.append('\n').append(prefix).append(get_uid());
     }
 
+    public BasicAction getAction() {
+        return null;
+    }
+
     public void clearHeuristicDecision(int newDecision) {
         if (super.heuristicList.size() == 0)
             setHeuristicDecision(newDecision);

Modified: labs/jbosstm/trunk/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jts/XAResourceRecordBean.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jts/XAResourceRecordBean.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jtax/classes/com/arjuna/ats/internal/jta/tools/osb/mbean/jts/XAResourceRecordBean.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -44,8 +44,9 @@
 
             if (_theXAResource != null) {
                 XAResourceRecordBean.this.className = _theXAResource.getClass().getName();
-                XAResourceRecordBean.this.eisProductName =  callMethod(_theXAResource, "getEisProductName");
-                XAResourceRecordBean.this.eisProductVersion = callMethod(_theXAResource, "getEisProductVersion");
+                XAResourceRecordBean.this.jndiName =  callMethod(_theXAResource, "getJndiName");
+                XAResourceRecordBean.this.eisProductName =  callMethod(_theXAResource, "getProductName");
+                XAResourceRecordBean.this.eisProductVersion = callMethod(_theXAResource, "getProductVersion");
 
                 try {
                     timeout = _theXAResource.getTransactionTimeout();

Modified: labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/common/RecoveryXAResource.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/common/RecoveryXAResource.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/common/RecoveryXAResource.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -51,11 +51,9 @@
 	
 	    xids[0] = new XidImple(a);
 
-	    byte[] c = com.arjuna.ats.arjuna.coordinator.TxControl.getXANodeName();
+	    String c = com.arjuna.ats.arjuna.coordinator.TxControl.getXANodeName();
 	
-	    byte[] b = new byte[1];
-	    
-	    b[0] = 'c';
+	    String b = "2";
 	
 	    com.arjuna.ats.arjuna.coordinator.TxControl.setXANodeName(b);
 	

Modified: labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/nested/SimpleNestedTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/nested/SimpleNestedTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/nested/SimpleNestedTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -1,8 +1,8 @@
 /*
  * 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 
+ * 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
@@ -14,7 +14,7 @@
  * 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.
  */
@@ -68,7 +68,7 @@
         transactionManager.begin();
 
         transactionManager.begin();
-        
+
         transactionManager.commit();
 
         transactionManager.commit();
@@ -77,6 +77,6 @@
         myORB.shutdown();
 
     }
-    
+
     // testDisabled moved to its own class, as it needs separate jvm to allow different property value in static init.
 }

Modified: labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/subordinate/SubordinateTestCase.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/subordinate/SubordinateTestCase.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/subordinate/SubordinateTestCase.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -69,6 +69,8 @@
         {
             orb.shutdown();
         }
+        System.clearProperty("com.arjuna.ats.jta.jtaTMImplementation");
+        System.clearProperty("com.arjuna.ats.jta.jtaUTImplementation");
     }
     
     @Override

Modified: labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/tools/JTSObjStoreBrowserTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/tools/JTSObjStoreBrowserTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jtax/tests/classes/com/hp/mwtests/ts/jta/jts/tools/JTSObjStoreBrowserTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -58,10 +58,8 @@
 	private ObjStoreBrowser createObjStoreBrowser() {
 		ObjStoreBrowser osb = new ObjStoreBrowser();
 
-		osb.setTypes( new HashMap<String, String>() {{
-			put("StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple", "com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean");
-			put("StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction", "com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean");
-		}});
+		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");
 
 		return osb;
 	}

Modified: labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/jts/utils/Utility.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/jts/utils/Utility.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jts/classes/com/arjuna/ats/jts/utils/Utility.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -175,7 +175,7 @@
 
 	otid_t otid = new otid_t();
 	byte[] b = theUid.getBytes();
-	byte[] nodeName = TxControl.getXANodeName();
+	byte[] nodeName = TxControl.getXANodeName().getBytes();
 
 	otid.formatID = 0;
 	otid.tid = new byte[b.length+nodeName.length];

Modified: labs/jbosstm/trunk/ArjunaJTS/jts/idl/omg/XA.idl
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jts/idl/omg/XA.idl	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jts/idl/omg/XA.idl	2012-01-04 16:33:42 UTC (rev 37829)
@@ -82,8 +82,7 @@
 			  in long rmid, in unsigned long flags);
     };
 	    
-    /*local interface CurrentConnection*/
-    interface CurrentConnection
+    local interface CurrentConnection
     {
 	void start( // xa_start(TMNOFLAGS) or xa_start(TMJOIN)
 		   in CosTransactions::Coordinator tx,
@@ -124,8 +123,7 @@
 	void unregister_before_completion_callback(in unsigned long key);
     };
 
-    /*local interface Connector*/
-    interface Connector
+    local interface Connector
     {
 	ResourceManager create_resource_manager(in string resource_manager_name,
 						in XASwitch xa_switch,

Modified: labs/jbosstm/trunk/ArjunaJTS/jts/tests/classes/com/hp/mwtests/ts/jts/local/orbsetup/ORBSetupTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jts/tests/classes/com/hp/mwtests/ts/jts/local/orbsetup/ORBSetupTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jts/tests/classes/com/hp/mwtests/ts/jts/local/orbsetup/ORBSetupTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -91,8 +91,8 @@
                 }
             }
 
+            myORB.destroy();
             myOA.destroy();
-            myORB.destroy();
         }
         catch (Throwable e)
         {

Modified: labs/jbosstm/trunk/ArjunaJTS/jts/tests/classes/com/hp/mwtests/ts/jts/orbspecific/recovery/RecoveryEnablementUnitTest.java
===================================================================
--- labs/jbosstm/trunk/ArjunaJTS/jts/tests/classes/com/hp/mwtests/ts/jts/orbspecific/recovery/RecoveryEnablementUnitTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/ArjunaJTS/jts/tests/classes/com/hp/mwtests/ts/jts/orbspecific/recovery/RecoveryEnablementUnitTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -31,6 +31,7 @@
 
 package com.hp.mwtests.ts.jts.orbspecific.recovery;
 
+import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -46,6 +47,11 @@
         // persistent POAs can't be anonymous, need a name:
         System.setProperty("jacorb.implname", "arjuna");
     }
+    
+    @AfterClass
+    public static void tearDownClass() {
+        System.clearProperty("jacorb.implname");
+    }
 
 
     @Test

Modified: labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/internal/jbossatx/jta/XAResourceRecordWrappingPluginImpl.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/internal/jbossatx/jta/XAResourceRecordWrappingPluginImpl.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/internal/jbossatx/jta/XAResourceRecordWrappingPluginImpl.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -20,38 +20,139 @@
  */
 package com.arjuna.ats.internal.jbossatx.jta;
 
-import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord;
-import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecordWrappingPlugin;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.transaction.xa.XAResource;
+
 import org.jboss.tm.XAResourceWrapper;
 
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+import com.arjuna.ats.arjuna.objectstore.ObjectStoreAPI;
+import com.arjuna.ats.arjuna.objectstore.StoreManager;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.internal.arjuna.common.UidHelper;
+import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord;
+import com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecordWrappingPlugin;
+
 /**
- * A plugin implementation for copying resource metadata from the JBoss AS specific
- * XAResourceWrapper class to an XAResourceRecord.
- *
+ * A plugin implementation for copying resource metadata from the JBoss AS
+ * specific XAResourceWrapper class to an XAResourceRecord.
+ * 
  * @author Jonathan Halliday (jonathan.halliday at redhat.com) 2011-07
  */
-public class XAResourceRecordWrappingPluginImpl implements XAResourceRecordWrappingPlugin
-{
-    public void transcribeWrapperData(XAResourceRecord record) {
+public class XAResourceRecordWrappingPluginImpl implements XAResourceRecordWrappingPlugin {
+	private ConcurrentMap<Integer, String> keyToName = new ConcurrentHashMap<Integer, String>();
+	private ConcurrentMap<String, Integer> nameToKey = new ConcurrentHashMap<String, Integer>();
+	private AtomicInteger nextKey = new AtomicInteger(1);
+	private ObjectStoreAPI eisNameStore;
+	private String nodeIdentifier;
 
-        XAResource xaResource = (XAResource)record.value();
+	public void transcribeWrapperData(XAResourceRecord record) {
 
-        if(xaResource instanceof XAResourceWrapper) {
-            XAResourceWrapper xaResourceWrapper = (XAResourceWrapper)xaResource;
-            record.setProductName(xaResourceWrapper.getProductName());
-            record.setProductVersion(xaResourceWrapper.getProductVersion());
-            record.setJndiName(xaResourceWrapper.getJndiName());
-        }
-    }
+		XAResource xaResource = (XAResource) record.value();
 
-    public String getEISName(XAResource xaResource) {
+		if (xaResource instanceof XAResourceWrapper) {
+			XAResourceWrapper xaResourceWrapper = (XAResourceWrapper) xaResource;
+			record.setProductName(xaResourceWrapper.getProductName());
+			record.setProductVersion(xaResourceWrapper.getProductVersion());
+			record.setJndiName(xaResourceWrapper.getJndiName());
+		}
+	}
 
-        if(xaResource instanceof XAResourceWrapper) {
-            return ((XAResourceWrapper) xaResource).getJndiName();
-        } else {
-            return null;
-        }
-    }
+	public Integer getEISName(XAResource xaResource) throws IOException, ObjectStoreException {
+		if (xaResource instanceof XAResourceWrapper) {
+			initialize();
+			String jndiName = ((XAResourceWrapper) xaResource).getJndiName();
+			Integer key = nameToKey.get(jndiName);
+			if (key == null) {
+				synchronized (this) {
+					// Recheck the resource, we do this so that we don't need to
+					// synchronize if this is a read
+					key = nameToKey.get(jndiName);
+					if (key == null) {
+						key = nextKey.getAndIncrement();
+						keyToName.put(key, jndiName);
+						nameToKey.put(jndiName, key);
+
+						OutputObjectState oos = new OutputObjectState();
+						oos.packString(nodeIdentifier);
+						oos.packInt(key);
+						oos.packString(jndiName);
+						eisNameStore.write_committed(new Uid(), "EISNAME", oos);
+						eisNameStore.sync();
+					}
+				}
+			}
+			return key;
+		} else {
+			return 0;
+		}
+	}
+
+	@Override
+	public String getEISName(Integer eisKey) {
+		try {
+			initialize();
+		} catch (IOException ioe) {
+			return "unloadable EIS key file: " + eisKey;
+		} catch (ObjectStoreException e) {
+			return "unloadable EIS key file: " + eisKey;
+		}
+		if (eisKey == 0) {
+			return "unknown eis name";
+		} else if (eisKey == -1) {
+			return "foreign XID";
+		} else {
+			String eisName = keyToName.get(eisKey);
+			if (eisName == null) {
+				return "forgot eis name for: " + eisKey;
+			} else {
+				return eisName;
+			}
+		}
+	}
+
+	private void initialize() throws ObjectStoreException, IOException {
+		if (this.nodeIdentifier == null) {
+			synchronized (this) {
+				// If we are here, check again that the node idenfier is still
+				// null in case of race condition
+				if (this.nodeIdentifier == null) {
+
+					this.nodeIdentifier = TxControl.getXANodeName();
+					this.eisNameStore = StoreManager.getEISNameStore();
+					InputObjectState states = new InputObjectState();
+					int keyMax = 0;
+					boolean allObjUids = eisNameStore.allObjUids("EISNAME", states);
+					while (states.notempty()) {
+						Uid uid = UidHelper.unpackFrom(states);
+						if (uid.equals(Uid.nullUid())) {
+							break;
+						} else {
+							InputObjectState oState = eisNameStore.read_committed(uid, "EISNAME");
+							String nodeName = oState.unpackString();
+							if (nodeName.equals(nodeIdentifier)) {
+								Integer key = oState.unpackInt();
+								String jndiName = oState.unpackString();
+								keyToName.put(key, jndiName);
+								nameToKey.put(jndiName, key);
+								if (key > keyMax) {
+									keyMax = key;
+								}
+							} else {
+								// logger warn that we are using a new node
+							}
+						}
+					}
+					nextKey.set(keyMax + 1);
+				}
+			}
+		}
+	}
 }

Modified: labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/RecoveryManagerService.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/RecoveryManagerService.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/RecoveryManagerService.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -20,18 +20,19 @@
  */
 package com.arjuna.ats.jbossatx.jta;
 
+import java.util.Vector;
+
+import org.jboss.tm.XAResourceRecovery;
+import org.jboss.tm.XAResourceRecoveryRegistry;
+
 import com.arjuna.ats.arjuna.recovery.RecoveryManager;
 import com.arjuna.ats.arjuna.recovery.RecoveryModule;
+import com.arjuna.ats.internal.jbossatx.jta.XAResourceRecoveryHelperWrapper;
 import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
-import com.arjuna.ats.internal.jbossatx.jta.XAResourceRecoveryHelperWrapper;
 import com.arjuna.ats.jbossatx.logging.jbossatxLogger;
+import com.arjuna.ats.jta.recovery.SerializableXAResourceDeserializer;
 import com.arjuna.common.util.ConfigurationInfo;
 
-import org.jboss.tm.XAResourceRecovery;
-import org.jboss.tm.XAResourceRecoveryRegistry;
-
-import java.util.Vector;
-
 /**
  * JBoss Transaction Recovery Service.
  *
@@ -42,7 +43,7 @@
 {
     private RecoveryManager _recoveryManager;
 
-    public void create() throws Exception
+    public void create()
     {
         String tag = ConfigurationInfo.getSourceId();
 
@@ -115,4 +116,22 @@
 
         xaRecoveryModule.removeXAResourceRecoveryHelper(new XAResourceRecoveryHelperWrapper(xaResourceRecovery));
     }
+
+	public void addSerializableXAResourceDeserializer(SerializableXAResourceDeserializer serializableXAResourceDeserializer) {
+
+        XARecoveryModule xaRecoveryModule = null;
+        for(RecoveryModule recoveryModule : ((Vector<RecoveryModule>)_recoveryManager.getModules())) {
+            if(recoveryModule instanceof XARecoveryModule) {
+                xaRecoveryModule = (XARecoveryModule)recoveryModule;
+                break;
+            }
+        }
+
+        if(xaRecoveryModule == null) {
+            throw new IllegalStateException(jbossatxLogger.i18NLogger.get_jta_RecoveryManagerService_norecoverymodule());
+        }
+
+        xaRecoveryModule.addSerializableXAResourceDeserializer(serializableXAResourceDeserializer);
+		
+	}
 }

Modified: labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/atsintegration/classes/com/arjuna/ats/jbossatx/jta/TransactionManagerService.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -57,7 +57,7 @@
 
     public TransactionManagerService() {}
 
-    public void create() throws Exception
+    public void create()
     {
         String tag = ConfigurationInfo.getSourceId();
 

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/ExampleDistributedJTATestCase.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/ExampleDistributedJTATestCase.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/ExampleDistributedJTATestCase.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,412 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+import org.jboss.tm.TransactionTimeoutConfiguration;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
+import com.arjuna.jta.distributed.example.server.IsolatableServersClassLoader;
+import com.arjuna.jta.distributed.example.server.LocalServer;
+import com.arjuna.jta.distributed.example.server.LookupProvider;
+
+/**
+ * This example shows how to use the JTA in a distributed manner.
+ * 
+ * In this example, LocalServer references should be considered to be activities
+ * that are performed on a local application server.
+ * 
+ * The method propagateTransaction is used to simulate invoking a remote server
+ * and should be considered the socket boundary between servers. If you look
+ * closely what I do to simulate this is use ClassLoaders so that servers dont
+ * share the same address space in the VM and won't therefore interfere with
+ * each other - inspired!
+ * 
+ * Note the use of LocalServer and RemoteServer is just an example, the
+ * transport is responsible for creating objects that perform similar
+ * capabilities to these.
+ * 
+ * Note that calls to getting the remaining time of a transaction may
+ * programatic configurably trigger a rollback exception which is good for
+ * certain situations, the example though guards "migrations" by checking their
+ * state before propagation - I recommend all transports do the same.
+ * 
+ * IMPORTANT: Although this example shows points at which the transport is
+ * expected to persist data, it does not define concretely the mechanisms to do
+ * so, nor should it be considered sufficient for reliably persisting this data.
+ * For instance, we do not flush to disk.
+ */
+public class ExampleDistributedJTATestCase {
+	/**
+	 * This is to simulate JNDI.
+	 */
+	private static LookupProvider lookupProvider = LookupProvider.getInstance();
+
+	/**
+	 * The list of server node names
+	 */
+	private static String[] serverNodeNames = new String[] { "1000", "2000", "3000" };
+
+	/**
+	 * A list of port offsets to use for the servers
+	 */
+	private static int[] serverPortOffsets = new int[] { 1000, 2000, 3000 };
+
+	/**
+	 * For each of the server nodes, a list of the other servers in the cluster
+	 */
+	private static String[][] clusterBuddies = new String[][] { new String[] { "2000", "3000" }, new String[] { "1000", "3000" },
+			new String[] { "1000", "2000" } };
+
+	/**
+	 * The example stores a reference to all local servers as a convenience
+	 */
+	private static LocalServer[] localServers = new LocalServer[serverNodeNames.length];
+
+	/**
+	 * Initialise references to the local and remote servers.
+	 * 
+	 * @throws SecurityException
+	 * @throws NoSuchMethodException
+	 * @throws InstantiationException
+	 * @throws IllegalAccessException
+	 * @throws ClassNotFoundException
+	 * @throws CoreEnvironmentBeanException
+	 * @throws IOException
+	 * @throws IllegalArgumentException
+	 * @throws NoSuchFieldException
+	 */
+	@BeforeClass
+	public static void setup() throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException,
+			CoreEnvironmentBeanException, IOException, IllegalArgumentException, NoSuchFieldException {
+		for (int i = 0; i < localServers.length; i++) {
+			// Create each instance of a server with its own private
+			// classloader, ensure all access to the local server instance is
+			// done within the scope of this classloader, this is to simulate a
+			// transports different address space
+			ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+			IsolatableServersClassLoader classLoaderForTransactionManager = new IsolatableServersClassLoader(null, ExampleDistributedJTATestCase.class
+					.getPackage().getName(), contextClassLoader);
+			IsolatableServersClassLoader classLoader = new IsolatableServersClassLoader(ExampleDistributedJTATestCase.class.getPackage().getName(), null,
+					classLoaderForTransactionManager);
+			localServers[i] = (LocalServer) classLoader.loadClass("com.arjuna.jta.distributed.example.server.impl.ServerImpl").newInstance();
+			Thread.currentThread().setContextClassLoader(localServers[i].getClassLoader());
+			localServers[i].initialise(lookupProvider, serverNodeNames[i], serverPortOffsets[i], clusterBuddies[i], classLoaderForTransactionManager);
+			// This is a short cut, normally remote servers would not be the
+			// same as the local servers and would be a tranport layer
+			// abstraction
+			lookupProvider.bind(i, localServers[i].connectTo());
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	/**
+	 * This example starts a transaction at the local server, it then performs
+	 * the steps required to propagate the transaction to a chain of remote
+	 * servers.
+	 * 
+	 * The nodesToFlowTo is a test abstraction that allows the example to
+	 * simulate conditional business logic that would propagate requests around
+	 * the cluster to access various business logic silos.
+	 * 
+	 * @throws NotSupportedException
+	 * @throws SystemException
+	 * @throws IllegalStateException
+	 * @throws RollbackException
+	 * @throws XAException
+	 * @throws SecurityException
+	 * @throws HeuristicMixedException
+	 * @throws HeuristicRollbackException
+	 * @throws IOException
+	 */
+	@Test
+	public void testMigrateTransaction() throws NotSupportedException, SystemException, IllegalStateException, RollbackException, XAException,
+			SecurityException, HeuristicMixedException, HeuristicRollbackException, IOException {
+
+		// The example does not set a timeout for transactions, we have unit
+		// tests that do
+		int startingTimeout = 0;
+
+		// The list of further nodes to propagate this transaction through
+		// These names are the transport allocated names, they happen to be
+		// string forms of the transaction manager node name but if you follow
+		// the code through you will see they could have been anything
+		List<String> nodesToFlowTo = new LinkedList<String>(Arrays.asList(new String[] { "2000", "3000", "2000", "1000", "2000", "3000", "1000", "3000" }));
+
+		// Start out at the first server
+		LocalServer originalServer = localServers[0];
+		// Access to this local server must be done by its own classloader to
+		// ensure the servers remain separate
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+
+		// THIS SIMULATES NORMAL BUSINESS LOGIC IN BMT/CMT (interceptors?)
+		{
+			// Get a reference on the transaction manager and create a
+			// transaction
+			TransactionManager transactionManager = originalServer.getTransactionManager();
+			transactionManager.setTransactionTimeout(startingTimeout);
+			transactionManager.begin();
+
+			// Do some simulated local work on the TestResource and register a
+			// TestSynchronization
+			Transaction transaction = transactionManager.getTransaction();
+			transaction.registerSynchronization(new TestSynchronization(originalServer.getNodeName()));
+			transaction.enlistResource(new TestResource(originalServer.getNodeName()));
+		}
+
+		// This is where we start to propagate the transaction - it is all
+		// transport related code - watch closely ;)
+		if (!nodesToFlowTo.isEmpty()) {
+
+			TransactionManager transactionManager = originalServer.getTransactionManager();
+			Transaction transaction = transactionManager.getTransaction();
+			int status = transaction.getStatus();
+
+			// Only propagate active transactions - this may be inactive through
+			// user code (rollback/setRollbackOnly) or it may be inactive due to
+			// the transaction reaper
+			if (status == Status.STATUS_ACTIVE) {
+				// Stash away the root transaction, this is needed in case a
+				// subordinate naughtily comes back to this server part way
+				// through
+				// so we can return the original transaction to them
+				originalServer.storeRootTransaction();
+
+				// Peek at the next node - this is just a test abstraction to
+				// simulate where business logic might decide to access an EJB
+				// at a
+				// server with a remoting name
+				String nextServerNodeName = nodesToFlowTo.get(0);
+
+				// Check the remaining timeout - false is passed in so the call
+				// doesn't raise a rollback exception
+				int remainingTimeout = (int) (((TransactionTimeoutConfiguration) transactionManager).getTimeLeftBeforeTransactionTimeout(false) / 1000);
+				// Get the Xid to propagate
+				Xid currentXid = originalServer.getCurrentXid();
+				// Suspend the transaction locally
+				transactionManager.suspend();
+
+				// WE CAN NOW PROPAGATE THE TRANSACTION
+				DataReturnedFromRemoteServer dataReturnedFromRemoteServer = propagateTransaction(nodesToFlowTo, remainingTimeout, currentXid);
+
+				// After the call retuns, resume the local transaction
+				transactionManager.resume(transaction);
+				// Enlist the proxy XA resource with the local transaction so
+				// that
+				// it can propagate the transaction completion events to the
+				// subordinate
+				transaction.enlistResource(originalServer.generateProxyXAResource(nextServerNodeName, dataReturnedFromRemoteServer.getRemoteXidCreated()));
+				// Register a synchronization that can proxy the
+				// beforeCompletion
+				// event to the remote side, after completion events are the
+				// responsibility of the remote server to initiate
+				transaction.registerSynchronization(originalServer.generateProxySynchronization(nextServerNodeName, currentXid));
+
+				// Deference the local copy of the current transaction so the GC
+				// can
+				// free it
+				originalServer.removeRootTransaction(currentXid);
+
+				// Align the local state with the returning state of the
+				// transaction
+				// from the subordinate
+				switch (dataReturnedFromRemoteServer.getTransactionState()) {
+				case Status.STATUS_MARKED_ROLLBACK:
+				case Status.STATUS_ROLLEDBACK:
+				case Status.STATUS_ROLLING_BACK:
+					switch (transaction.getStatus()) {
+					case Status.STATUS_MARKED_ROLLBACK:
+					case Status.STATUS_ROLLEDBACK:
+					case Status.STATUS_ROLLING_BACK:
+						transaction.setRollbackOnly();
+					}
+					break;
+				default:
+					break;
+				}
+			}
+		}
+		// Again - this is business logic in BMT/CMT (interceptors?)
+		{
+			TransactionManager transactionManager = originalServer.getTransactionManager();
+			// Commit the local transaction!
+			// This should propagate to the nodes required!
+			transactionManager.commit();
+		}
+		// Reset the test classloader
+		Thread.currentThread().setContextClassLoader(classLoader);
+	}
+
+	/**
+	 * This work is simulated to be performed in a remote server.
+	 * 
+	 * @param nodesToFlowTo
+	 * @param remainingTimeout
+	 * @param toMigrate
+	 * @return
+	 * @throws RollbackException
+	 * @throws IllegalStateException
+	 * @throws XAException
+	 * @throws SystemException
+	 * @throws NotSupportedException
+	 * @throws IOException
+	 */
+	private DataReturnedFromRemoteServer propagateTransaction(List<String> nodesToFlowTo, int remainingTimeout, Xid toMigrate) throws RollbackException,
+			IllegalStateException, XAException, SystemException, NotSupportedException, IOException {
+		// Do some test setup to initialize this method as it if was being
+		// invoked in a remote server
+		String currentServerName = nodesToFlowTo.remove(0);
+		// Do some work to convert the remote server name to an index against
+		// the cache of local servers - clearly IRL this is not required as we
+		// are at the server :)
+		int index = (Integer.valueOf(currentServerName) / 1000) - 1;
+		LocalServer currentServer = localServers[index];
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(currentServer.getClassLoader());
+
+		// Check if this server has seen this transaction before - this is
+		// crucial to ensure that calling servers will only lay down a proxy if
+		// they are the first visitor to this server.
+		Xid requiresProxyAtPreviousServer = currentServer.locateOrImportTransactionThenResumeIt(remainingTimeout, toMigrate);
+
+		{
+			// Perform work on the migrated transaction
+			TransactionManager transactionManager = currentServer.getTransactionManager();
+			Transaction transaction = transactionManager.getTransaction();
+			// Do some simple work on local dummy resources and synchronizations
+			transaction.registerSynchronization(new TestSynchronization(currentServer.getNodeName()));
+			transaction.enlistResource(new TestResource(currentServer.getNodeName()));
+		}
+
+		// If there are any more nodes to simulate a flow to
+		if (!nodesToFlowTo.isEmpty()) {
+
+			TransactionManager transactionManager = currentServer.getTransactionManager();
+			Transaction transaction = transactionManager.getTransaction();
+			int status = transaction.getStatus();
+
+			// Only propagate active transactions - this may be inactive through
+			// user code (rollback/setRollbackOnly) or it may be inactive due to
+			// the transaction reaper
+			if (status == Status.STATUS_ACTIVE) {
+				// Get the transport specific representation of the remote
+				// server
+				// name
+				String nextServerNodeName = nodesToFlowTo.get(0);
+
+				// Determine the remaining timeout to propagate
+				remainingTimeout = (int) (((TransactionTimeoutConfiguration) transactionManager).getTimeLeftBeforeTransactionTimeout(false) / 1000);
+				// Get the XID to propagate
+				Xid currentXid = currentServer.getCurrentXid();
+				// Suspend the transaction ready for propagation
+				transactionManager.suspend();
+				// Propagate the transaction - in the example I return a boolean
+				// to
+				// indicate whether this caller is the first client to establish
+				// the
+				// subordinate transaction at the remote node
+				DataReturnedFromRemoteServer dataReturnedFromRemoteServer = propagateTransaction(nodesToFlowTo, remainingTimeout, currentXid);
+				// Resume the transaction locally, ready for any more local work
+				// and
+				// to add the proxy resource and sync if needed
+				transactionManager.resume(transaction);
+				// If this caller was the first entity to propagate the
+				// transaction
+				// to the remote server
+				if (dataReturnedFromRemoteServer.getRemoteXidCreated() != null) {
+					// Formally enlist the resource
+					transaction.enlistResource(currentServer.generateProxyXAResource(nextServerNodeName, dataReturnedFromRemoteServer.getRemoteXidCreated()));
+					// Register a sync
+					transaction.registerSynchronization(currentServer.generateProxySynchronization(nextServerNodeName, toMigrate));
+				}
+
+				// Align the local state with the returning state of the
+				// transaction
+				// from the subordinate
+				switch (dataReturnedFromRemoteServer.getTransactionState()) {
+				case Status.STATUS_MARKED_ROLLBACK:
+				case Status.STATUS_ROLLEDBACK:
+				case Status.STATUS_ROLLING_BACK:
+					switch (transaction.getStatus()) {
+					case Status.STATUS_MARKED_ROLLBACK:
+					case Status.STATUS_ROLLEDBACK:
+					case Status.STATUS_ROLLING_BACK:
+						transaction.setRollbackOnly();
+					}
+					break;
+				default:
+					break;
+				}
+			}
+		}
+
+		TransactionManager transactionManager = currentServer.getTransactionManager();
+		int transactionState = transactionManager.getStatus();
+		// SUSPEND THE TRANSACTION WHEN YOU ARE READY TO RETURN TO YOUR CALLER
+		transactionManager.suspend();
+		// Return to the previous caller back over the transport/classloader
+		// boundary in this case
+		Thread.currentThread().setContextClassLoader(classLoader);
+		return new DataReturnedFromRemoteServer(requiresProxyAtPreviousServer, transactionState);
+	}
+
+	/**
+	 * This is the transactional data the transport needs to return from remote
+	 * instances.
+	 */
+	private class DataReturnedFromRemoteServer {
+		private Xid proxyRequired;
+
+		private int transactionState;
+
+		public DataReturnedFromRemoteServer(Xid proxyRequired, int transactionState) {
+			this.proxyRequired = proxyRequired;
+			this.transactionState = transactionState;
+		}
+
+		public Xid getRemoteXidCreated() {
+			return proxyRequired;
+		}
+
+		public int getTransactionState() {
+			return transactionState;
+		}
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestResource.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestResource.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestResource.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+package com.arjuna.jta.distributed.example;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.arjuna.common.Uid;
+
+/**
+ * This is a simple TestResource, any knowledge it has of the rest of the
+ * example is purely for debugging. It should be considered a black box.
+ */
+public class TestResource implements XAResource {
+	private Xid xid;
+
+	protected int timeout = 0;
+
+	private File file;
+
+	private String localServerName;
+
+	public TestResource(String localServerName) {
+		this.localServerName = localServerName;
+	}
+
+	public TestResource(String localServerName, File file) throws IOException {
+		this.localServerName = localServerName;
+		this.file = file;
+		DataInputStream fis = new DataInputStream(new FileInputStream(file));
+		final int formatId = fis.readInt();
+		final int gtrid_length = fis.readInt();
+		final byte[] gtrid = new byte[gtrid_length];
+		fis.read(gtrid, 0, gtrid_length);
+		final int bqual_length = fis.readInt();
+		final byte[] bqual = new byte[bqual_length];
+		fis.read(bqual, 0, bqual_length);
+		this.xid = new Xid() {
+
+			@Override
+			public byte[] getGlobalTransactionId() {
+				return gtrid;
+			}
+
+			@Override
+			public int getFormatId() {
+				return formatId;
+			}
+
+			@Override
+			public byte[] getBranchQualifier() {
+				return bqual;
+			}
+		};
+	}
+
+	public synchronized int prepare(Xid xid) throws XAException {
+		System.out.println("        TestResource (" + localServerName + ")      XA_PREPARE [" + xid + "]");
+
+		File dir = new File(System.getProperty("user.dir") + "/distributedjta-examples/TestResource/" + localServerName + "/");
+		dir.mkdirs();
+		file = new File(dir, new Uid().fileStringForm() + "_");
+		try {
+			file.createNewFile();
+			final int formatId = xid.getFormatId();
+			final byte[] gtrid = xid.getGlobalTransactionId();
+			final int gtrid_length = gtrid.length;
+			final byte[] bqual = xid.getBranchQualifier();
+			final int bqual_length = bqual.length;
+
+			DataOutputStream fos = new DataOutputStream(new FileOutputStream(file));
+			fos.writeInt(formatId);
+			fos.writeInt(gtrid_length);
+			fos.write(gtrid, 0, gtrid_length);
+			fos.writeInt(bqual_length);
+			fos.write(bqual, 0, bqual_length);
+			fos.flush();
+			fos.close();
+		} catch (IOException e) {
+			throw new XAException(XAException.XAER_RMERR);
+		}
+		return XA_OK;
+	}
+
+	public synchronized void commit(Xid id, boolean onePhase) throws XAException {
+		System.out.println("        TestResource (" + localServerName + ")      XA_COMMIT  [" + id + "]");
+		if (file != null) {
+			file.delete();
+		}
+		this.xid = null;
+	}
+
+	public synchronized void rollback(Xid xid) throws XAException {
+		System.out.println("        TestResource (" + localServerName + ")      XA_ROLLBACK[" + xid + "]");
+		if (file != null) {
+			file.delete();
+		}
+		this.xid = null;
+	}
+
+	public void start(Xid xid, int flags) throws XAException {
+		System.out.println("        TestResource (" + localServerName + ")      XA_START   [" + xid + "] Flags=" + flags);
+	}
+
+	public void end(Xid xid, int flags) throws XAException {
+		System.out.println("        TestResource (" + localServerName + ")      XA_END     [" + xid + "] Flags=" + flags);
+	}
+
+	public void forget(Xid xid) throws XAException {
+		System.out.println("        TestResource (" + localServerName + ")      XA_FORGET[" + xid + "]");
+	}
+
+	public int getTransactionTimeout() throws XAException {
+		return (timeout);
+	}
+
+	public boolean isSameRM(XAResource xares) throws XAException {
+		if (xares instanceof TestResource) {
+			TestResource other = (TestResource) xares;
+			if ((this.xid != null && other.xid != null)) {
+				if (this.xid.getFormatId() == other.xid.getFormatId()) {
+					if (Arrays.equals(this.xid.getGlobalTransactionId(), other.xid.getGlobalTransactionId())) {
+						if (Arrays.equals(this.xid.getBranchQualifier(), other.xid.getBranchQualifier())) {
+							return true;
+						}
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	public Xid[] recover(int flag) throws XAException {
+		Xid[] toReturn = null;
+		if ((flag & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN) {
+			System.out.println("        TestResource (" + localServerName + ")      RECOVER[XAResource.TMSTARTRSCAN]: " + localServerName);
+			if (xid != null) {
+				toReturn = new Xid[] { xid };
+			}
+		}
+		if ((flag & XAResource.TMENDRSCAN) == XAResource.TMENDRSCAN) {
+			System.out.println("        TestResource (" + localServerName + ")      RECOVER[XAResource.TMENDRSCAN]: " + localServerName);
+		}
+		if (flag == XAResource.TMNOFLAGS) {
+			System.out.println("        TestResource (" + localServerName + ")      RECOVER[XAResource.TMENDRSCAN]: " + localServerName);
+		}
+		return toReturn;
+	}
+
+	public boolean setTransactionTimeout(int seconds) throws XAException {
+		timeout = seconds;
+		return (true);
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestResourceRecovery.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestResourceRecovery.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestResourceRecovery.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,60 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.transaction.xa.XAResource;
+
+import org.jboss.tm.XAResourceRecovery;
+
+/**
+ * This is a simple TestResource XAResourceRecovery helper, any knowledge it has
+ * of the rest of the example is purely for debugging. It should be considered a
+ * black box.
+ */
+public class TestResourceRecovery implements XAResourceRecovery {
+
+	private List<TestResource> resources = new ArrayList<TestResource>();
+
+	public TestResourceRecovery(String nodeName) throws IOException {
+		File file = new File(System.getProperty("user.dir") + "/distributedjta-examples/TestResource/" + nodeName + "/");
+		if (file.exists() && file.isDirectory()) {
+			File[] listFiles = file.listFiles();
+			for (int i = 0; i < listFiles.length; i++) {
+				File currentFile = listFiles[i];
+				if (currentFile.getAbsolutePath().endsWith("_")) {
+					resources.add(new TestResource(nodeName, currentFile));
+				}
+			}
+		}
+	}
+
+	@Override
+	public XAResource[] getXAResources() {
+		return resources.toArray(new XAResource[] {});
+	}
+
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestSynchronization.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestSynchronization.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/TestSynchronization.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,47 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example;
+
+import javax.transaction.Synchronization;
+
+/**
+ * This is a simple Synchronization, any knowledge (such as the server name) it
+ * has of the rest of the example is purely for debugging. It should be
+ * considered a black box.
+ */
+public class TestSynchronization implements Synchronization {
+	private String localServerName;
+
+	public TestSynchronization(String localServerName) {
+		this.localServerName = localServerName;
+	}
+
+	@Override
+	public void beforeCompletion() {
+		System.out.println(" TestSynchronization (" + localServerName + ")      beforeCompletion");
+	}
+
+	@Override
+	public void afterCompletion(int status) {
+		System.out.println(" TestSynchronization (" + localServerName + ")      afterCompletion");
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/IsolatableServersClassLoader.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/IsolatableServersClassLoader.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/IsolatableServersClassLoader.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,120 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import sun.misc.Resource;
+import sun.misc.URLClassPath;
+
+/**
+ * This classloader will reload copies of classes (except a package that is
+ * configured for ignoring - the interfaces that the root example requires
+ * basically).
+ */
+public class IsolatableServersClassLoader extends ClassLoader {
+	private Map<String, Class<?>> clazzMap = new HashMap<String, Class<?>>();
+	private URLClassPath ucp;
+	private String ignoredPackage;
+	private String includedPackage;
+	private String otherIgnoredPackage;
+
+	/**
+	 * Create the classloader.
+	 * 
+	 * @param ignoredPackage
+	 *            This package will be ignored by this classloader and delegated
+	 *            to its parent so that the example testcase can access required
+	 *            interfaces of its test.
+	 * @param parent
+	 * @throws SecurityException
+	 * @throws NoSuchMethodException
+	 * @throws MalformedURLException
+	 */
+
+	public IsolatableServersClassLoader(String includedPackage, String ignoredPackage, ClassLoader parent) throws SecurityException, NoSuchMethodException,
+			MalformedURLException {
+		super(parent);
+		this.includedPackage = includedPackage;
+		this.otherIgnoredPackage = ignoredPackage;
+		this.ignoredPackage = IsolatableServersClassLoader.class.getPackage().getName();
+		String property = System.getProperty("java.class.path");
+		String[] split = property.split(System.getProperty("path.separator"));
+		URL[] urls = new URL[split.length];
+		for (int i = 0; i < urls.length; i++) {
+			String url = split[i];
+			if (url.endsWith(".jar")) {
+				urls[i] = new URL("jar:file:" + url + "!/");
+			} else {
+				urls[i] = new URL("file:" + url + "/");
+			}
+		}
+		this.ucp = new URLClassPath(urls);
+	}
+
+	@Override
+	protected Class<?> findClass(String name) throws ClassNotFoundException {
+		if (clazzMap.containsKey(name)) {
+			return clazzMap.get(name);
+		}
+		return super.findClass(name);
+	}
+
+	public Class<?> loadClass(String name) throws ClassNotFoundException {
+		if (!name.matches(ignoredPackage + ".[A-Za-z0-9]*") && otherIgnoredPackage != null && name.startsWith(otherIgnoredPackage)) {
+			throw new ClassNotFoundException(name);
+		}
+		Class<?> clazz = null;
+		if (clazzMap.containsKey(name)) {
+			clazz = clazzMap.get(name);
+		}
+
+		if (clazz != null) {
+			System.err.println("Already loaded: " + name);
+		} else {
+			if (!name.startsWith("com.arjuna") || name.matches(ignoredPackage + ".[A-Za-z0-9]*")
+					|| (includedPackage != null && !name.startsWith(includedPackage))) {
+				clazz = getParent().loadClass(name);
+			} else {
+
+				String path = name.replace('.', '/').concat(".class");
+				Resource res = ucp.getResource(path, false);
+				if (res == null) {
+					throw new ClassNotFoundException(name);
+				}
+				try {
+					byte[] classData = res.getBytes();
+					clazz = defineClass(name, classData, 0, classData.length);
+					clazzMap.put(name, clazz);
+				} catch (IOException e) {
+					throw new ClassNotFoundException(name, e);
+				}
+			}
+
+		}
+		return clazz;
+	}
+}
\ No newline at end of file

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/LocalServer.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/LocalServer.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/LocalServer.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,177 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server;
+
+import java.io.IOException;
+
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
+
+/**
+ * This is the local interface of the server, operations invoked here should be
+ * considered to be called on local objects. The are sat behind this interface
+ * though in order to allow multiple copies of a server to be loaded into
+ * memory.
+ */
+public interface LocalServer {
+
+	/**
+	 * Initialize this server, this will create a transaction manager service
+	 * and a recovery manager service.
+	 * 
+	 * @param classLoaderForTransactionManager
+	 *            This is the classloader that the transaction manager would
+	 *            normally have access to.
+	 * 
+	 * @throws CoreEnvironmentBeanException
+	 * @throws IOException
+	 * @throws SecurityException
+	 * @throws NoSuchFieldException
+	 * @throws IllegalArgumentException
+	 * @throws IllegalAccessException
+	 */
+	public void initialise(LookupProvider lookupProvider, String nodeName, int portOffset, String[] clusterCompatriots,
+			ClassLoader classLoaderForTransactionManager) throws CoreEnvironmentBeanException, IOException, SecurityException, NoSuchFieldException,
+			IllegalArgumentException, IllegalAccessException;
+
+	/**
+	 * Get the local transaction managers node name.
+	 */
+	public String getNodeName();
+
+	/**
+	 * Get a reference on the local transaction manager.
+	 * 
+	 * @return
+	 * @throws NotSupportedException
+	 * @throws SystemException
+	 */
+	public TransactionManager getTransactionManager() throws NotSupportedException, SystemException;
+
+	/**
+	 * Store the current transaction, this is so if a subordinate comes back
+	 * here we have a hashmap to locate the transaction in.
+	 * 
+	 * Clearly servers where the transaction has been inflowed back to *must
+	 * not* commit the transaction.
+	 * 
+	 * NOTE: CMT would not allow you do this anyway
+	 * 
+	 * @throws SystemException
+	 */
+	public void storeRootTransaction() throws SystemException;
+
+	/**
+	 * Remove the parent transaction from the local cache. It is indexed on XID.
+	 * 
+	 * @param toMigrate
+	 */
+	public void removeRootTransaction(Xid toMigrate);
+
+	/**
+	 * Either create or locate a subordinate (or root) transaction for a given
+	 * Xid.
+	 * 
+	 * If it is the root transaction, it must not be committed!
+	 * 
+	 * NOTE: CMT would not allow you do this anyway
+	 * 
+	 * e.g. A transaction flowed 1,2,1 **must not** be committed at the third
+	 * stage of the flow even though we are back at the originating server!!!
+	 * 
+	 * When a transaction is propagated to a server the transport is responsible
+	 * for detecting that the server has not participated in the transaction yet
+	 * and if so it must assign it the next available subordinate name and
+	 * persist this information to help with recovery (see ServerImpl.java and
+	 * the test itself for how to determine the next available subordinate name
+	 * and a potential method of persisting this data). This is important when a
+	 * proxy xa resource is involved in recovery and invokes commit or rollback
+	 * as the transaction must be reloaded by the remote server before the
+	 * commit/rollback – if it was prepared - before we attempt to complete the
+	 * transaction.
+	 * 
+	 * @param remainingTimeout
+	 * @param toImport
+	 * @return
+	 * @throws XAException
+	 * @throws InvalidTransactionException
+	 * @throws IllegalStateException
+	 * @throws SystemException
+	 * @throws IOException
+	 */
+	public Xid locateOrImportTransactionThenResumeIt(int remainingTimeout, Xid toImport) throws XAException, InvalidTransactionException,
+			IllegalStateException, SystemException, IOException;
+
+	/**
+	 * Transport specific function to generate a proxy for a remote server.
+	 * 
+	 * @param remoteServerName
+	 * 
+	 * @return
+	 * @throws IOException
+	 * @throws SystemException
+	 */
+	public XAResource generateProxyXAResource(String remoteServerName, Xid migratedTransaction) throws SystemException;
+
+	/**
+	 * Generate a proxy synchronization
+	 * 
+	 * @param remoteServerName
+	 * @param toRegisterAgainst
+	 * 
+	 * @return
+	 */
+	public Synchronization generateProxySynchronization(String remoteServerName, Xid toRegisterAgainst);
+
+	/**
+	 * Get the current Xid - this is what will be propagated to the remote
+	 * servers.
+	 * 
+	 * @return
+	 * @throws SystemException
+	 */
+	public Xid getCurrentXid() throws SystemException;
+
+	/**
+	 * Test code to create a reference of this server as a remote endpoint for
+	 * other servers to communicate with.
+	 * 
+	 * @return
+	 */
+	public RemoteServer connectTo();
+
+	/**
+	 * This is used by the test to ensure that the servers classloader is set on
+	 * a thread.
+	 * 
+	 * @return
+	 */
+	public ClassLoader getClassLoader();
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/LookupProvider.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/LookupProvider.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/LookupProvider.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,57 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server;
+
+/**
+ * Simulates a JNDI environment. Should be considered a black box.
+ */
+public class LookupProvider {
+	private static LookupProvider instance;
+
+	private RemoteServer[] remoteServers = new RemoteServer[3];
+
+	public static LookupProvider getInstance() {
+		if (instance == null) {
+			instance = new LookupProvider();
+		}
+		return instance;
+	}
+
+	protected LookupProvider() {
+	}
+
+	public RemoteServer lookup(String jndiName) {
+		int index = (Integer.valueOf(jndiName) / 1000) - 1;
+		return remoteServers[index];
+	}
+
+	public void clear() {
+		for (int i = 0; i < remoteServers.length; i++) {
+			// Disconnect
+			remoteServers[i] = null;
+		}
+	}
+
+	public void bind(int index, RemoteServer connectTo) {
+		remoteServers[index] = connectTo;
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/RemoteServer.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/RemoteServer.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/RemoteServer.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,96 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server;
+
+import javax.transaction.SystemException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+/**
+ * This interface attempts to illustrate the network endpoint requirements of
+ * the remote transaction server with regards subordinate transactions.
+ * 
+ * <p>
+ * Many of the methods are required to provide a recover parameter, this is
+ * because when using Serializable ProxyXAResources, the recover method is not
+ * invoked and therfore the remote server will not have had chance to recover
+ * the subordinate transactions.
+ */
+public interface RemoteServer {
+
+	/**
+	 * Prepare the subordinate transaction
+	 * 
+	 * @param xid
+	 * @param recover
+	 * @return
+	 * @throws XAException
+	 */
+	public int prepare(Xid xid, boolean recover) throws XAException;
+
+	/**
+	 * Commit the subordinate transaction.
+	 * 
+	 * @param xid
+	 * @param onePhase
+	 * @param recover
+	 * @throws XAException
+	 */
+	public void commit(Xid xid, boolean onePhase, boolean recover) throws XAException;
+
+	/**
+	 * Rollback the subordinate transaction.
+	 * 
+	 * @param xid
+	 * @param recover
+	 * @throws XAException
+	 */
+	public void rollback(Xid xid, boolean recover) throws XAException;
+
+	/**
+	 * Forget a subordinate transaction.
+	 * 
+	 * @param xid
+	 * @param recover
+	 * @throws XAException
+	 */
+	public void forget(Xid xid, boolean recover) throws XAException;
+
+	/**
+	 * Proxy synchronizations will need to invoke this.
+	 * 
+	 * @param xid
+	 * @throws SystemException
+	 */
+	public void beforeCompletion(Xid xid) throws SystemException;
+
+	/**
+	 * This is used by the ProxyXAResourceRecovery helper class to detect
+	 * orphaned subordinate transactions.
+	 * 
+	 * @param localServerName
+	 * @return
+	 * @throws XAException
+	 */
+	public Xid[] recoverFor(String localServerName) throws XAException;
+
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxySynchronization.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxySynchronization.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxySynchronization.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server.impl;
+
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.jta.distributed.example.server.LookupProvider;
+
+/**
+ * An example to show how the transport can register a proxy synchronization.
+ * 
+ * <p>
+ * Note that we do not proxy the afterCompletion, this is left to run locally
+ * per subordinate.
+ */
+public class ProxySynchronization implements Synchronization {
+
+	private String localServerName;
+	private String remoteServerName;
+	private Xid toRegisterAgainst;
+
+	public ProxySynchronization(String localServerName, String remoteServerName, Xid toRegisterAgainst) {
+		this.localServerName = localServerName;
+		this.remoteServerName = remoteServerName;
+		this.toRegisterAgainst = toRegisterAgainst;
+	}
+
+	/**
+	 * Propagate the before completion in a transport specific manner.
+	 */
+	@Override
+	public void beforeCompletion() {
+		System.out.println("ProxySynchronization (" + localServerName + ":" + remoteServerName + ") beforeCompletion");
+		try {
+			LookupProvider.getInstance().lookup(remoteServerName).beforeCompletion(toRegisterAgainst);
+		} catch (SystemException e) {
+			// Unfortunately we cannot do much else here
+			e.printStackTrace();
+		}
+	}
+
+	@Override
+	public void afterCompletion(int status) {
+		// These are not proxied but are handled during local commits
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResource.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResource.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResource.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,221 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server.impl;
+
+import java.io.Serializable;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.jboss.tm.XAResourceWrapper;
+
+import com.arjuna.jta.distributed.example.server.LookupProvider;
+
+/**
+ * I chose for this class to implement XAResourceWrapper so that I can provide a
+ * name to the Transaction manager for it to store in its XID.
+ * 
+ * <p>
+ * In the normal situation, a ProxyXAResource is Serialized, therefore we do not
+ * get the chance to recover the transactions in a call to
+ * XAResource::recover(), therefore the ProxyXAResource must tell the remote
+ * side when it calls each method, whether or not to attempt to recover the
+ * transaction before invoking its transactional directive, this may be seen in
+ * the prepare, commit, rollback and forget method.
+ */
+public class ProxyXAResource implements XAResource, XAResourceWrapper, Serializable {
+
+	private int transactionTimeout;
+	private String remoteServerName;
+	private String localServerName;
+	private transient boolean nonerecovered;
+
+	private Xid migratedXid;
+
+	/**
+	 * Create a new proxy to the remote server.
+	 * 
+	 * @param LookupProvider
+	 *            .getLookupProvider()
+	 * @param localServerName
+	 * @param remoteServerName
+	 */
+	public ProxyXAResource(String localServerName, String remoteServerName, Xid migratedXid) {
+		this.localServerName = localServerName;
+		this.remoteServerName = remoteServerName;
+		this.migratedXid = migratedXid;
+		this.nonerecovered = true;
+	}
+
+	/**
+	 * Constructor for fallback bottom up recovery.
+	 * 
+	 * @param localServerName
+	 * @param remoteServerName
+	 */
+	public ProxyXAResource(String localServerName, String remoteServerName) {
+		this.localServerName = localServerName;
+		this.remoteServerName = remoteServerName;
+	}
+
+	/**
+	 * Store the XID.
+	 */
+	@Override
+	public void start(Xid xid, int flags) throws XAException {
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_START   [" + xid + "]");
+	}
+
+	/**
+	 * Reference the XID.
+	 */
+	@Override
+	public void end(Xid xid, int flags) throws XAException {
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_END     [" + xid + "]");
+	}
+
+	/**
+	 * This propagates the transaction directive in a transport specific manner.
+	 */
+	@Override
+	public int prepare(Xid xid) throws XAException {
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_PREPARE [" + xid + "]");
+
+		Xid toPropagate = migratedXid != null ? migratedXid : xid;
+		int propagatePrepare = LookupProvider.getInstance().lookup(remoteServerName).prepare(toPropagate, !nonerecovered);
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_PREPARED");
+		return propagatePrepare;
+	}
+
+	/**
+	 * This propagates the transaction directive in a transport specific manner.
+	 */
+	@Override
+	public void commit(Xid xid, boolean onePhase) throws XAException {
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_COMMIT  [" + xid + "]");
+
+		Xid toPropagate = migratedXid != null ? migratedXid : xid;
+		LookupProvider.getInstance().lookup(remoteServerName).commit(toPropagate, onePhase, !nonerecovered);
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_COMMITED");
+	}
+
+	/**
+	 * This propagates the transaction directive in a transport specific manner.
+	 */
+	@Override
+	public void rollback(Xid xid) throws XAException {
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_ROLLBACK[" + xid + "]");
+
+		Xid toPropagate = migratedXid != null ? migratedXid : xid;
+		LookupProvider.getInstance().lookup(remoteServerName).rollback(toPropagate, !nonerecovered);
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_ROLLBACKED");
+	}
+
+	/**
+	 * This method is used when the local side is attempting to detect
+	 * subordinate transactions that prepared remotely but the local
+	 * ProxyXAResource has not prepared and are therefore orphaned.
+	 */
+	@Override
+	public Xid[] recover(int flag) throws XAException {
+		if ((flag & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN) {
+			System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_RECOVER [XAResource.TMSTARTRSCAN]");
+		}
+		if ((flag & XAResource.TMENDRSCAN) == XAResource.TMENDRSCAN) {
+			System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_RECOVER [XAResource.TMENDRSCAN]");
+		}
+
+		Xid[] toReturn = LookupProvider.getInstance().lookup(remoteServerName).recoverFor(localServerName);
+
+		if (toReturn != null) {
+			for (int i = 0; i < toReturn.length; i++) {
+				System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_RECOVERD: " + toReturn[i]);
+			}
+		}
+		return toReturn;
+	}
+
+	@Override
+	public void forget(Xid xid) throws XAException {
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_FORGET  [" + xid + "]");
+
+		Xid toPropagate = migratedXid != null ? migratedXid : xid;
+		LookupProvider.getInstance().lookup(remoteServerName).forget(toPropagate, !nonerecovered);
+		System.out.println("     ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_FORGETED[" + xid + "]");
+	}
+
+	@Override
+	public int getTransactionTimeout() throws XAException {
+		return transactionTimeout;
+	}
+
+	@Override
+	public boolean setTransactionTimeout(int seconds) throws XAException {
+		this.transactionTimeout = seconds;
+		return true;
+	}
+
+	@Override
+	public boolean isSameRM(XAResource xares) throws XAException {
+		boolean toReturn = false;
+		if (xares instanceof ProxyXAResource) {
+			if (((ProxyXAResource) xares).remoteServerName == remoteServerName) {
+				toReturn = true;
+			}
+		}
+		return toReturn;
+	}
+
+	/**
+	 * Not used by the TM.
+	 */
+	@Override
+	public XAResource getResource() {
+		return null;
+	}
+
+	/**
+	 * Not used by the TM.
+	 */
+	@Override
+	public String getProductName() {
+		return null;
+	}
+
+	/**
+	 * Not used by the TM.
+	 */
+	@Override
+	public String getProductVersion() {
+		return null;
+	}
+
+	/**
+	 * This allows the proxy to contain meaningful information in the XID in
+	 * case of failure to recover.
+	 */
+	@Override
+	public String getJndiName() {
+		return "ProxyXAResource: " + localServerName + " " + remoteServerName;
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResourceDeserializer.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResourceDeserializer.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResourceDeserializer.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,32 @@
+package com.arjuna.jta.distributed.example.server.impl;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+import javax.transaction.xa.XAResource;
+
+import com.arjuna.ats.jta.recovery.SerializableXAResourceDeserializer;
+
+/**
+ * This is an additional recovery helper that allows a transport to provide a
+ * deserializer for its ProxyXAResource. We need this as otherwise the
+ * transaction manager would not be able to see the transports classes. Check
+ * out the Javadocs on {@link SerializableXAResourceDeserializer}
+ */
+public class ProxyXAResourceDeserializer implements SerializableXAResourceDeserializer {
+
+	@Override
+	public boolean canDeserialze(String className) {
+		if (className.equals(ProxyXAResource.class.getName())) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	@Override
+	public XAResource deserialze(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+		return (XAResource) ois.readObject();
+	}
+
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResourceRecovery.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResourceRecovery.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ProxyXAResourceRecovery.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.transaction.xa.XAResource;
+
+import org.jboss.tm.XAResourceRecovery;
+
+/**
+ * This class is required by the transaction manager so that it can query the
+ * list of running subordinate transactions which have it as a parent. Typically
+ * we will know about these via the <code>Serializable</code>
+ * <code>ProxyXAResource</code> class. However scenarios exist whereby the
+ * subordinate has a prepared transaction and failed to return to the parent or
+ * the parent failed itself. In this case we need to query these XIDs from the
+ * remote server to detect these orphans.
+ */
+public class ProxyXAResourceRecovery implements XAResourceRecovery {
+
+	private List<ProxyXAResource> resources = new ArrayList<ProxyXAResource>();
+
+	public ProxyXAResourceRecovery(String nodeName, String[] toRecoverFor) {
+		for (int i = 0; i < toRecoverFor.length; i++) {
+			resources.add(new ProxyXAResource(nodeName, toRecoverFor[i]));
+		}
+	}
+
+	@Override
+	public XAResource[] getXAResources() {
+		return resources.toArray(new XAResource[] {});
+	}
+
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/RemoteServerImpl.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/RemoteServerImpl.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/RemoteServerImpl.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,173 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server.impl;
+
+import java.util.Arrays;
+import java.util.Set;
+
+import javax.transaction.SystemException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.SubordinationManager;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.TransactionImporterImple;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.XATerminatorImple;
+import com.arjuna.jta.distributed.example.server.RemoteServer;
+
+/**
+ * This class could translate quite easily to a server-side network endpoint
+ * interceptor for the client-side ProxyXAResource. One change that is required
+ * is to remove the classloader behavior as that is purely to allow the test to
+ * run multiple servers within a single VM. When reading this class, tend to
+ * ignore the classloader work as that is test scaffolding.
+ * 
+ * <p>
+ * In the normal situation, a ProxyXAResource is Serialized, therefore we do not
+ * get the chance to recover the transactions in a call to
+ * XAResource::recover(), therefore the ProxyXAResource must tell us when it
+ * calls each method, whether or not to attempt to recover the transaction
+ * before invoking its transactional directive.
+ */
+public class RemoteServerImpl implements RemoteServer {
+	/**
+	 * Remember to ignore the classloader shenanigans when reading the method.
+	 * 
+	 * @param recover
+	 *            Should be set by the clients ProxyXAResource when the client
+	 *            knows the remote side needs the transaction loading.
+	 */
+	@Override
+	public int prepare(Xid xid, boolean recover) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			if (recover) {
+				((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(xid, null);
+			}
+			return SubordinationManager.getXATerminator().prepare(xid);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	/**
+	 * Remember to ignore the classloader shenanigans when reading the method.
+	 * 
+	 * @param recover
+	 *            Should be set by the clients ProxyXAResource when the client
+	 *            knows the remote side needs the transaction loading.
+	 */
+	@Override
+	public void commit(Xid xid, boolean onePhase, boolean recover) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			if (recover) {
+				((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(xid, null);
+			}
+			SubordinationManager.getXATerminator().commit(xid, onePhase);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	/**
+	 * Remember to ignore the classloader shenanigans when reading the method.
+	 * 
+	 * @param recover
+	 *            Should be set by the clients ProxyXAResource when the client
+	 *            knows the remote side needs the transaction loading.
+	 */
+	@Override
+	public void rollback(Xid xid, boolean recover) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			if (recover) {
+				((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(xid, null);
+			}
+			SubordinationManager.getXATerminator().rollback(xid);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	/**
+	 * Remember to ignore the classloader shenanigans when reading the method.
+	 * 
+	 * @param recover
+	 *            Should be set by the clients ProxyXAResource when the client
+	 *            knows the remote side needs the transaction loading.
+	 */
+	@Override
+	public void forget(Xid xid, boolean recover) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			if (recover) {
+				((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(xid, null);
+			}
+			SubordinationManager.getXATerminator().forget(xid);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+
+	}
+
+	/**
+	 * Remember to ignore the classloader shenanigans when reading the method.
+	 * 
+	 * @param recover
+	 *            Should be set by the clients ProxyXAResource when the client
+	 *            knows the remote side needs the transaction loading.
+	 * @throws SystemException
+	 */
+	@Override
+	public void beforeCompletion(Xid xid) throws SystemException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			((XATerminatorImple) SubordinationManager.getXATerminator()).beforeCompletion(xid);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	/**
+	 * Remember to ignore the classloader shenanigans when reading the method.
+	 */
+	@Override
+	public Xid[] recoverFor(String localServerName) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			Set<Xid> toReturn = ((TransactionImporterImple) SubordinationManager.getTransactionImporter()).getInflightXids(localServerName);
+			Xid[] doRecover = ((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(null, localServerName);
+			if (doRecover != null) {
+				toReturn.addAll(Arrays.asList(doRecover));
+			}
+			return toReturn.toArray(new Xid[0]);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ServerImpl.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ServerImpl.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/examples/classes/com/arjuna/jta/distributed/example/server/impl/ServerImpl.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,265 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.jta.distributed.example.server.impl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+import org.jboss.tm.XAResourceRecovery;
+
+import com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean;
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBean;
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
+import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean;
+import com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+import com.arjuna.ats.arjuna.tools.osb.mbean.ObjStoreBrowser;
+import com.arjuna.ats.internal.arjuna.utils.ManualProcessId;
+import com.arjuna.ats.internal.jbossatx.jta.XAResourceRecordWrappingPluginImpl;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.SubordinateXidImple;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.SubordinationManager;
+import com.arjuna.ats.jbossatx.jta.RecoveryManagerService;
+import com.arjuna.ats.jbossatx.jta.TransactionManagerService;
+import com.arjuna.ats.jta.common.JTAEnvironmentBean;
+import com.arjuna.jta.distributed.example.TestResourceRecovery;
+import com.arjuna.jta.distributed.example.server.LocalServer;
+import com.arjuna.jta.distributed.example.server.LookupProvider;
+import com.arjuna.jta.distributed.example.server.RemoteServer;
+
+/**
+ * Most of this class is self explanatory, the main part to take note of is how
+ * <code>locateOrImportTransactionThenResumeIt</code>,
+ * <code>storeRootTransaction</code> and <code>remoteRootTransaction</code>
+ * interact with each other.
+ * 
+ * <p>
+ * It is the responsibility of the root transaction manager to cache the root
+ * transaction in a manner that incoming subordinates may be able to resume the
+ * root transaction at that node, rather than creating a subordinate which would
+ * be inefficient.
+ */
+public class ServerImpl implements LocalServer {
+
+	private String nodeName;
+	private RecoveryManagerService recoveryManagerService;
+	private TransactionManagerService transactionManagerService;
+	private Map<SubordinateXidImple, TransactionImple> rootTransactionsAsSubordinate = new HashMap<SubordinateXidImple, TransactionImple>();
+	private RecoveryManager _recoveryManager;
+	private ClassLoader classLoaderForTransactionManager;
+
+	/**
+	 * This is typically done by the application server.
+	 * 
+	 * The addition required for the distributed JTA code is:
+	 * RecoveryManagerService::addSerializableXAResourceDeserializer()
+	 * 
+	 * You must also register with RecoveryManagerService::addXAResourceRecovery
+	 * an {@link XAResourceRecovery} for your Proxy XA Resources so that they
+	 * can find orphan subordinate transactions.
+	 */
+	public void initialise(LookupProvider lookupProvider, String nodeName, int portOffset, String[] clusterBuddies, ClassLoader classLoaderForTransactionManager)
+			throws CoreEnvironmentBeanException, IOException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
+		this.nodeName = nodeName;
+		this.classLoaderForTransactionManager = classLoaderForTransactionManager;
+
+		RecoveryEnvironmentBean recoveryEnvironmentBean = com.arjuna.ats.arjuna.common.recoveryPropertyManager.getRecoveryEnvironmentBean();
+		recoveryEnvironmentBean.setRecoveryBackoffPeriod(1);
+
+		recoveryEnvironmentBean.setRecoveryInetAddress(InetAddress.getByName("localhost"));
+		recoveryEnvironmentBean.setRecoveryPort(4712 + portOffset);
+		recoveryEnvironmentBean.setTransactionStatusManagerInetAddress(InetAddress.getByName("localhost"));
+		recoveryEnvironmentBean.setTransactionStatusManagerPort(4713 + portOffset);
+		List<String> recoveryModuleClassNames = new ArrayList<String>();
+
+		recoveryModuleClassNames.add("com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule");
+		// recoveryModuleClassNames.add("com.arjuna.ats.internal.txoj.recovery.TORecoveryModule");
+		recoveryModuleClassNames.add("com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule");
+		recoveryEnvironmentBean.setRecoveryModuleClassNames(recoveryModuleClassNames);
+		List<String> expiryScannerClassNames = new ArrayList<String>();
+		expiryScannerClassNames.add("com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner");
+		recoveryEnvironmentBean.setExpiryScannerClassNames(expiryScannerClassNames);
+		recoveryEnvironmentBean.setRecoveryActivators(null);
+
+		CoreEnvironmentBean coreEnvironmentBean = com.arjuna.ats.arjuna.common.arjPropertyManager.getCoreEnvironmentBean();
+		// coreEnvironmentBean.setSocketProcessIdPort(4714 + nodeName);
+		coreEnvironmentBean.setNodeIdentifier(nodeName);
+		// coreEnvironmentBean.setSocketProcessIdMaxPorts(1);
+		coreEnvironmentBean.setProcessImplementationClassName(ManualProcessId.class.getName());
+		coreEnvironmentBean.setPid(portOffset);
+
+		CoordinatorEnvironmentBean coordinatorEnvironmentBean = com.arjuna.ats.arjuna.common.arjPropertyManager.getCoordinatorEnvironmentBean();
+		coordinatorEnvironmentBean.setEnableStatistics(false);
+		coordinatorEnvironmentBean.setDefaultTimeout(300);
+		coordinatorEnvironmentBean.setTransactionStatusManagerEnable(false);
+		coordinatorEnvironmentBean.setDefaultTimeout(0);
+
+		ObjectStoreEnvironmentBean actionStoreObjectStoreEnvironmentBean = com.arjuna.common.internal.util.propertyservice.BeanPopulator.getNamedInstance(
+				com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.class, "default");
+		actionStoreObjectStoreEnvironmentBean.setObjectStoreDir(System.getProperty("user.dir") + "/distributedjta-examples/tx-object-store/" + nodeName);
+
+		ObjectStoreEnvironmentBean stateStoreObjectStoreEnvironmentBean = com.arjuna.common.internal.util.propertyservice.BeanPopulator.getNamedInstance(
+				com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.class, "stateStore");
+		stateStoreObjectStoreEnvironmentBean.setObjectStoreDir(System.getProperty("user.dir") + "/distributedjta-examples/tx-object-store/" + nodeName);
+
+		ObjectStoreEnvironmentBean communicationStoreObjectStoreEnvironmentBean = com.arjuna.common.internal.util.propertyservice.BeanPopulator
+				.getNamedInstance(com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.class, "communicationStore");
+		communicationStoreObjectStoreEnvironmentBean.setObjectStoreDir(System.getProperty("user.dir") + "/distributedjta-examples/tx-object-store/" + nodeName);
+
+		JTAEnvironmentBean jTAEnvironmentBean = com.arjuna.ats.jta.common.jtaPropertyManager.getJTAEnvironmentBean();
+		jTAEnvironmentBean.setLastResourceOptimisationInterface(org.jboss.tm.LastResource.class);
+		jTAEnvironmentBean.setTransactionManagerClassName("com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate");
+		jTAEnvironmentBean.setUserTransactionClassName("com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple");
+		jTAEnvironmentBean
+				.setTransactionSynchronizationRegistryClassName("com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple");
+		List<String> xaRecoveryNodes = new ArrayList<String>();
+		xaRecoveryNodes.add(nodeName);
+		jTAEnvironmentBean.setXaRecoveryNodes(xaRecoveryNodes);
+
+		List<String> xaResourceOrphanFilterClassNames = new ArrayList<String>();
+
+		xaResourceOrphanFilterClassNames.add("com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter");
+		xaResourceOrphanFilterClassNames.add("com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter");
+		xaResourceOrphanFilterClassNames.add("com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinateJTAXAResourceOrphanFilter");
+		jTAEnvironmentBean.setXaResourceOrphanFilterClassNames(xaResourceOrphanFilterClassNames);
+		jTAEnvironmentBean.setXAResourceRecordWrappingPlugin(new XAResourceRecordWrappingPluginImpl());
+
+		recoveryManagerService = new RecoveryManagerService();
+		recoveryManagerService.create();
+		recoveryManagerService.addXAResourceRecovery(new TestResourceRecovery(nodeName));
+		// This MUST be the last XAResourceRecovery class registered or you will
+		// get unexpected recovery results, could add a specific interface for
+		// this?
+		recoveryManagerService.addXAResourceRecovery(new ProxyXAResourceRecovery(nodeName, clusterBuddies));
+		recoveryManagerService.addSerializableXAResourceDeserializer(new ProxyXAResourceDeserializer());
+
+		// recoveryManagerService.start();
+		_recoveryManager = RecoveryManager.manager();
+		RecoveryManager.manager().initialize();
+
+		transactionManagerService = new TransactionManagerService();
+		TxControl txControl = new com.arjuna.ats.arjuna.coordinator.TxControl();
+		transactionManagerService.setJbossXATerminator(new com.arjuna.ats.internal.jbossatx.jta.jca.XATerminator());
+		transactionManagerService
+				.setTransactionSynchronizationRegistry(new com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple());
+		transactionManagerService.create();
+	}
+
+	@Override
+	public ClassLoader getClassLoader() {
+		return classLoaderForTransactionManager;
+	}
+
+	@Override
+	public RemoteServer connectTo() {
+		return new RemoteServerImpl();
+	}
+
+	@Override
+	public String getNodeName() {
+		return nodeName;
+	}
+
+	@Override
+	public TransactionManager getTransactionManager() {
+		return transactionManagerService.getTransactionManager();
+	}
+
+	@Override
+	public Xid getCurrentXid() throws SystemException {
+		TransactionImple transaction = ((TransactionImple) transactionManagerService.getTransactionManager().getTransaction());
+		return transaction.getTxId();
+	}
+
+	/**
+	 * This factory method is provided purely for the test purposes to ensure
+	 * the correct classloader is used.
+	 */
+	@Override
+	public ProxyXAResource generateProxyXAResource(String remoteServerName, Xid migratedXid) throws SystemException {
+		return new ProxyXAResource(nodeName, remoteServerName, migratedXid);
+	}
+
+	/**
+	 * This factory method is provided purely for the test purposes to ensure
+	 * the correct classloader is used.
+	 */
+	@Override
+	public Synchronization generateProxySynchronization(String remoteServerName, Xid toRegisterAgainst) {
+		return new ProxySynchronization(nodeName, remoteServerName, toRegisterAgainst);
+	}
+
+	/**
+	 * This method first checks a local <code>Map</code> to ensure that if the
+	 * server being flowed to is actually where the root transaction resides
+	 * then that transaction is resumed, rather than a subordinate created.
+	 */
+	@Override
+	public Xid locateOrImportTransactionThenResumeIt(int remainingTimeout, Xid toResume) throws XAException, IllegalStateException, SystemException,
+			InvalidTransactionException {
+		Xid toReturn = null;
+		Transaction transaction = rootTransactionsAsSubordinate.get(new SubordinateXidImple(toResume));
+		if (transaction == null) {
+			transaction = SubordinationManager.getTransactionImporter().getImportedTransaction(toResume);
+			if (transaction == null) {
+				transaction = SubordinationManager.getTransactionImporter().importTransaction(toResume, remainingTimeout);
+				toReturn = ((TransactionImple) transaction).getTxId();
+			}
+		}
+		transactionManagerService.getTransactionManager().resume(transaction);
+		return toReturn;
+	}
+
+	/**
+	 * Cache the root transaction, this is important if the transaction flows
+	 * back to this node, then we want to associate resources with the root
+	 * transaction, rather than a subordinate.
+	 */
+	@Override
+	public void storeRootTransaction() throws SystemException {
+		TransactionImple transaction = ((TransactionImple) transactionManagerService.getTransactionManager().getTransaction());
+		Xid txId = transaction.getTxId();
+		rootTransactionsAsSubordinate.put(new SubordinateXidImple(txId), transaction);
+	}
+
+	/**
+	 * After the transaction completes, remove the transaction from the local
+	 * cache. This could have been done by a <code>Synchronization</code>.
+	 */
+	@Override
+	public void removeRootTransaction(Xid toMigrate) {
+		rootTransactionsAsSubordinate.remove(new SubordinateXidImple(toMigrate));
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/fail2pc.txt
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/fail2pc.txt	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/fail2pc.txt	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,16 @@
+########################################################################
+#
+# byteman script used to ensure that tests can synchronize with various
+# actions performed by the recovery code
+
+#########################################################################
+RULE Fail 2PC
+CLASS  com.arjuna.ats.arjuna.coordinator.BasicAction
+METHOD phase2Commit
+AT ENTRY
+BIND NOTHING
+IF readCounter("phase2commit") == 0
+	DO debug("Called"),
+	incrementCounter("phase2commit");
+   	Thread.currentThread().stop()
+ENDRULE

Added: labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leave-subordinate-orphan.txt
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leave-subordinate-orphan.txt	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leave-subordinate-orphan.txt	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,16 @@
+########################################################################
+#
+# byteman script used to ensure that tests can synchronize with various
+# actions performed by the recovery code
+
+
+
+#########################################################################
+RULE Fail phase2Commit
+CLASS  com.arjuna.ats.arjuna.coordinator.BasicAction
+METHOD phase2Commit
+AT ENTRY
+BIND NOTHING
+IF TRUE
+	DO throw new Error()
+ENDRULE
\ No newline at end of file

Added: labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leaveorphan.txt
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leaveorphan.txt	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leaveorphan.txt	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,16 @@
+########################################################################
+#
+# byteman script used to ensure that tests can synchronize with various
+# actions performed by the recovery code
+
+#########################################################################
+
+RULE Fail resource prepare finishing
+CLASS  com.arjuna.ats.jta.distributed.TestResource
+METHOD prepare
+AT EXIT
+BIND NOTHING
+IF TRUE
+	DO debug("Neutralizing target"),
+	throw new java.lang.Error()
+ENDRULE
\ No newline at end of file

Added: labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leaverunningorphan.txt
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leaverunningorphan.txt	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/byteman-scripts/leaverunningorphan.txt	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,18 @@
+########################################################################
+#
+# byteman script used to ensure that tests can synchronize with various
+# actions performed by the recovery code
+
+#########################################################################
+
+RULE Fail resource prepare finishing
+CLASS  com.arjuna.ats.jta.distributed.SimpleIsolatedServers
+METHOD performTransactionalWork
+AT EXIT
+BIND NOTHING
+IF TRUE
+	DO debug("Neutralizing target"),
+	Thread.currentThread().stop()
+ENDRULE
+
+

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/internal/jbossatx/jta/TestXAResourceRecordWrapperImpl.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/internal/jbossatx/jta/TestXAResourceRecordWrapperImpl.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/internal/jbossatx/jta/TestXAResourceRecordWrapperImpl.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,148 @@
+package com.arjuna.ats.internal.jbossatx.jta;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.io.IOException;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.jboss.tm.XAResourceWrapper;
+import org.junit.Test;
+
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
+
+public class TestXAResourceRecordWrapperImpl {
+
+	@Test
+	public void testReadAndWriteEISNameSameKey() throws IOException, ObjectStoreException {
+		String xaNodeName = TxControl.getXANodeName();
+		TxControl.setXANodeName("1");
+		XAResourceRecordWrappingPluginImpl xaResourceRecordWrappingPluginImpl = new XAResourceRecordWrappingPluginImpl();
+		MyWrapper myWrapper = new MyWrapper("Simple");
+		Integer eisNameKey = xaResourceRecordWrappingPluginImpl.getEISName(myWrapper);
+
+		MyWrapper otherMyWrapper = new MyWrapper("Simple");
+		Integer shouldBeSameEisNameKey = xaResourceRecordWrappingPluginImpl.getEISName(otherMyWrapper);
+
+		System.out.println(eisNameKey + " == " + shouldBeSameEisNameKey + "?");
+		assertEquals(eisNameKey, shouldBeSameEisNameKey);
+		assertEquals("Simple", xaResourceRecordWrappingPluginImpl.getEISName(eisNameKey));
+		assertEquals("Simple", xaResourceRecordWrappingPluginImpl.getEISName(shouldBeSameEisNameKey));
+		TxControl.setXANodeName(xaNodeName);
+	}
+
+	@Test
+	public void testReadAndWriteEISNameDifferentKey() throws IOException, ObjectStoreException {
+		String xaNodeName = TxControl.getXANodeName();
+		TxControl.setXANodeName("1");
+		XAResourceRecordWrappingPluginImpl xaResourceRecordWrappingPluginImpl = new XAResourceRecordWrappingPluginImpl();
+		MyWrapper myWrapper = new MyWrapper("Simple1");
+		Integer eisNameKey = xaResourceRecordWrappingPluginImpl.getEISName(myWrapper);
+
+		MyWrapper otherMyWrapper = new MyWrapper("Simple2");
+		Integer shouldBeSameEisNameKey = xaResourceRecordWrappingPluginImpl.getEISName(otherMyWrapper);
+
+		System.out.println(eisNameKey + " == " + shouldBeSameEisNameKey + "?");
+		assertFalse(eisNameKey.equals(shouldBeSameEisNameKey));
+		assertEquals("Simple1", xaResourceRecordWrappingPluginImpl.getEISName(eisNameKey));
+		assertEquals("Simple2", xaResourceRecordWrappingPluginImpl.getEISName(shouldBeSameEisNameKey));
+		TxControl.setXANodeName(xaNodeName);
+	}
+
+	private class MyWrapper implements XAResourceWrapper {
+
+		private String jndiName;
+
+		public MyWrapper(String jndiName) {
+			this.jndiName = jndiName;
+		}
+
+		@Override
+		public String getJndiName() {
+			return jndiName;
+		}
+
+		@Override
+		public void commit(Xid arg0, boolean arg1) throws XAException {
+			// TODO Auto-generated method stub
+
+		}
+
+		@Override
+		public void end(Xid arg0, int arg1) throws XAException {
+			// TODO Auto-generated method stub
+
+		}
+
+		@Override
+		public void forget(Xid arg0) throws XAException {
+			// TODO Auto-generated method stub
+
+		}
+
+		@Override
+		public int getTransactionTimeout() throws XAException {
+			// TODO Auto-generated method stub
+			return 0;
+		}
+
+		@Override
+		public boolean isSameRM(XAResource arg0) throws XAException {
+			// TODO Auto-generated method stub
+			return false;
+		}
+
+		@Override
+		public int prepare(Xid arg0) throws XAException {
+			// TODO Auto-generated method stub
+			return 0;
+		}
+
+		@Override
+		public Xid[] recover(int arg0) throws XAException {
+			// TODO Auto-generated method stub
+			return null;
+		}
+
+		@Override
+		public void rollback(Xid arg0) throws XAException {
+			// TODO Auto-generated method stub
+
+		}
+
+		@Override
+		public boolean setTransactionTimeout(int arg0) throws XAException {
+			// TODO Auto-generated method stub
+			return false;
+		}
+
+		@Override
+		public void start(Xid arg0, int arg1) throws XAException {
+			// TODO Auto-generated method stub
+
+		}
+
+		@Override
+		public XAResource getResource() {
+			// TODO Auto-generated method stub
+			return null;
+		}
+
+		@Override
+		public String getProductName() {
+			// TODO Auto-generated method stub
+			return null;
+		}
+
+		@Override
+		public String getProductVersion() {
+			// TODO Auto-generated method stub
+			return null;
+		}
+
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/SimpleIsolatedServers.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/SimpleIsolatedServers.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/SimpleIsolatedServers.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,1084 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.jboss.byteman.contrib.bmunit.BMScript;
+import org.jboss.byteman.contrib.bmunit.BMUnitRunner;
+import org.jboss.byteman.rule.exception.ExecuteException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
+import com.arjuna.ats.jta.distributed.server.CompletionCounter;
+import com.arjuna.ats.jta.distributed.server.IsolatableServersClassLoader;
+import com.arjuna.ats.jta.distributed.server.LocalServer;
+import com.arjuna.ats.jta.distributed.server.LookupProvider;
+
+ at RunWith(BMUnitRunner.class)
+public class SimpleIsolatedServers {
+	private static String[] serverNodeNames = new String[] { "1000", "2000", "3000" };
+	private static int[] serverPortOffsets = new int[] { 1000, 2000, 3000 };
+	private static String[][] clusterBuddies = new String[][] { new String[] { "2000", "3000" }, new String[] { "1000", "3000" },
+			new String[] { "1000", "2000" } };
+	private static LookupProvider lookupProvider = LookupProvider.getInstance();
+	private static LocalServer[] localServers = new LocalServer[serverNodeNames.length];
+	private static CompletionCounter completionCounter = CompletionCounter.getInstance();
+
+	@BeforeClass
+	public static void setup() throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, ClassNotFoundException,
+			CoreEnvironmentBeanException, IOException, IllegalArgumentException, NoSuchFieldException {
+		for (int i = 0; i < serverNodeNames.length; i++) {
+			boot(i);
+		}
+	}
+
+	public static boolean deleteDir(File dir) {
+		if (dir.isDirectory()) {
+			String[] children = dir.list();
+			for (int i = 0; i < children.length; i++) {
+				boolean success = deleteDir(new File(dir, children[i]));
+				if (!success) {
+					return false;
+				}
+			}
+		}
+
+		// The directory is now empty so delete it
+		return dir.delete();
+	}
+
+	@AfterClass
+	public static void tearDown() throws Exception {
+		// Enable it if you need to ensure the folder is empty for some reason
+		if (false) {
+			File file = new File(System.getProperty("user.dir") + "/distributedjta-tests/");
+			boolean delete = !file.exists() ? true : deleteDir(file);
+			if (!delete) {
+				throw new Exception("Could not delete folder");
+			}
+		}
+		for (int i = 0; i < localServers.length; i++) {
+			ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+			Thread.currentThread().setContextClassLoader(localServers[i].getClassLoader());
+			localServers[i].shutdown();
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+		completionCounter.reset();
+		lookupProvider.clear();
+	}
+
+	private static void reboot(String serverName) throws Exception {
+		// int index = (Integer.valueOf(serverName) / 1000) - 1;
+		for (int i = 0; i < localServers.length; i++) {
+			if (localServers[i].getNodeName().equals(serverName)) {
+				ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+				Thread.currentThread().setContextClassLoader(localServers[i].getClassLoader());
+				localServers[i].shutdown();
+				Thread.currentThread().setContextClassLoader(contextClassLoader);
+
+				boot(i);
+				break;
+			}
+		}
+
+	}
+
+	private static void boot(int index) throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException,
+			ClassNotFoundException, IllegalArgumentException, CoreEnvironmentBeanException, IOException, NoSuchFieldException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		IsolatableServersClassLoader classLoaderForTransactionManager = new IsolatableServersClassLoader(null, SimpleIsolatedServers.class.getPackage()
+				.getName(), contextClassLoader);
+		IsolatableServersClassLoader classLoader = new IsolatableServersClassLoader(SimpleIsolatedServers.class.getPackage().getName(), null,
+				classLoaderForTransactionManager);
+		localServers[index] = (LocalServer) classLoader.loadClass("com.arjuna.ats.jta.distributed.server.impl.ServerImpl").newInstance();
+		Thread.currentThread().setContextClassLoader(localServers[index].getClassLoader());
+		localServers[index].initialise(lookupProvider, serverNodeNames[index], serverPortOffsets[index], clusterBuddies[index],
+				classLoaderForTransactionManager);
+		lookupProvider.bind(index, localServers[index].connectTo());
+		Thread.currentThread().setContextClassLoader(contextClassLoader);
+	}
+
+	/**
+	 * Ensure that two servers can start up and call recover on the same server
+	 * 
+	 * The JCA XATerminator call wont allow intermediary calls to
+	 * XATerminator::recover between TMSTARTSCAN and TMENDSCAN. This is fine for
+	 * distributed JTA.
+	 * 
+	 * @throws XAException
+	 * @throws IOException
+	 * @throws DummyRemoteException
+	 */
+	@Test
+	@BMScript("leave-subordinate-orphan")
+	public void testSimultaneousRecover() throws Exception {
+		System.out.println("testSimultaneousRecover");
+		tearDown();
+		setup();
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+		final CompletionCountLock phase2CommitAborted = new CompletionCountLock();
+		{
+			Thread thread = new Thread(new Runnable() {
+				public void run() {
+					int startingTimeout = 0;
+					try {
+						LocalServer originalServer = getLocalServer("1000");
+						ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+						Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+						TransactionManager transactionManager = originalServer.getTransactionManager();
+						transactionManager.setTransactionTimeout(startingTimeout);
+						transactionManager.begin();
+						Transaction originalTransaction = transactionManager.getTransaction();
+						int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+						Xid currentXid = originalServer.getCurrentXid();
+						originalServer.storeRootTransaction();
+						transactionManager.suspend();
+						DataReturnedFromRemoteServer performTransactionalWork = performTransactionalWork(
+								new LinkedList<String>(Arrays.asList(new String[] { "2000" })), remainingTimeout, currentXid, 2, false, false);
+						transactionManager.resume(originalTransaction);
+						XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", performTransactionalWork.getProxyRequired());
+						originalTransaction.enlistResource(proxyXAResource);
+						originalServer.removeRootTransaction(currentXid);
+						transactionManager.commit();
+						Thread.currentThread().setContextClassLoader(classLoader);
+					} catch (ExecuteException e) {
+						System.err.println("Should be a thread death but cest la vie");
+						synchronized (phase2CommitAborted) {
+							phase2CommitAborted.incrementCount();
+							phase2CommitAborted.notify();
+						}
+					} catch (LinkageError t) {
+						System.err.println("Should be a thread death but cest la vie");
+						synchronized (phase2CommitAborted) {
+							phase2CommitAborted.incrementCount();
+							phase2CommitAborted.notify();
+						}
+					} catch (Throwable t) {
+						System.err.println("Should be a thread death but cest la vie");
+						synchronized (phase2CommitAborted) {
+							phase2CommitAborted.incrementCount();
+							phase2CommitAborted.notify();
+						}
+					}
+				}
+			}, "Orphan-creator");
+			thread.start();
+		}
+
+		{
+			Thread thread = new Thread(new Runnable() {
+				public void run() {
+					int startingTimeout = 0;
+					try {
+						LocalServer originalServer = getLocalServer("2000");
+						ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+						Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+						TransactionManager transactionManager = originalServer.getTransactionManager();
+						transactionManager.setTransactionTimeout(startingTimeout);
+						transactionManager.begin();
+						Transaction originalTransaction = transactionManager.getTransaction();
+						int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+						Xid currentXid = originalServer.getCurrentXid();
+						originalServer.storeRootTransaction();
+						transactionManager.suspend();
+						DataReturnedFromRemoteServer performTransactionalWork = performTransactionalWork(
+								new LinkedList<String>(Arrays.asList(new String[] { "1000" })), remainingTimeout, currentXid, 2, false, false);
+						transactionManager.resume(originalTransaction);
+						XAResource proxyXAResource = originalServer.generateProxyXAResource("1000", performTransactionalWork.getProxyRequired());
+						originalTransaction.enlistResource(proxyXAResource);
+						originalServer.removeRootTransaction(currentXid);
+						transactionManager.commit();
+						Thread.currentThread().setContextClassLoader(classLoader);
+					} catch (ExecuteException e) {
+						System.err.println("Should be a thread death but cest la vie");
+						synchronized (phase2CommitAborted) {
+							phase2CommitAborted.incrementCount();
+							phase2CommitAborted.notify();
+						}
+					} catch (LinkageError t) {
+						System.err.println("Should be a thread death but cest la vie");
+						synchronized (phase2CommitAborted) {
+							phase2CommitAborted.incrementCount();
+							phase2CommitAborted.notify();
+						}
+					} catch (Throwable t) {
+						System.err.println("Should be a thread death but cest la vie");
+						synchronized (phase2CommitAborted) {
+							phase2CommitAborted.incrementCount();
+							phase2CommitAborted.notify();
+						}
+					}
+				}
+			}, "Orphan-creator");
+			thread.start();
+		}
+		synchronized (phase2CommitAborted) {
+			if (phase2CommitAborted.getCount() < 2) {
+				phase2CommitAborted.wait();
+			}
+		}
+
+		reboot("1000");
+		reboot("2000");
+		reboot("3000");
+
+		final CompletionCountLock lock = new CompletionCountLock();
+		{
+			new Thread(new Runnable() {
+				public void run() {
+					getLocalServer("2000").doRecoveryManagerScan(true);
+					lock.incrementCount();
+				}
+			}, "RecMan2000").start();
+		}
+
+		{
+			new Thread(new Runnable() {
+				public void run() {
+					getLocalServer("1000").doRecoveryManagerScan(true);
+					lock.incrementCount();
+				}
+			}, "RecMan1000").start();
+		}
+
+		synchronized (lock) {
+			while (lock.getCount() < 2) {
+				lock.wait();
+			}
+		}
+
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 3);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 3);
+
+	}
+
+	/**
+	 * Ensure that subordinate XA resource orphans created during 2PC can be
+	 * recovered
+	 */
+	@Test
+	@BMScript("leaveorphan")
+	public void testTwoPhaseXAResourceOrphan() throws Exception {
+		System.out.println("testTwoPhaseXAResourceOrphan");
+		tearDown();
+		setup();
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		final CompletionCountLock phase2CommitAborted = new CompletionCountLock();
+		Thread thread = new Thread(new Runnable() {
+			public void run() {
+				int startingTimeout = 0;
+				try {
+					LocalServer originalServer = getLocalServer("1000");
+					ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+					Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+					TransactionManager transactionManager = originalServer.getTransactionManager();
+					transactionManager.setTransactionTimeout(startingTimeout);
+					transactionManager.begin();
+					Transaction originalTransaction = transactionManager.getTransaction();
+					int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+					Xid currentXid = originalServer.getCurrentXid();
+					originalServer.storeRootTransaction();
+					transactionManager.suspend();
+					DataReturnedFromRemoteServer performTransactionalWork = performTransactionalWork(
+							new LinkedList<String>(Arrays.asList(new String[] { "2000" })), remainingTimeout, currentXid, 1, false, false);
+					transactionManager.resume(originalTransaction);
+					XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", performTransactionalWork.getProxyRequired());
+					originalTransaction.enlistResource(proxyXAResource);
+					// Needs a second resource to make sure we dont get the one
+					// phase optimization happening
+					originalTransaction.enlistResource(new TestResource(originalServer.getNodeName(), false));
+					originalServer.removeRootTransaction(currentXid);
+					transactionManager.commit();
+					Thread.currentThread().setContextClassLoader(classLoader);
+				} catch (Error t) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				} catch (Throwable t) {
+					t.printStackTrace();
+				}
+			}
+		}, "Orphan-creator");
+		thread.start();
+		synchronized (phase2CommitAborted) {
+			if (phase2CommitAborted.getCount() < 1) {
+				phase2CommitAborted.wait();
+			}
+		}
+
+		reboot("1000");
+		reboot("2000");
+		reboot("3000");
+
+		{
+
+			assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+			assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+			getLocalServer("2000").doRecoveryManagerScan(true);
+			assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+			assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 1);
+		}
+		{
+			assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+			assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+			getLocalServer("1000").doRecoveryManagerScan(true);
+			assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+			assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+		}
+	}
+
+	/**
+	 * Ensure that subordinate XA resource orphans created during 1PC (at root)
+	 * can be recovered
+	 */
+	@Test
+	@BMScript("leaveorphan")
+	public void testOnePhaseXAResourceOrphan() throws Exception {
+		System.out.println("testOnePhaseXAResourceOrphan");
+		tearDown();
+		setup();
+		assertTrue("" + completionCounter.getCommitCount("3000"), completionCounter.getCommitCount("3000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		final CompletionCountLock phase2CommitAborted = new CompletionCountLock();
+		Thread thread = new Thread(new Runnable() {
+			public void run() {
+				int startingTimeout = 0;
+				try {
+					LocalServer originalServer = getLocalServer("1000");
+					ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+					Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+					TransactionManager transactionManager = originalServer.getTransactionManager();
+					transactionManager.setTransactionTimeout(startingTimeout);
+					transactionManager.begin();
+					Transaction originalTransaction = transactionManager.getTransaction();
+					int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+					Xid currentXid = originalServer.getCurrentXid();
+					originalServer.storeRootTransaction();
+					transactionManager.suspend();
+					DataReturnedFromRemoteServer performTransactionalWork = performTransactionalWork(
+							new LinkedList<String>(Arrays.asList(new String[] { "2000" })), remainingTimeout, currentXid, 2, false, false);
+					transactionManager.resume(originalTransaction);
+					XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", performTransactionalWork.getProxyRequired());
+					originalTransaction.enlistResource(proxyXAResource);
+					originalServer.removeRootTransaction(currentXid);
+					transactionManager.commit();
+					Thread.currentThread().setContextClassLoader(classLoader);
+				} catch (Error t) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				} catch (Throwable t) {
+					t.printStackTrace();
+					// synchronized (phase2CommitAborted) {
+					// phase2CommitAborted.incrementPhase2CommitAborted();
+					// phase2CommitAborted.notify();
+					// }
+				}
+			}
+		}, "Orphan-creator");
+		thread.start();
+		synchronized (phase2CommitAborted) {
+			if (phase2CommitAborted.getCount() < 1) {
+				phase2CommitAborted.wait();
+			}
+		}
+
+		reboot("1000");
+		reboot("2000");
+		reboot("3000");
+
+		{
+
+			assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+			assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+			getLocalServer("2000").doRecoveryManagerScan(true);
+			assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+			assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 1);
+		}
+		{
+			assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+			assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+			getLocalServer("1000").doRecoveryManagerScan(true);
+			assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+			assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+		}
+	}
+
+	/**
+	 * Ensure that subordinate transaction orphans created during 1PC (at root)
+	 * can be recovered
+	 */
+	@Test
+	@BMScript("leave-subordinate-orphan")
+	public void testOnePhaseSubordinateOrphan() throws Exception {
+		System.out.println("testOnePhaseSubordinateOrphan");
+		tearDown();
+		setup();
+		assertTrue("" + completionCounter.getCommitCount("3000"), completionCounter.getCommitCount("3000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		final CompletionCountLock phase2CommitAborted = new CompletionCountLock();
+		Thread thread = new Thread(new Runnable() {
+			public void run() {
+				int startingTimeout = 0;
+				try {
+					LocalServer originalServer = getLocalServer("1000");
+					ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+					Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+					TransactionManager transactionManager = originalServer.getTransactionManager();
+					transactionManager.setTransactionTimeout(startingTimeout);
+					transactionManager.begin();
+					Transaction originalTransaction = transactionManager.getTransaction();
+					int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+					Xid currentXid = originalServer.getCurrentXid();
+					originalServer.storeRootTransaction();
+					transactionManager.suspend();
+					DataReturnedFromRemoteServer performTransactionalWork = performTransactionalWork(
+							new LinkedList<String>(Arrays.asList(new String[] { "2000" })), remainingTimeout, currentXid, 2, false, false);
+					transactionManager.resume(originalTransaction);
+					XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", performTransactionalWork.getProxyRequired());
+					originalTransaction.enlistResource(proxyXAResource);
+					originalServer.removeRootTransaction(currentXid);
+					transactionManager.commit();
+					Thread.currentThread().setContextClassLoader(classLoader);
+				} catch (ExecuteException e) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				} catch (LinkageError t) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				} catch (Throwable t) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				}
+			}
+		}, "Orphan-creator");
+		thread.start();
+		synchronized (phase2CommitAborted) {
+			if (phase2CommitAborted.getCount() < 1) {
+				phase2CommitAborted.wait();
+			}
+		}
+		reboot("1000");
+		reboot("2000");
+		reboot("3000");
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+		getLocalServer("1000").doRecoveryManagerScan(true);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 1);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 2);
+
+	}
+
+	/**
+	 * Check that if transaction was in flight when a root crashed, when
+	 * recovered it can terminate it.
+	 * 
+	 * recoverFor first greps the logs for any subordinates that are owned by
+	 * "parentNodeName" then it greps the list of currently running transactions
+	 * to see if any of them are owned by "parentNodeName" this is covered by
+	 * testRecoverInflightTransaction basically what can happen is:
+	 * 
+	 * 1. TM1 starts tx 2. propagate to TM2 3. TM1 crashes 4. we need to
+	 * rollback TM2 as it is now orphaned the detail being that as TM2 hasn't
+	 * prepared we cant just grep the logs at TM2 as there wont be one
+	 */
+	@Test
+	@BMScript("leaverunningorphan")
+	public void testRecoverInflightTransaction() throws Exception {
+		System.out.println("testRecoverInflightTransaction");
+		tearDown();
+		setup();
+
+		assertTrue("" + completionCounter.getCommitCount("3000"), completionCounter.getCommitCount("3000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		final CompletionCountLock phase2CommitAborted = new CompletionCountLock();
+		Thread thread = new Thread(new Runnable() {
+			public void run() {
+				int startingTimeout = 0;
+				try {
+					LocalServer originalServer = getLocalServer("1000");
+					ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+					Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+					TransactionManager transactionManager = originalServer.getTransactionManager();
+					transactionManager.setTransactionTimeout(startingTimeout);
+					transactionManager.begin();
+					Transaction originalTransaction = transactionManager.getTransaction();
+					int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+					Xid currentXid = originalServer.getCurrentXid();
+					originalServer.storeRootTransaction();
+					transactionManager.suspend();
+					DataReturnedFromRemoteServer performTransactionalWork = performTransactionalWork(
+							new LinkedList<String>(Arrays.asList(new String[] { "2000" })), remainingTimeout, currentXid, 2, false, false);
+					transactionManager.resume(originalTransaction);
+					XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", performTransactionalWork.getProxyRequired());
+					originalTransaction.enlistResource(proxyXAResource);
+					originalServer.removeRootTransaction(currentXid);
+					transactionManager.commit();
+					Thread.currentThread().setContextClassLoader(classLoader);
+				} catch (ExecuteException e) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				} catch (LinkageError t) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				} catch (Throwable t) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				}
+			}
+		}, "Orphan-creator");
+		thread.start();
+		synchronized (phase2CommitAborted) {
+			if (phase2CommitAborted.getCount() < 1) {
+				phase2CommitAborted.wait();
+			}
+		}
+		reboot("1000");
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+		getLocalServer("1000").doRecoveryManagerScan(true);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		assertTrue("Rollback count at 1000: " + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 1);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 2);
+	}
+
+	/**
+	 * Top down recovery of a prepared transaction
+	 */
+	@Test
+	@BMScript("fail2pc")
+	public void testRecovery() throws Exception {
+		System.out.println("testRecovery");
+		tearDown();
+		setup();
+		assertTrue("" + completionCounter.getCommitCount("3000"), completionCounter.getCommitCount("3000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("3000"), completionCounter.getRollbackCount("3000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+		final CompletionCountLock phase2CommitAborted = new CompletionCountLock();
+		Thread thread = new Thread(new Runnable() {
+			public void run() {
+				int startingTimeout = 0;
+				List<String> nodesToFlowTo = new LinkedList<String>(Arrays.asList(new String[] { "1000", "2000", "3000", "2000", "1000", "2000", "3000",
+						"1000", "3000" }));
+				try {
+					doRecursiveTransactionalWork(startingTimeout, nodesToFlowTo, true, false);
+				} catch (ExecuteException e) {
+					System.err.println("Should be a thread death but cest la vie");
+					synchronized (phase2CommitAborted) {
+						phase2CommitAborted.incrementCount();
+						phase2CommitAborted.notify();
+					}
+				} catch (Exception e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
+		});
+		thread.start();
+		synchronized (phase2CommitAborted) {
+			if (phase2CommitAborted.getCount() < 1) {
+				phase2CommitAborted.wait();
+			}
+		}
+
+		reboot("1000");
+		reboot("2000");
+		reboot("3000");
+
+		getLocalServer("1000").doRecoveryManagerScan(false);
+
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 4);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 4);
+		assertTrue("" + completionCounter.getCommitCount("3000"), completionCounter.getCommitCount("3000") == 3);
+		assertTrue("" + completionCounter.getRollbackCount("3000"), completionCounter.getRollbackCount("3000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+	}
+
+	@Test
+	public void testOnePhaseCommit() throws Exception {
+		System.out.println("testOnePhaseCommit");
+		tearDown();
+		setup();
+		LocalServer originalServer = getLocalServer("1000");
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+		TransactionManager transactionManager = originalServer.getTransactionManager();
+		transactionManager.setTransactionTimeout(0);
+		transactionManager.begin();
+		Transaction originalTransaction = transactionManager.getTransaction();
+		int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+		Xid currentXid = originalServer.getCurrentXid();
+		originalServer.storeRootTransaction();
+		transactionManager.suspend();
+		DataReturnedFromRemoteServer performTransactionalWork = performTransactionalWork(new LinkedList<String>(Arrays.asList(new String[] { "2000" })),
+				remainingTimeout, currentXid, 1, false, false);
+		transactionManager.resume(originalTransaction);
+		XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", performTransactionalWork.getProxyRequired());
+		originalTransaction.enlistResource(proxyXAResource);
+		originalServer.removeRootTransaction(currentXid);
+		transactionManager.commit();
+		Thread.currentThread().setContextClassLoader(classLoader);
+
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 1);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 1);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+	}
+
+	@Test
+	public void testUnPreparedRollback() throws Exception {
+		System.out.println("testUnPreparedRollback");
+		tearDown();
+		setup();
+		LocalServer originalServer = getLocalServer("1000");
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+		TransactionManager transactionManager = originalServer.getTransactionManager();
+		transactionManager.setTransactionTimeout(0);
+		transactionManager.begin();
+		Transaction originalTransaction = transactionManager.getTransaction();
+		int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+		Xid currentXid = originalServer.getCurrentXid();
+		originalServer.storeRootTransaction();
+		transactionManager.suspend();
+		DataReturnedFromRemoteServer performTransactionalWork = performTransactionalWork(new LinkedList<String>(Arrays.asList(new String[] { "2000" })),
+				remainingTimeout, currentXid, 1, false, false);
+		transactionManager.resume(originalTransaction);
+		XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", performTransactionalWork.getProxyRequired());
+		originalTransaction.enlistResource(proxyXAResource);
+		originalTransaction.registerSynchronization(originalServer.generateProxySynchronization("2000", currentXid));
+		originalServer.removeRootTransaction(currentXid);
+		transactionManager.rollback();
+		Thread.currentThread().setContextClassLoader(classLoader);
+
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 0);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 1);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 1);
+	}
+
+	@Test
+	public void testMigrateTransactionRollbackOnlyCommit() throws Exception {
+		System.out.println("testMigrateTransactionRollbackOnlyCommit");
+		int startingTimeout = 0;
+		List<String> nodesToFlowTo = new LinkedList<String>(
+				Arrays.asList(new String[] { "1000", "2000", "3000", "2000", "1000", "2000", "3000", "1000", "3000" }));
+		doRecursiveTransactionalWork(startingTimeout, nodesToFlowTo, true, true);
+	}
+
+	@Test
+	public void testMigrateTransactionRollbackOnlyRollback() throws Exception {
+		System.out.println("testMigrateTransactionRollbackOnlyRollback");
+		int startingTimeout = 0;
+		List<String> nodesToFlowTo = new LinkedList<String>(
+				Arrays.asList(new String[] { "1000", "2000", "3000", "2000", "1000", "2000", "3000", "1000", "3000" }));
+		doRecursiveTransactionalWork(startingTimeout, nodesToFlowTo, false, true);
+	}
+
+	@Test
+	public void testMigrateTransactionCommit() throws Exception {
+		System.out.println("testMigrateTransactionCommit");
+		int startingTimeout = 0;
+		List<String> nodesToFlowTo = new LinkedList<String>(
+				Arrays.asList(new String[] { "1000", "2000", "3000", "2000", "1000", "2000", "3000", "1000", "3000" }));
+		doRecursiveTransactionalWork(startingTimeout, nodesToFlowTo, true, false);
+	}
+
+	@Test
+	public void testMigrateTransactionCommitDiamond() throws Exception {
+		System.out.println("testMigrateTransactionCommitDiamond");
+
+		int startingTimeout = 0;
+		List<String> nodesToFlowTo = new LinkedList<String>(Arrays.asList(new String[] { "1000", "2000", "1000", "3000", "1000", "2000", "3000" }));
+		doRecursiveTransactionalWork(startingTimeout, nodesToFlowTo, true, false);
+	}
+
+	@Test
+	public void testMigrateTransactionRollback() throws Exception {
+		System.out.println("testMigrateTransactionRollback");
+		int startingTimeout = 0;
+		List<String> nodesToFlowTo = new LinkedList<String>(
+				Arrays.asList(new String[] { "1000", "2000", "3000", "2000", "1000", "2000", "3000", "1000", "3000" }));
+		doRecursiveTransactionalWork(startingTimeout, nodesToFlowTo, false, false);
+	}
+
+	@Test
+	public void testMigrateTransactionRollbackDiamond() throws Exception {
+		System.out.println("testMigrateTransactionRollbackDiamond");
+		int startingTimeout = 0;
+		List<String> nodesToFlowTo = new LinkedList<String>(Arrays.asList(new String[] { "1000", "2000", "1000", "3000", "1000", "2000", "3000" }));
+		doRecursiveTransactionalWork(startingTimeout, nodesToFlowTo, false, false);
+	}
+
+	@Test
+	public void testMigrateTransactionSubordinateTimeout() throws Exception {
+		System.out.println("testMigrateTransactionSubordinateTimeout");
+		tearDown();
+		setup();
+		int rootTimeout = 10000;
+		int subordinateTimeout = 1;
+		LocalServer originalServer = getLocalServer("1000");
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+		TransactionManager transactionManager = originalServer.getTransactionManager();
+		transactionManager.setTransactionTimeout(rootTimeout);
+		transactionManager.begin();
+		Transaction originalTransaction = transactionManager.getTransaction();
+		Xid currentXid = originalServer.getCurrentXid();
+		originalServer.storeRootTransaction();
+		originalTransaction.enlistResource(new TestResource(originalServer.getNodeName(), false));
+		transactionManager.suspend();
+
+		// Migrate a transaction
+		LocalServer currentServer = getLocalServer("2000");
+		ClassLoader parentsClassLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(currentServer.getClassLoader());
+		Xid migratedXid = currentServer.locateOrImportTransactionThenResumeIt(subordinateTimeout, currentXid);
+		currentServer.getTransactionManager().getTransaction().enlistResource(new TestResource(currentServer.getNodeName(), false));
+		currentServer.getTransactionManager().suspend();
+		Thread.currentThread().setContextClassLoader(parentsClassLoader);
+
+		// Complete the transaction at the original server
+		transactionManager.resume(originalTransaction);
+		XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", migratedXid);
+		originalTransaction.enlistResource(proxyXAResource);
+		originalServer.removeRootTransaction(currentXid);
+		Thread.currentThread().sleep((subordinateTimeout + 1) * 1000);
+		try {
+			transactionManager.commit();
+			fail("Did not rollback");
+		} catch (RollbackException rbe) {
+			// GOOD!
+		} finally {
+			Thread.currentThread().setContextClassLoader(classLoader);
+		}
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 1);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 2);
+	}
+
+	@Test
+	public void testTransactionReaperIsCleanedUp() throws Exception {
+		System.out.println("testTransactionReaperIsCleanedUp");
+		tearDown();
+		setup();
+		int rootTimeout = 5;
+		LocalServer originalServer = getLocalServer("1000");
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+		TransactionManager transactionManager = originalServer.getTransactionManager();
+		transactionManager.setTransactionTimeout(rootTimeout);
+		transactionManager.begin();
+		Transaction originalTransaction = transactionManager.getTransaction();
+		Xid currentXid = originalServer.getCurrentXid();
+		originalServer.storeRootTransaction();
+		originalTransaction.enlistResource(new TestResource(originalServer.getNodeName(), false));
+		int subordinateTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+		transactionManager.suspend();
+
+		// Migrate a transaction
+		LocalServer currentServer = getLocalServer("2000");
+		ClassLoader parentsClassLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(currentServer.getClassLoader());
+		Xid migratedXid = currentServer.locateOrImportTransactionThenResumeIt(subordinateTimeout, currentXid);
+		currentServer.getTransactionManager().getTransaction().enlistResource(new TestResource(currentServer.getNodeName(), false));
+		currentServer.getTransactionManager().suspend();
+		Thread.currentThread().setContextClassLoader(parentsClassLoader);
+
+		// Complete the transaction at the original server
+		transactionManager.resume(originalTransaction);
+		XAResource proxyXAResource = originalServer.generateProxyXAResource("2000", migratedXid);
+		originalTransaction.enlistResource(proxyXAResource);
+		originalServer.removeRootTransaction(currentXid);
+		transactionManager.commit();
+		Thread.currentThread().setContextClassLoader(classLoader);
+		assertTrue("" + completionCounter.getCommitCount("2000"), completionCounter.getCommitCount("2000") == 1);
+		assertTrue("" + completionCounter.getCommitCount("1000"), completionCounter.getCommitCount("1000") == 2);
+		assertTrue("" + completionCounter.getRollbackCount("2000"), completionCounter.getRollbackCount("2000") == 0);
+		assertTrue("" + completionCounter.getRollbackCount("1000"), completionCounter.getRollbackCount("1000") == 0);
+
+		Thread.currentThread().sleep((subordinateTimeout + 4) * 1000);
+	}
+
+	private void doRecursiveTransactionalWork(int startingTimeout, List<String> nodesToFlowTo, boolean commit, boolean rollbackOnlyOnLastNode) throws Exception {
+		tearDown();
+		setup();
+
+		List<String> uniqueServers = new ArrayList<String>();
+		Iterator<String> iterator = nodesToFlowTo.iterator();
+		while (iterator.hasNext()) {
+			String intern = iterator.next().intern();
+			if (!uniqueServers.contains(intern)) {
+				uniqueServers.add(intern);
+			}
+		}
+		// Start out at the first server
+		int totalCompletionCount = nodesToFlowTo.size() + uniqueServers.size() - 1;
+		String startingServer = nodesToFlowTo.get(0);
+		LocalServer originalServer = getLocalServer(startingServer);
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(originalServer.getClassLoader());
+		TransactionManager transactionManager = originalServer.getTransactionManager();
+		transactionManager.setTransactionTimeout(startingTimeout);
+		transactionManager.begin();
+		Transaction transaction = transactionManager.getTransaction();
+		int remainingTimeout = (int) (originalServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+		Xid currentXid = originalServer.getCurrentXid();
+		originalServer.storeRootTransaction();
+		transactionManager.suspend();
+		DataReturnedFromRemoteServer dataReturnedFromRemoteServer = performTransactionalWork(nodesToFlowTo, remainingTimeout, currentXid, 1, true,
+				rollbackOnlyOnLastNode);
+		transactionManager.resume(transaction);
+		originalServer.removeRootTransaction(currentXid);
+
+		// Align the local state with the returning state of the
+		// transaction
+		// from the subordinate
+		switch (dataReturnedFromRemoteServer.getTransactionState()) {
+		case Status.STATUS_MARKED_ROLLBACK:
+		case Status.STATUS_ROLLEDBACK:
+		case Status.STATUS_ROLLING_BACK:
+			switch (transaction.getStatus()) {
+			case Status.STATUS_MARKED_ROLLBACK:
+			case Status.STATUS_ROLLEDBACK:
+			case Status.STATUS_ROLLING_BACK:
+				transaction.setRollbackOnly();
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (commit) {
+			try {
+				transactionManager.commit();
+				assertTrue(completionCounter.getTotalCommitCount() == totalCompletionCount);
+			} catch (RollbackException e) {
+				if (!rollbackOnlyOnLastNode) {
+					assertTrue(completionCounter.getTotalRollbackCount() == totalCompletionCount);
+				}
+			}
+		} else {
+			transactionManager.rollback();
+			assertTrue(completionCounter.getTotalRollbackCount() == totalCompletionCount);
+		}
+		Thread.currentThread().setContextClassLoader(classLoader);
+	}
+
+	private DataReturnedFromRemoteServer performTransactionalWork(List<String> nodesToFlowTo, int remainingTimeout, Xid toMigrate,
+			int numberOfResourcesToRegister, boolean addSynchronization, boolean rollbackOnlyOnLastNode) throws RollbackException, IllegalStateException,
+			XAException, SystemException, NotSupportedException, IOException {
+		String currentServerName = nodesToFlowTo.remove(0);
+		LocalServer currentServer = getLocalServer(currentServerName);
+
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(currentServer.getClassLoader());
+
+		Xid requiresProxyAtPreviousServer = currentServer.locateOrImportTransactionThenResumeIt(remainingTimeout, toMigrate);
+
+		// Perform work on the migrated transaction
+		{
+			TransactionManager transactionManager = currentServer.getTransactionManager();
+			Transaction transaction = transactionManager.getTransaction();
+			if (addSynchronization) {
+				transaction.registerSynchronization(new TestSynchronization(currentServer.getNodeName()));
+			}
+			for (int i = 0; i < numberOfResourcesToRegister; i++) {
+				transaction.enlistResource(new TestResource(currentServer.getNodeName(), false));
+			}
+
+			if (rollbackOnlyOnLastNode && nodesToFlowTo.isEmpty()) {
+				transaction.setRollbackOnly();
+			}
+		}
+
+		if (!nodesToFlowTo.isEmpty()) {
+
+			TransactionManager transactionManager = currentServer.getTransactionManager();
+			Transaction transaction = transactionManager.getTransaction();
+			int status = transaction.getStatus();
+
+			// Only propagate active transactions - this may be inactive through
+			// user code (rollback/setRollbackOnly) or it may be inactive due to
+			// the transaction reaper
+			if (status == Status.STATUS_ACTIVE) {
+				String nextServerNodeName = nodesToFlowTo.get(0);
+
+				// FLOW THE TRANSACTION
+				remainingTimeout = (int) (currentServer.getTimeLeftBeforeTransactionTimeout() / 1000);
+
+				// STORE AND SUSPEND THE TRANSACTION
+				Xid currentXid = currentServer.getCurrentXid();
+				transactionManager.suspend();
+
+				DataReturnedFromRemoteServer dataReturnedFromRemoteServer = performTransactionalWork(nodesToFlowTo, remainingTimeout, currentXid,
+						numberOfResourcesToRegister, addSynchronization, rollbackOnlyOnLastNode);
+				transactionManager.resume(transaction);
+
+				// Create a proxy for the new server if necessary, this can
+				// orphan
+				// the remote server but XA recovery will handle that on the
+				// remote
+				// server
+				// The alternative is to always create a proxy but this is a
+				// performance drain and will result in multiple subordinate
+				// transactions and performance issues
+				if (dataReturnedFromRemoteServer.getProxyRequired() != null) {
+					XAResource proxyXAResource = currentServer.generateProxyXAResource(nextServerNodeName, dataReturnedFromRemoteServer.getProxyRequired());
+					transaction.enlistResource(proxyXAResource);
+					transaction.registerSynchronization(currentServer.generateProxySynchronization(nextServerNodeName, toMigrate));
+				}
+
+				// Align the local state with the returning state of the
+				// transaction
+				// from the subordinate
+				switch (dataReturnedFromRemoteServer.getTransactionState()) {
+				case Status.STATUS_MARKED_ROLLBACK:
+				case Status.STATUS_ROLLEDBACK:
+				case Status.STATUS_ROLLING_BACK:
+					switch (transaction.getStatus()) {
+					case Status.STATUS_MARKED_ROLLBACK:
+					case Status.STATUS_ROLLEDBACK:
+					case Status.STATUS_ROLLING_BACK:
+						transaction.setRollbackOnly();
+					}
+					break;
+				default:
+					break;
+				}
+			}
+		}
+		TransactionManager transactionManager = currentServer.getTransactionManager();
+		int transactionState = transactionManager.getStatus();
+		// SUSPEND THE TRANSACTION WHEN YOU ARE READY TO RETURN TO YOUR CALLER
+		transactionManager.suspend();
+		// Return to the previous caller back over the transport/classloader
+		// boundary in this case
+		Thread.currentThread().setContextClassLoader(classLoader);
+		return new DataReturnedFromRemoteServer(requiresProxyAtPreviousServer, transactionState);
+	}
+
+	private static LocalServer getLocalServer(String jndiName) {
+		int index = (Integer.valueOf(jndiName) / 1000) - 1;
+		return localServers[index];
+	}
+
+	private class CompletionCountLock {
+		private int count;
+
+		public int getCount() {
+			return count;
+		}
+
+		public synchronized void incrementCount() {
+			this.count++;
+			this.notify();
+		}
+	}
+
+	/**
+	 * This is the transactional data the transport needs to return from remote
+	 * instances.
+	 */
+	private class DataReturnedFromRemoteServer {
+		private Xid proxyRequired;
+
+		private int transactionState;
+
+		public DataReturnedFromRemoteServer(Xid proxyRequired, int transactionState) {
+			this.proxyRequired = proxyRequired;
+			this.transactionState = transactionState;
+		}
+
+		public Xid getProxyRequired() {
+			return proxyRequired;
+		}
+
+		public int getTransactionState() {
+			return transactionState;
+		}
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestResource.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestResource.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestResource.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+
+package com.arjuna.ats.jta.distributed;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.jta.distributed.server.CompletionCounter;
+
+public class TestResource implements XAResource {
+	private Xid xid;
+
+	protected int timeout = 0;
+
+	private boolean readonly = false;
+
+	private File file;
+
+	private String serverId;
+
+	private CompletionCounter completionCounter;
+
+	public TestResource(String serverId, boolean readonly) {
+		this.completionCounter = CompletionCounter.getInstance();
+		this.serverId = serverId;
+		this.readonly = readonly;
+	}
+
+	public TestResource(String serverId, File file) throws IOException {
+		this.completionCounter = CompletionCounter.getInstance();
+		this.serverId = serverId;
+		this.file = file;
+		DataInputStream fis = new DataInputStream(new FileInputStream(file));
+		final int formatId = fis.readInt();
+		final int gtrid_length = fis.readInt();
+		final byte[] gtrid = new byte[gtrid_length];
+		fis.read(gtrid, 0, gtrid_length);
+		final int bqual_length = fis.readInt();
+		final byte[] bqual = new byte[bqual_length];
+		fis.read(bqual, 0, bqual_length);
+		this.xid = new Xid() {
+
+			@Override
+			public byte[] getGlobalTransactionId() {
+				return gtrid;
+			}
+
+			@Override
+			public int getFormatId() {
+				return formatId;
+			}
+
+			@Override
+			public byte[] getBranchQualifier() {
+				return bqual;
+			}
+			
+			public String toString() {
+				StringBuilder stringBuilder = new StringBuilder();
+		        stringBuilder.append("< formatId=");
+		        stringBuilder.append(formatId);
+		        stringBuilder.append(", gtrid_length=");
+		        stringBuilder.append(gtrid_length);
+		        stringBuilder.append(", bqual_length=");
+		        stringBuilder.append(bqual_length);
+		        stringBuilder.append(", tx_uid=");
+		        stringBuilder.append(new Uid(gtrid).stringForm());
+		        stringBuilder.append(", node_name=");
+		        stringBuilder.append(new String(Arrays.copyOfRange(gtrid, Uid.UID_SIZE, gtrid_length)));
+		        stringBuilder.append(", branch_uid=");
+		        stringBuilder.append(new Uid(bqual));;
+		        stringBuilder.append(", subordinatenodename=");
+		        
+		        int offset = Uid.UID_SIZE + 4;
+		        int length = (bqual[offset++] << 24)
+						+ ((bqual[offset++] & 0xFF) << 16)
+						+ ((bqual[offset++] & 0xFF) << 8)
+						+ (bqual[offset++] & 0xFF);
+				if (length > 0) 
+					stringBuilder.append(new String(Arrays.copyOfRange(bqual, offset, offset+length)));
+					
+		        
+		        stringBuilder.append(", eis_name=unknown");
+		        stringBuilder.append(" >");
+
+		        return stringBuilder.toString();
+			}
+		};
+		fis.close();
+	}
+
+	/**
+	 * This class declares that it throws an Error *purely for byteman* so that
+	 * we can crash the resource during this method:
+	 * https://issues.jboss.org/browse/BYTEMAN-156
+	 * https://issues.jboss.org/browse/BYTEMAN-175
+	 */
+	public synchronized int prepare(Xid xid) throws XAException, Error {
+		System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      XA_PREPARE [" + xid + "]");
+
+		if (readonly)
+			return XA_RDONLY;
+		else {
+			File dir = new File(System.getProperty("user.dir") + "/distributedjta-tests/TestResource/" + serverId + "/");
+			dir.mkdirs();
+			file = new File(dir, new Uid().fileStringForm() + "_");
+			try {
+				file.createNewFile();
+				final int formatId = xid.getFormatId();
+				final byte[] gtrid = xid.getGlobalTransactionId();
+				final int gtrid_length = gtrid.length;
+				final byte[] bqual = xid.getBranchQualifier();
+				final int bqual_length = bqual.length;
+
+				DataOutputStream fos = new DataOutputStream(new FileOutputStream(file));
+				fos.writeInt(formatId);
+				fos.writeInt(gtrid_length);
+				fos.write(gtrid, 0, gtrid_length);
+				fos.writeInt(bqual_length);
+				fos.write(bqual, 0, bqual_length);
+				fos.flush();
+				fos.close();
+			} catch (IOException e) {
+				throw new XAException(XAException.XAER_RMERR);
+			}
+			return XA_OK;
+		}
+	}
+
+	public synchronized void commit(Xid id, boolean onePhase) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      XA_COMMIT  [" + id + "]");
+		completionCounter.incrementCommit(serverId);
+		if (file != null) {
+			if (!file.delete()) {
+				throw new XAException(XAException.XA_RETRY);
+			}
+		}
+		this.xid = null;
+	}
+
+	public synchronized void rollback(Xid xid) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      XA_ROLLBACK[" + xid + "]");
+		completionCounter.incrementRollback(serverId);
+		if (file != null) {
+			if (!file.delete()) {
+				throw new XAException(XAException.XA_RETRY);
+			}
+		}
+		this.xid = null;
+	}
+
+	public void start(Xid xid, int flags) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      XA_START   [" + xid + "] Flags=" + flags);
+	}
+
+	public void end(Xid xid, int flags) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      XA_END     [" + xid + "] Flags=" + flags);
+	}
+
+	public void forget(Xid xid) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      XA_FORGET[" + xid + "]");
+	}
+
+	public int getTransactionTimeout() throws XAException {
+		return (timeout);
+	}
+
+	public boolean isSameRM(XAResource xares) throws XAException {
+		if (xares instanceof TestResource) {
+			TestResource other = (TestResource) xares;
+			if ((this.xid != null && other.xid != null)) {
+				if (this.xid.getFormatId() == other.xid.getFormatId()) {
+					if (Arrays.equals(this.xid.getGlobalTransactionId(), other.xid.getGlobalTransactionId())) {
+						if (Arrays.equals(this.xid.getBranchQualifier(), other.xid.getBranchQualifier())) {
+							return true;
+						}
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	public Xid[] recover(int flag) throws XAException {
+		Xid toReturn = null;
+		if ((flag & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN) {
+			System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      RECOVER[XAResource.TMSTARTRSCAN]: " + serverId);
+			if (xid != null) {
+				toReturn = xid;
+				System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      RECOVERED: " + toReturn);
+			}
+		}
+		if ((flag & XAResource.TMENDRSCAN) == XAResource.TMENDRSCAN) {
+			System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      RECOVER[XAResource.TMENDRSCAN]: " + serverId);
+		}
+		if (flag == XAResource.TMNOFLAGS) {
+			System.out.println("[" + Thread.currentThread().getName() + "] TestResource (" + serverId + ")      RECOVER[XAResource.TMENDRSCAN]: " + serverId);
+		}
+		return new Xid[] { toReturn };
+	}
+
+	public boolean setTransactionTimeout(int seconds) throws XAException {
+		timeout = seconds;
+		return (true);
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestResourceRecovery.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestResourceRecovery.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestResourceRecovery.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,60 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.transaction.xa.XAResource;
+
+import org.jboss.tm.XAResourceRecovery;
+
+public class TestResourceRecovery implements XAResourceRecovery {
+
+	private List<TestResource> resources = new ArrayList<TestResource>();
+	private String nodeName;
+
+	public TestResourceRecovery(String nodeName) throws IOException {
+		this.nodeName = nodeName;
+		System.out.println("[" + Thread.currentThread().getName() + "] TestResourceRecovery (" + nodeName + ")");
+		File file = new File(System.getProperty("user.dir") + "/distributedjta-tests/TestResource/" + nodeName + "/");
+		if (file.exists() && file.isDirectory()) {
+			File[] listFiles = file.listFiles();
+			for (int i = 0; i < listFiles.length; i++) {
+				File currentFile = listFiles[i];
+				if (currentFile.getAbsolutePath().endsWith("_")) {
+					resources.add(new TestResource(nodeName, currentFile));
+					System.out.println("[" + Thread.currentThread().getName() + "] TestResourceRecovery (" + nodeName + ") Added TestResource: " + currentFile.getName());
+				}
+			}
+		}
+	}
+
+	@Override
+	public XAResource[] getXAResources() {
+		System.out.println("[" + Thread.currentThread().getName() + "] TestResourceRecovery (" + nodeName + ") returning list of TestResources of length: " + resources.size());
+		return resources.toArray(new XAResource[] {});
+	}
+
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestSynchronization.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestSynchronization.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/TestSynchronization.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed;
+
+import javax.transaction.Synchronization;
+
+public class TestSynchronization implements Synchronization {
+	private String serverId;
+
+	public TestSynchronization(String serverId) {
+		this.serverId = serverId;
+	}
+
+	@Override
+	public void beforeCompletion() {
+		System.out.println(" TestSynchronization (" + serverId + ")      beforeCompletion");
+	}
+
+	@Override
+	public void afterCompletion(int status) {
+		System.out.println(" TestSynchronization (" + serverId + ")      afterCompletion");
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/CompletionCounter.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/CompletionCounter.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/CompletionCounter.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,105 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class CompletionCounter {
+
+	private static CompletionCounter instance;
+
+	private Map<String, Integer> commitCounter = new HashMap<String, Integer>();
+	private Map<String, Integer> rollbackCounter = new HashMap<String, Integer>();
+
+	public static CompletionCounter getInstance() {
+		if (instance == null) {
+			instance = new CompletionCounter();
+		}
+		return instance;
+	}
+
+	protected CompletionCounter() {
+
+	}
+
+	public void incrementCommit(String nodeName) {
+		Integer integer = commitCounter.get(nodeName);
+		if (integer == null) {
+			integer = new Integer(1);
+		} else {
+			integer = new Integer(integer.intValue() + 1);
+		}
+		commitCounter.put(nodeName, integer);
+
+	}
+
+	public void incrementRollback(String nodeName) {
+		Integer integer = rollbackCounter.get(nodeName);
+		if (integer == null) {
+			integer = new Integer(1);
+		} else {
+			integer = new Integer(integer.intValue() + 1);
+		}
+		rollbackCounter.put(nodeName, integer);
+	}
+
+	public int getCommitCount(String nodeName) {
+		Integer integer = commitCounter.get(nodeName);
+		if (integer == null) {
+			integer = new Integer(0);
+		}
+		return integer;
+	}
+
+	public int getRollbackCount(String nodeName) {
+		Integer integer = rollbackCounter.get(nodeName);
+		if (integer == null) {
+			integer = new Integer(0);
+		}
+		return integer;
+	}
+
+	public int getTotalCommitCount() {
+		Integer toReturn = 0;
+		Iterator<Integer> iterator = commitCounter.values().iterator();
+		while (iterator.hasNext()) {
+			toReturn += iterator.next();
+		}
+		return toReturn;
+	}
+
+	public int getTotalRollbackCount() {
+		Integer toReturn = 0;
+		Iterator<Integer> iterator = rollbackCounter.values().iterator();
+		while (iterator.hasNext()) {
+			toReturn += iterator.next();
+		}
+		return toReturn;
+	}
+
+	public void reset() {
+		commitCounter.clear();
+		rollbackCounter.clear();
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/IsolatableServersClassLoader.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/IsolatableServersClassLoader.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/IsolatableServersClassLoader.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,101 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import sun.misc.Resource;
+import sun.misc.URLClassPath;
+
+public class IsolatableServersClassLoader extends ClassLoader {
+
+	private Map<String, Class<?>> clazzMap = new HashMap<String, Class<?>>();
+	private URLClassPath ucp;
+	private String ignoredPackage;
+	private String includedPackage;
+	private String otherIgnoredPackage;
+
+	public IsolatableServersClassLoader(String includedPackage, String ignoredPackage, ClassLoader parent) throws SecurityException, NoSuchMethodException,
+			MalformedURLException {
+		super(parent);
+		this.includedPackage = includedPackage;
+		this.otherIgnoredPackage = ignoredPackage;
+		this.ignoredPackage = IsolatableServersClassLoader.class.getPackage().getName();
+		String property = System.getProperty("java.class.path");
+		String[] split = property.split(System.getProperty("path.separator"));
+		URL[] urls = new URL[split.length];
+		for (int i = 0; i < urls.length; i++) {
+			String url = split[i];
+			if (url.endsWith(".jar")) {
+				urls[i] = new URL("jar:file:" + url + "!/");
+			} else {
+				urls[i] = new URL("file:" + url + "/");
+			}
+		}
+		this.ucp = new URLClassPath(urls);
+	}
+
+	@Override
+	protected Class<?> findClass(String name) throws ClassNotFoundException {
+		if (clazzMap.containsKey(name)) {
+			return clazzMap.get(name);
+		}
+		return super.findClass(name);
+	}
+
+	public Class<?> loadClass(String name) throws ClassNotFoundException {
+		if (!name.matches(ignoredPackage + ".[A-Za-z0-9]*") && otherIgnoredPackage != null && name.startsWith(otherIgnoredPackage)) {
+			throw new ClassNotFoundException(name);
+		}
+		Class<?> clazz = null;
+		if (clazzMap.containsKey(name)) {
+			clazz = clazzMap.get(name);
+		}
+
+		if (clazz == null) {
+			if (!name.startsWith("com.arjuna") || name.matches(ignoredPackage + ".[A-Za-z0-9]*")
+					|| (includedPackage != null && !name.startsWith(includedPackage))) {
+				clazz = getParent().loadClass(name);
+			} else {
+
+				String path = name.replace('.', '/').concat(".class");
+				Resource res = ucp.getResource(path, false);
+				if (res == null) {
+					throw new ClassNotFoundException(name);
+				}
+				try {
+					byte[] classData = res.getBytes();
+					clazz = defineClass(name, classData, 0, classData.length);
+					clazzMap.put(name, clazz);
+				} catch (IOException e) {
+					throw new ClassNotFoundException(name, e);
+				}
+			}
+
+		}
+		return clazz;
+	}
+}
\ No newline at end of file

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/LocalServer.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/LocalServer.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/LocalServer.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,69 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server;
+
+import java.io.IOException;
+
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
+
+public interface LocalServer {
+
+	public void initialise(LookupProvider lookupProvider, String nodeName, int portOffset, String[] clusterBuddies, ClassLoader classLoaderForTransactionManager)
+			throws CoreEnvironmentBeanException, IOException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException;
+
+	public ClassLoader getClassLoader();
+
+	public String getNodeName();
+
+	public TransactionManager getTransactionManager() throws NotSupportedException, SystemException;
+
+	public void doRecoveryManagerScan(boolean hackSafetyInterval);
+
+	public long getTimeLeftBeforeTransactionTimeout() throws RollbackException;
+
+	public void storeRootTransaction() throws SystemException;
+
+	public void removeRootTransaction(Xid toMigrate);
+
+	public Xid locateOrImportTransactionThenResumeIt(int remainingTimeout, Xid toImport) throws XAException, InvalidTransactionException,
+			IllegalStateException, SystemException, IOException;
+
+	public RemoteServer connectTo();
+
+	public XAResource generateProxyXAResource(String remoteServerName, Xid xid) throws SystemException, IOException;
+
+	public Synchronization generateProxySynchronization(String remoteServerName, Xid toRegisterAgainst);
+
+	public Xid getCurrentXid() throws SystemException;
+
+	public void shutdown() throws Exception;
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/LookupProvider.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/LookupProvider.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/LookupProvider.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server;
+
+public class LookupProvider {
+	private static LookupProvider instance;
+
+	private RemoteServer[] remoteServers = new RemoteServer[3];
+
+	public static LookupProvider getInstance() {
+		if (instance == null) {
+			instance = new LookupProvider();
+		}
+		return instance;
+	}
+
+	protected LookupProvider() {
+	}
+
+	public RemoteServer lookup(String jndiName) {
+		int index = (Integer.valueOf(jndiName) / 1000) - 1;
+		return remoteServers[index];
+	}
+
+	public void clear() {
+		for (int i = 0; i < remoteServers.length; i++) {
+			// Disconnect
+			remoteServers[i] = null;
+		}
+	}
+
+	public void bind(int index, RemoteServer connectTo) {
+		remoteServers[index] = connectTo;
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/RemoteServer.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/RemoteServer.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/RemoteServer.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server;
+
+import javax.transaction.SystemException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+public interface RemoteServer {
+
+	public int prepare(Xid xid, boolean recover) throws XAException;
+
+	public void commit(Xid xid, boolean onePhase, boolean recover) throws XAException;
+
+	public void rollback(Xid xid, boolean recover) throws XAException;
+
+	public void forget(Xid xid, boolean recover) throws XAException;
+
+	public void beforeCompletion(Xid xid) throws SystemException;
+
+	public Xid[] recoverFor(String localServerName) throws XAException;
+
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxySynchronization.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxySynchronization.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxySynchronization.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,57 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server.impl;
+
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.jta.distributed.server.LookupProvider;
+
+public class ProxySynchronization implements Synchronization {
+
+	private String localServerName;
+	private String remoteServerName;
+	private Xid toRegisterAgainst;
+
+	public ProxySynchronization(String localServerName, String remoteServerName, Xid toRegisterAgainst) {
+		this.localServerName = localServerName;
+		this.remoteServerName = remoteServerName;
+		this.toRegisterAgainst = toRegisterAgainst;
+	}
+
+	@Override
+	public void beforeCompletion() {
+		System.out.println("ProxySynchronization (" + localServerName + ":" + remoteServerName + ") beforeCompletion");
+		try {
+			LookupProvider.getInstance().lookup(remoteServerName).beforeCompletion(toRegisterAgainst);
+		} catch (SystemException e) {
+			// Nothing we can really do here
+			e.printStackTrace();
+		}
+	}
+
+	@Override
+	public void afterCompletion(int status) {
+		// These are not proxied but are handled during local commits
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResource.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResource.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResource.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,235 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server.impl;
+
+import java.io.Serializable;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.jboss.tm.XAResourceWrapper;
+
+import com.arjuna.ats.jta.distributed.server.CompletionCounter;
+import com.arjuna.ats.jta.distributed.server.LookupProvider;
+
+/**
+ * I chose for this class to implement XAResourceWrapper so that I can provide a
+ * name to the Transaction manager for it to store in its XID.
+ * <p>
+ * In the normal situation, a ProxyXAResource is Serialized, therefore we do not
+ * get the chance to recover the transactions in a call to
+ * XAResource::recover(), therefore the ProxyXAResource must tell the remote
+ * side when it calls each method, whether or not to attempt to recover the
+ * transaction before invoking its transactional directive.
+ */
+public class ProxyXAResource implements XAResource, XAResourceWrapper, Serializable {
+
+	private int transactionTimeout;
+	private String remoteServerName;
+	private String localServerName;
+	private transient boolean nonerecovered;
+
+	private Xid migratedXid;
+
+	/**
+	 * Create a new proxy to the remote server.
+	 * 
+	 * @param LookupProvider
+	 *            .getLookupProvider()
+	 * @param localServerName
+	 * @param remoteServerName
+	 */
+	public ProxyXAResource(String localServerName, String remoteServerName, Xid migratedXid) {
+		this.localServerName = localServerName;
+		this.remoteServerName = remoteServerName;
+		this.migratedXid = migratedXid;
+		this.nonerecovered = true;
+	}
+
+	/**
+	 * Constructor for fallback bottom up recovery.
+	 * 
+	 * @param localServerName
+	 * @param remoteServerName
+	 */
+	public ProxyXAResource(String localServerName, String remoteServerName) {
+		this.localServerName = localServerName;
+		this.remoteServerName = remoteServerName;
+	}
+
+	/**
+	 * Store the XID.
+	 */
+	@Override
+	public void start(Xid xid, int flags) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_START   [" + xid + "]");
+	}
+
+	/**
+	 * Reference the XID.
+	 */
+	@Override
+	public void end(Xid xid, int flags) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_END     [" + xid + "]");
+	}
+
+	/**
+	 * Prepare the resource, save the XID locally first, the propagate the
+	 * prepare. This ensures that in recovery we know the XID to ask a remote
+	 * server about.
+	 */
+	@Override
+	public int prepare(Xid xid) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_PREPARE [" + xid + "]");
+
+		Xid toPropagate = migratedXid != null ? migratedXid : xid;
+		int propagatePrepare = LookupProvider.getInstance().lookup(remoteServerName).prepare(toPropagate, !nonerecovered);
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_PREPARED");
+		return propagatePrepare;
+	}
+
+	@Override
+	public void commit(Xid xid, boolean onePhase) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_COMMIT  [" + xid + "]");
+
+		Xid toPropagate = migratedXid != null ? migratedXid : xid;
+		LookupProvider.getInstance().lookup(remoteServerName).commit(toPropagate, onePhase, !nonerecovered);
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_COMMITED");
+
+		CompletionCounter.getInstance().incrementCommit(localServerName);
+
+	}
+
+	@Override
+	public void rollback(Xid xid) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_ROLLBACK[" + xid + "]");
+
+		Xid toPropagate = migratedXid != null ? migratedXid : xid;
+		try {
+			LookupProvider.getInstance().lookup(remoteServerName).rollback(toPropagate, !nonerecovered);
+			System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_ROLLBACKED");
+		} catch (XAException e) {
+			// We know the remote side must have done a JBTM-927
+			if (e.errorCode == XAException.XAER_INVAL) {
+				// We know that this means that the transaction is not known at
+				// the remote side
+				CompletionCounter.getInstance().incrementRollback(localServerName);
+			}
+			throw e;
+		}
+		CompletionCounter.getInstance().incrementRollback(localServerName);
+	}
+
+	/**
+	 * This will ensure that the remote server has loaded the subordinate
+	 * transaction.
+	 * 
+	 * @return It returns the proxies view of the XID state, returning the
+	 *         remote servers view of the XID would present an XID to the local
+	 *         server that it knows nothing about and indeed potentially the
+	 *         remote server does not have a corresponding record of the XID in
+	 *         case of failure during prepare.
+	 */
+	@Override
+	public Xid[] recover(int flag) throws XAException {
+		if ((flag & XAResource.TMSTARTRSCAN) == XAResource.TMSTARTRSCAN) {
+			System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_RECOVER [XAResource.TMSTARTRSCAN]");
+		}
+		if ((flag & XAResource.TMENDRSCAN) == XAResource.TMENDRSCAN) {
+			System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_RECOVER [XAResource.TMENDRSCAN]");
+		}
+
+		Xid[] toReturn = LookupProvider.getInstance().lookup(remoteServerName).recoverFor(localServerName);
+
+		if (toReturn != null) {
+			for (int i = 0; i < toReturn.length; i++) {
+				System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_RECOVERD: " + toReturn[i]);
+			}
+		}
+		return toReturn;
+	}
+
+	@Override
+	public void forget(Xid xid) throws XAException {
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_FORGET  [" + xid + "]");
+
+		Xid toPropagate = migratedXid != null ? migratedXid : xid;
+		LookupProvider.getInstance().lookup(remoteServerName).forget(toPropagate, !nonerecovered);
+		System.out.println("[" + Thread.currentThread().getName() + "] ProxyXAResource (" + localServerName + ":" + remoteServerName + ") XA_FORGETED[" + xid + "]");
+	}
+
+	@Override
+	public int getTransactionTimeout() throws XAException {
+		return transactionTimeout;
+	}
+
+	@Override
+	public boolean setTransactionTimeout(int seconds) throws XAException {
+		this.transactionTimeout = seconds;
+		return true;
+	}
+
+	@Override
+	public boolean isSameRM(XAResource xares) throws XAException {
+		boolean toReturn = false;
+		if (xares instanceof ProxyXAResource) {
+			if (((ProxyXAResource) xares).remoteServerName == remoteServerName) {
+				toReturn = true;
+			}
+		}
+		return toReturn;
+	}
+
+	/**
+	 * Not used by the TM.
+	 */
+	@Override
+	public XAResource getResource() {
+		return null;
+	}
+
+	/**
+	 * Not used by the TM.
+	 */
+	@Override
+	public String getProductName() {
+		return null;
+	}
+
+	/**
+	 * Not used by the TM.
+	 */
+	@Override
+	public String getProductVersion() {
+		return null;
+	}
+
+	/**
+	 * This allows the proxy to contain meaningful information in the XID in
+	 * case of failure to recover.
+	 */
+	@Override
+	public String getJndiName() {
+		return "ProxyXAResource: " + localServerName + " " + remoteServerName;
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResourceDeserializer.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResourceDeserializer.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResourceDeserializer.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,26 @@
+package com.arjuna.ats.jta.distributed.server.impl;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+import javax.transaction.xa.XAResource;
+
+import com.arjuna.ats.jta.recovery.SerializableXAResourceDeserializer;
+
+public class ProxyXAResourceDeserializer implements SerializableXAResourceDeserializer {
+
+	@Override
+	public boolean canDeserialze(String className) {
+		if (className.equals(ProxyXAResource.class.getName())) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	@Override
+	public XAResource deserialze(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+		return (XAResource) ois.readObject();
+	}
+
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResourceRecovery.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResourceRecovery.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ProxyXAResourceRecovery.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.transaction.xa.XAResource;
+
+import org.jboss.tm.XAResourceRecovery;
+
+public class ProxyXAResourceRecovery implements XAResourceRecovery {
+
+	private List<ProxyXAResource> resources = new ArrayList<ProxyXAResource>();
+
+	public ProxyXAResourceRecovery(String nodeName, String[] toRecoverFor) {
+		for (int i = 0; i < toRecoverFor.length; i++) {
+			resources.add(new ProxyXAResource(nodeName, toRecoverFor[i]));
+		}
+	}
+
+	@Override
+	public XAResource[] getXAResources() {
+		return resources.toArray(new XAResource[] {});
+	}
+
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/RemoteServerImpl.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/RemoteServerImpl.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/RemoteServerImpl.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,120 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server.impl;
+
+import java.util.Arrays;
+import java.util.Set;
+
+import javax.transaction.SystemException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.SubordinationManager;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.TransactionImporterImple;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.XATerminatorImple;
+import com.arjuna.ats.jta.distributed.server.RemoteServer;
+
+public class RemoteServerImpl implements RemoteServer {
+	@Override
+	public int prepare(Xid xid, boolean recover) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			if (recover) {
+				((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(xid, null);
+			}
+			return SubordinationManager.getXATerminator().prepare(xid);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	@Override
+	public void commit(Xid xid, boolean onePhase, boolean recover) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			if (recover) {
+				((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(xid, null);
+			}
+			SubordinationManager.getXATerminator().commit(xid, onePhase);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	@Override
+	public void rollback(Xid xid, boolean recover) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			if (recover) {
+				((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(xid, null);
+			}
+			SubordinationManager.getXATerminator().rollback(xid);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	@Override
+	public void forget(Xid xid, boolean recover) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			if (recover) {
+				((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(xid, null);
+			}
+			SubordinationManager.getXATerminator().forget(xid);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+
+	}
+
+	@Override
+	public void beforeCompletion(Xid xid) throws SystemException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			((XATerminatorImple) SubordinationManager.getXATerminator()).beforeCompletion(xid);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+
+	@Override
+	public Xid[] recoverFor(String localServerName) throws XAException {
+		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+		try {
+			Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+			Set<Xid> toReturn = ((TransactionImporterImple) SubordinationManager.getTransactionImporter()).getInflightXids(localServerName);
+			Xid[] doRecover = ((XATerminatorImple) SubordinationManager.getXATerminator()).doRecover(null, localServerName);
+			if (doRecover != null) {
+				toReturn.addAll(Arrays.asList(doRecover));
+			}
+			return toReturn.toArray(new Xid[0]);
+		} finally {
+			Thread.currentThread().setContextClassLoader(contextClassLoader);
+		}
+	}
+}

Added: labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ServerImpl.java
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ServerImpl.java	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/classes/com/arjuna/ats/jta/distributed/server/impl/ServerImpl.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1,267 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt 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.arjuna.ats.jta.distributed.server.impl;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.transaction.RollbackException;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+
+import org.jboss.tm.TransactionTimeoutConfiguration;
+
+import com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean;
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBean;
+import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
+import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean;
+import com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean;
+import com.arjuna.ats.arjuna.coordinator.TransactionReaper;
+import com.arjuna.ats.arjuna.coordinator.TxControl;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+import com.arjuna.ats.arjuna.tools.osb.mbean.ObjStoreBrowser;
+import com.arjuna.ats.internal.arjuna.utils.ManualProcessId;
+import com.arjuna.ats.internal.jbossatx.jta.XAResourceRecordWrappingPluginImpl;
+import com.arjuna.ats.internal.jta.recovery.arjunacore.RecoveryXids;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.SubordinateXidImple;
+import com.arjuna.ats.internal.jta.transaction.arjunacore.jca.SubordinationManager;
+import com.arjuna.ats.jbossatx.jta.RecoveryManagerService;
+import com.arjuna.ats.jbossatx.jta.TransactionManagerService;
+import com.arjuna.ats.jta.common.JTAEnvironmentBean;
+import com.arjuna.ats.jta.distributed.TestResourceRecovery;
+import com.arjuna.ats.jta.distributed.server.LocalServer;
+import com.arjuna.ats.jta.distributed.server.LookupProvider;
+import com.arjuna.ats.jta.distributed.server.RemoteServer;
+
+public class ServerImpl implements LocalServer {
+
+	private String nodeName;
+	private RecoveryManagerService recoveryManagerService;
+	private TransactionManagerService transactionManagerService;
+	private Map<SubordinateXidImple, TransactionImple> rootTransactionsAsSubordinate = new HashMap<SubordinateXidImple, TransactionImple>();
+	private RecoveryManager _recoveryManager;
+	private ClassLoader classLoaderForTransactionManager;
+
+	public void initialise(LookupProvider lookupProvider, String nodeName, int portOffset, String[] clusterBuddies, ClassLoader classLoaderForTransactionManager)
+			throws CoreEnvironmentBeanException, IOException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
+		this.nodeName = nodeName;
+		this.classLoaderForTransactionManager = classLoaderForTransactionManager;
+
+		RecoveryEnvironmentBean recoveryEnvironmentBean = com.arjuna.ats.arjuna.common.recoveryPropertyManager.getRecoveryEnvironmentBean();
+		recoveryEnvironmentBean.setRecoveryBackoffPeriod(1);
+
+		recoveryEnvironmentBean.setRecoveryInetAddress(InetAddress.getByName("localhost"));
+		recoveryEnvironmentBean.setRecoveryPort(4712 + portOffset);
+		recoveryEnvironmentBean.setTransactionStatusManagerInetAddress(InetAddress.getByName("localhost"));
+		recoveryEnvironmentBean.setTransactionStatusManagerPort(4713 + portOffset);
+		List<String> recoveryModuleClassNames = new ArrayList<String>();
+
+		recoveryModuleClassNames.add("com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule");
+		// recoveryModuleClassNames.add("com.arjuna.ats.internal.txoj.recovery.TORecoveryModule");
+		recoveryModuleClassNames.add("com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule");
+		recoveryEnvironmentBean.setRecoveryModuleClassNames(recoveryModuleClassNames);
+		List<String> expiryScannerClassNames = new ArrayList<String>();
+		expiryScannerClassNames.add("com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner");
+		recoveryEnvironmentBean.setExpiryScannerClassNames(expiryScannerClassNames);
+		recoveryEnvironmentBean.setRecoveryActivators(null);
+
+		CoreEnvironmentBean coreEnvironmentBean = com.arjuna.ats.arjuna.common.arjPropertyManager.getCoreEnvironmentBean();
+		// coreEnvironmentBean.setSocketProcessIdPort(4714 + nodeName);
+		coreEnvironmentBean.setNodeIdentifier(nodeName);
+		// coreEnvironmentBean.setSocketProcessIdMaxPorts(1);
+		coreEnvironmentBean.setProcessImplementationClassName(ManualProcessId.class.getName());
+		coreEnvironmentBean.setPid(portOffset);
+
+		CoordinatorEnvironmentBean coordinatorEnvironmentBean = com.arjuna.ats.arjuna.common.arjPropertyManager.getCoordinatorEnvironmentBean();
+		coordinatorEnvironmentBean.setEnableStatistics(false);
+		coordinatorEnvironmentBean.setDefaultTimeout(300);
+		coordinatorEnvironmentBean.setTransactionStatusManagerEnable(false);
+		coordinatorEnvironmentBean.setDefaultTimeout(0);
+
+		ObjectStoreEnvironmentBean actionStoreObjectStoreEnvironmentBean = com.arjuna.common.internal.util.propertyservice.BeanPopulator.getNamedInstance(
+				com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.class, "default");
+		actionStoreObjectStoreEnvironmentBean.setObjectStoreDir(System.getProperty("user.dir") + "/distributedjta-tests/tx-object-store/" + nodeName);
+
+		ObjectStoreEnvironmentBean stateStoreObjectStoreEnvironmentBean = com.arjuna.common.internal.util.propertyservice.BeanPopulator.getNamedInstance(
+				com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.class, "stateStore");
+		stateStoreObjectStoreEnvironmentBean.setObjectStoreDir(System.getProperty("user.dir") + "/distributedjta-tests/tx-object-store/" + nodeName);
+
+		ObjectStoreEnvironmentBean communicationStoreObjectStoreEnvironmentBean = com.arjuna.common.internal.util.propertyservice.BeanPopulator
+				.getNamedInstance(com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean.class, "communicationStore");
+		communicationStoreObjectStoreEnvironmentBean.setObjectStoreDir(System.getProperty("user.dir") + "/distributedjta-tests/tx-object-store/" + nodeName);
+
+		JTAEnvironmentBean jTAEnvironmentBean = com.arjuna.ats.jta.common.jtaPropertyManager.getJTAEnvironmentBean();
+		jTAEnvironmentBean.setLastResourceOptimisationInterface(org.jboss.tm.LastResource.class);
+		jTAEnvironmentBean.setTransactionManagerClassName("com.arjuna.ats.jbossatx.jta.TransactionManagerDelegate");
+		jTAEnvironmentBean.setUserTransactionClassName("com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple");
+		jTAEnvironmentBean
+				.setTransactionSynchronizationRegistryClassName("com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple");
+		List<String> xaRecoveryNodes = new ArrayList<String>();
+		xaRecoveryNodes.add(nodeName);
+		jTAEnvironmentBean.setXaRecoveryNodes(xaRecoveryNodes);
+
+		List<String> xaResourceOrphanFilterClassNames = new ArrayList<String>();
+
+		xaResourceOrphanFilterClassNames.add("com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter");
+		xaResourceOrphanFilterClassNames.add("com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter");
+		xaResourceOrphanFilterClassNames.add("com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinateJTAXAResourceOrphanFilter");
+		jTAEnvironmentBean.setXaResourceOrphanFilterClassNames(xaResourceOrphanFilterClassNames);
+		jTAEnvironmentBean.setXAResourceRecordWrappingPlugin(new XAResourceRecordWrappingPluginImpl());
+
+		recoveryManagerService = new RecoveryManagerService();
+		recoveryManagerService.create();
+		recoveryManagerService.addXAResourceRecovery(new TestResourceRecovery(nodeName));
+		// This MUST be the last XAResourceRecovery class registered or you will
+		// get unexpected recovery results, could add a specific interface for
+		// this?
+		recoveryManagerService.addXAResourceRecovery(new ProxyXAResourceRecovery(nodeName, clusterBuddies));
+		recoveryManagerService.addSerializableXAResourceDeserializer(new ProxyXAResourceDeserializer());
+
+		// recoveryManagerService.start();
+		_recoveryManager = RecoveryManager.manager();
+		RecoveryManager.manager().initialize();
+
+		transactionManagerService = new TransactionManagerService();
+		TxControl txControl = new com.arjuna.ats.arjuna.coordinator.TxControl();
+		transactionManagerService.setJbossXATerminator(new com.arjuna.ats.internal.jbossatx.jta.jca.XATerminator());
+		transactionManagerService
+				.setTransactionSynchronizationRegistry(new com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple());
+		transactionManagerService.create();
+	}
+
+	@Override
+	public ClassLoader getClassLoader() {
+		return classLoaderForTransactionManager;
+	}
+
+	@Override
+	public void shutdown() throws Exception {
+		recoveryManagerService.stop();
+		TransactionReaper.transactionReaper().terminate(false);
+	}
+
+	@Override
+	public void doRecoveryManagerScan(boolean hackSafetyInterval) {
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+		Thread.currentThread().setContextClassLoader(getClassLoader());
+		int originalSafetyInterval = -1;
+
+		if (hackSafetyInterval) {
+			try {
+				Field safetyIntervalMillis = RecoveryXids.class.getDeclaredField("safetyIntervalMillis");
+				safetyIntervalMillis.setAccessible(true);
+				originalSafetyInterval = (Integer) safetyIntervalMillis.get(null);
+				safetyIntervalMillis.set(null, 0);
+			} catch (Throwable t) {
+				t.printStackTrace();
+			}
+		}
+
+		_recoveryManager.scan();
+
+		if (hackSafetyInterval) {
+			try {
+				Field safetyIntervalMillis = RecoveryXids.class.getDeclaredField("safetyIntervalMillis");
+				safetyIntervalMillis.setAccessible(true);
+				safetyIntervalMillis.set(null, originalSafetyInterval);
+			} catch (Throwable t) {
+				t.printStackTrace();
+			}
+		}
+		Thread.currentThread().setContextClassLoader(classLoader);
+	}
+
+	@Override
+	public TransactionManager getTransactionManager() {
+		return transactionManagerService.getTransactionManager();
+	}
+
+	@Override
+	public Xid locateOrImportTransactionThenResumeIt(int remainingTimeout, Xid toResume) throws XAException, IllegalStateException, SystemException,
+			IOException {
+		Xid toReturn = null;
+		Transaction transaction = rootTransactionsAsSubordinate.get(new SubordinateXidImple(toResume));
+		if (transaction == null) {
+			transaction = SubordinationManager.getTransactionImporter().getImportedTransaction(toResume);
+			if (transaction == null) {
+				transaction = SubordinationManager.getTransactionImporter().importTransaction(toResume, remainingTimeout);
+				toReturn = ((TransactionImple) transaction).getTxId();
+			}
+		}
+		transactionManagerService.getTransactionManager().resume(transaction);
+		return toReturn;
+	}
+
+	@Override
+	public String getNodeName() {
+		return nodeName;
+	}
+
+	@Override
+	public long getTimeLeftBeforeTransactionTimeout() throws RollbackException {
+		return ((TransactionTimeoutConfiguration) transactionManagerService.getTransactionManager()).getTimeLeftBeforeTransactionTimeout(true);
+	}
+
+	@Override
+	public void storeRootTransaction() throws SystemException {
+		TransactionImple transaction = ((TransactionImple) transactionManagerService.getTransactionManager().getTransaction());
+		Xid txId = transaction.getTxId();
+		rootTransactionsAsSubordinate.put(new SubordinateXidImple(txId), transaction);
+	}
+
+	@Override
+	public Xid getCurrentXid() throws SystemException {
+		TransactionImple transaction = ((TransactionImple) transactionManagerService.getTransactionManager().getTransaction());
+		return transaction.getTxId();
+	}
+
+	@Override
+	public void removeRootTransaction(Xid toMigrate) {
+		rootTransactionsAsSubordinate.remove(new SubordinateXidImple(toMigrate));
+	}
+
+	@Override
+	public ProxyXAResource generateProxyXAResource(String remoteServerName, Xid migratedXid) throws SystemException, IOException {
+		return new ProxyXAResource(nodeName, remoteServerName, migratedXid);
+	}
+
+	@Override
+	public Synchronization generateProxySynchronization(String remoteServerName, Xid toRegisterAgainst) {
+		return new ProxySynchronization(nodeName, remoteServerName, toRegisterAgainst);
+	}
+
+	@Override
+	public RemoteServer connectTo() {
+		return new RemoteServerImpl();
+	}
+
+}

Added: labs/jbosstm/trunk/atsintegration/tests/resources/jbossts-properties.xml
===================================================================
--- labs/jbosstm/trunk/atsintegration/tests/resources/jbossts-properties.xml	                        (rev 0)
+++ labs/jbosstm/trunk/atsintegration/tests/resources/jbossts-properties.xml	2012-01-04 16:33:42 UTC (rev 37829)
@@ -0,0 +1 @@
+<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"><properties/>
\ No newline at end of file

Modified: labs/jbosstm/trunk/common/classes/com/arjuna/common/util/ConfigurationInfo.java
===================================================================
--- labs/jbosstm/trunk/common/classes/com/arjuna/common/util/ConfigurationInfo.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/common/classes/com/arjuna/common/util/ConfigurationInfo.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -123,7 +123,7 @@
             }
 
         } catch(Exception exception) {
-            // This is OK, it means we will run with defaults exception.printStackTrace();
+            exception.printStackTrace();
         } finally {
             if(is!= null) {
                 try {

Modified: labs/jbosstm/trunk/common/classes/com/arjuna/common/util/propertyservice/PropertiesFactory.java
===================================================================
--- labs/jbosstm/trunk/common/classes/com/arjuna/common/util/propertyservice/PropertiesFactory.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/common/classes/com/arjuna/common/util/propertyservice/PropertiesFactory.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -80,8 +80,7 @@
             // in cases where the .jar comes before the etc dir on the classpath.
             URL url = PropertiesFactory.class.getResource("/default-"+propertyFileName);
             if(url == null) {
-                // No properties file this is OK, we will run with defaults 
-            	// throw new RuntimeException("missing property file "+propertyFileName);
+                throw new RuntimeException("missing property file "+propertyFileName);
             } else {
                 propertiesSourceUri = url.toString();
             }
@@ -93,18 +92,13 @@
 
         Properties properties = null;
 
-		if (propertiesSourceUri != null) {
-			try {
-				properties = loadFromFile(propertiesSourceUri);
-				properties = applySystemProperties(properties);
+        try {
+            properties = loadFromFile(propertiesSourceUri);
+            properties = applySystemProperties(properties);
 
-			} catch (Exception e) {
-				throw new RuntimeException("unable to load properties from "
-						+ propertiesSourceUri, e);
-			}
-		} else {
-			properties = new Properties();
-		}
+        } catch(Exception e) {
+            throw new RuntimeException("unable to load properties from "+propertiesSourceUri, e);
+        }
 
         return properties;
     }

Modified: labs/jbosstm/trunk/common/tests/com/arjuna/common/tests/simple/EnvironmentBeanTest.java
===================================================================
--- labs/jbosstm/trunk/common/tests/com/arjuna/common/tests/simple/EnvironmentBeanTest.java	2012-01-03 20:28:44 UTC (rev 37828)
+++ labs/jbosstm/trunk/common/tests/com/arjuna/common/tests/simple/EnvironmentBeanTest.java	2012-01-04 16:33:42 UTC (rev 37829)
@@ -105,7 +105,7 @@
         } else {
 
             inputValue = new ArrayList<String>();
-            ((List)inputValue).add("testValue");
+            ((List)inputValue).add("1");
             
         }
 



More information about the jboss-svn-commits mailing list