[jboss-svn-commits] JBL Code SVN: r24123 - in labs/jbosstm/workspace: resttx and 24 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Thu Nov 27 11:37:30 EST 2008


Author: mmusgrov
Date: 2008-11-27 11:37:30 -0500 (Thu, 27 Nov 2008)
New Revision: 24123

Added:
   labs/jbosstm/workspace/resttx/
   labs/jbosstm/workspace/resttx/build.xml
   labs/jbosstm/workspace/resttx/docs/
   labs/jbosstm/workspace/resttx/docs/readme.txt
   labs/jbosstm/workspace/resttx/docs/spec.txt
   labs/jbosstm/workspace/resttx/jbossjta-properties.xml
   labs/jbosstm/workspace/resttx/pom.xml
   labs/jbosstm/workspace/resttx/src/
   labs/jbosstm/workspace/resttx/src/main/
   labs/jbosstm/workspace/resttx/src/main/java/
   labs/jbosstm/workspace/resttx/src/main/java/org/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/client/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/client/TxTest.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/Participant.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/Work.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/HttpResponseException.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/NotFoundMapper.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/ResourceNotFoundException.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TMUnavailableException.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TMUnavailableMapper.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TransactionStatusException.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TransactionStatusMapper.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/RESTRecord.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/RESTRecordSetup.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/Transaction.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/TransactionList.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/Coordinator.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/TMApplication.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/TransactionalParticipant.java
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/util/
   labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/util/TxUtil.java
   labs/jbosstm/workspace/resttx/src/main/webapp/
   labs/jbosstm/workspace/resttx/src/main/webapp/WEB-INF/
   labs/jbosstm/workspace/resttx/src/main/webapp/WEB-INF/web.xml
   labs/jbosstm/workspace/resttx/src/main/webapp/tx.html
   labs/jbosstm/workspace/resttx/src/test/
   labs/jbosstm/workspace/resttx/src/test/java/
   labs/jbosstm/workspace/resttx/src/test/java/org/
   labs/jbosstm/workspace/resttx/src/test/java/org/jboss/
   labs/jbosstm/workspace/resttx/src/test/java/org/jboss/jbossts/
   labs/jbosstm/workspace/resttx/src/test/java/org/jboss/jbossts/rts/
   labs/jbosstm/workspace/resttx/src/test/java/org/jboss/jbossts/rts/test/
   labs/jbosstm/workspace/resttx/src/test/java/org/jboss/jbossts/rts/test/AppTest.java
   labs/jbosstm/workspace/resttx/src/test/resources/
   labs/jbosstm/workspace/resttx/src/test/resources/log4j.xml
Log:
first commit

Added: labs/jbosstm/workspace/resttx/build.xml
===================================================================
--- labs/jbosstm/workspace/resttx/build.xml	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/build.xml	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+
+<project name="REST" default="run" basedir=".">
+
+  <property name="build.classes.dir" value="target/classes/"/>
+  <property name="xsrc.dir" value="src"/>
+  <property name="src.dir" value="src/main/java/org/jboss/jbossts/rts/client/"/>
+
+  <property name="resteasy.dir" value="${basedir}/target/rest-tx/WEB-INF/lib"/>
+
+  <path id="classpath">
+    <pathelement location="${build.classes.dir}"/>
+    <pathelement location="target/rest-tx/WEB-INF/lib/junit-4.5.jar"/>
+    <pathelement location="${basedir}/target/rest-tx/WEB-INF/lib/log4j-1.2.12.jar"/>
+    <pathelement location="${basedir}/target/rest-tx/WEB-INF/lib/commons-logging-1.1.jar"/>
+    <pathelement location="${basedir}/target/test-classes"/>
+    <fileset dir="${resteasy.dir}" >
+        <include name="jaxrs-api-1.0-beta-8.jar"/>
+        <include name="resteasy-jaxrs-1.0-beta-8.jar"/>
+        <include name="commons-httpclient-3.1.jar"/>
+        <include name="commons-logging-1.0.4.jar"/>
+        <include name="commons-codec-1.2.jar"/>
+    </fileset>
+   </path>
+   
+    <target name="compile">
+        <javac srcdir="${src.dir}"
+               destdir="${build.classes.dir}"
+               debug="on"
+               deprecation="on"
+               optimize="off"
+               includes="**">
+            <classpath refid="classpath"/>
+        </javac>
+    </target>
+    <target name="classes" depends="compile" />
+
+  <target name="run" depends="compile">
+      <java classname="org.jboss.jbossts.rts.client.TxTest" fork="yes" dir=".">
+          <classpath refid="classpath"/>
+          <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5006" />
+          <jvmarg value="-Xms128m" />
+          <jvmarg value="-Xmx512M" />
+      </java>
+  </target>
+</project>

Added: labs/jbosstm/workspace/resttx/docs/readme.txt
===================================================================
--- labs/jbosstm/workspace/resttx/docs/readme.txt	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/docs/readme.txt	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,19 @@
+This project is work in progress.
+
+mvn test # run tests
+mvn jetty6:run-exploded # run the coordinator and particpants
+ant # runs a client of the coordinator
+
+To debug the code:
+
+export MAVEN_OPTS="-Xdebug -Xnoagent
+-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5001
+-Dmaven.test.skip=true"
+
+mvn -DforkMode=none test 
+
+To integrate with JBossTM set the following property:
+
+<property name="com.arjuna.ats.internal.arjuna.inventory.staticInventoryImple.RESTRecord"
+          value="org.jboss.jbossts.rts.resource.RESTRecordSetup" />
+

Added: labs/jbosstm/workspace/resttx/docs/spec.txt
===================================================================
--- labs/jbosstm/workspace/resttx/docs/spec.txt	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/docs/spec.txt	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,186 @@
+
+The OASIS WS-CAF/WS-TXM Business Process model was the one we were
+aiming for RESTful transactions in "the large", but we started with an
+ACID (2PC) model for simplicity. I haven't found the implementation
+notes for BP yet, but will keep looking. In the meantime, here is what
+we had for 2PC (remember it was influenced by JTS at the time):
+
+Communication is initiated by the client in the form of HTTP GET,
+DELETE, POST, and PUT requests. GET is used to read data, DELETE is used
+to delete data, POST is used to create new data, and PUT is used to
+modify existing data. The server responds to requests by returning
+status codes, data, or both. Because REST transactions are stateless,
+each HTTP request must be accompanied by a URI that uniquely identifies
+the specific resource being requested. In addition to a URI, POST and
+PUT requests must be accompanied by an XML document containing data that
+conforms to the schema.
+
+Different HTTP status codes (and data) are returned by the server in
+response to requests:
+
+GET
+A successful GET returns status code 200 (OK) and XML data in the body.
+PUT
+A successful PUT returns status code 200 (OK) and XML data in the body.
+POST
+A successful POST returns status code 201 (created) and a Location
+header containing the URI of the newly created resource.
+DELETE
+A successful DELETE returns status code 204 only (no data).
+
+Error status codes include:
+
+400: Bad request
+The request could not be understood by the server due to malformed syntax.
+401: Unauthorized
+Invalid username and/or password, or insufficient privileges for the
+command.
+404: Not found
+Couldn't find the resource specified by the URI.
+410: Gone
+The requested resource is no longer available at the server and no
+forwarding address is known.
+
+Although POST is "unsafe" and we should use PUT because it is
+idempotent, it's not well supported by browsers.
+
+The transaction coordinator URL is:
+
+http://<machine>/transaction-coordinator
+
+Performing a GET on that URL returns a list of all transactions know to
+the coordinator (active and recovery).
+
+Performing a GET on /transaction-coordinator/recovery returns a list of
+transactions that are in recovery.
+
+Performing a GET on /transaction-coordinator/active returns a list of
+inflight transaction ids, which can the be used below.
+
+Performing a DELETE on any of the transaction-coordinator URLs will
+return a 401.
+
+Each client is expected to have a unique identity. We'll call that
+ClientID, which can be a URL too.
+
+Performing a POST on transaction-coordinator/begin?<ClientID> will start
+a new transaction with a default timeout and return a URL of the form
+<machine>/transaction-coordinator/<id>
+Performing a POST on transaction-coordinator/begin?<ClientID>#timeout
+will start a new transaction with the specified timeout and return a URL
+of the form <machine>/transaction-coordinator/<id>
+
+If the transaction is terminated because of a timeout, the
+/transaction-coordinator/<id> URL is deleted. All further invocations on
+the URL will return 404. The invoker can assume this was a rollback.
+
+Performing a PUT on transaction-coordinator/<id>/commit will trigger the
+commit of the transaction. Upon termination, the URL is implicitly
+deleted. If it no longer exists, then 404 will be returned. The invoker
+cannot know for sure whether the transaction committed or rolled back.
+This is the same problem with JTS: to be sure you should register a 2PC
+participant.
+**If the commit resulted in a heuristic outcome then performing a get on the
+/transaction-coordinator/<id> URL will return the transaction status.
+
+Each transaction is also uniquely identified.
+
+When a participant registers with a transaction we need to return a
+unique handle (aka RecoveryCoordinator) so that it can be uniquely
+reasoned about later. So we use PUT:
+
+Performing a PUT on /transactions-coordinator/<TxId> with the URL of the
+participant (see below), will register the participant in the
+transaction and also return a unique resource reference for that
+participant:
+
+http://<machine>/recovery-coordinator/<RecCoordId>
+
+Performing a GET on that URL will return the original participant URL.
+Performing a PUT on that URL will overwrite the old participant URL with
+the new one supplied. As with JTS, this will also trigger off a recovery
+attempt on the associated transaction.
+
+Performing a DELETE or POST will return a 401.
+
+When making an invocation on a resource that needs to participate in a
+transaction, the transaction context (URL) needs to be transmitted to
+the resource. How this happens is outside the scope of this effort. It
+may occur as additional payload on the initial request, or it may be
+that the client sends the context out-of-band to the resource.
+
+Once a resource has the transaction URL, it can register participation
+in the transaction. The participant is free to use whatever URL
+structure it desires for uniquely identifying itself. The resource
+(participant) URL must be unique for the transaction as well, i.e., same
+limitation as OTS::Resource: the same participant cannot be involved in
+more than one transaction. The resource must support the following
+operations:
+
+Performing a GET on the participant URL will return the current status
+of the participant, or 404 if the participant is no longer present.
+
+Performing a POST on <URL>/prepare will cause the participant to prepare
+the (implicitly) associated transaction. It will return a URL if
+successful, which indicates the outcome. That URL can be probed (via
+GET) and will simply return the same (implicit) information:
+
+<URL>/prepare-ok
+<URL>/prepare-readonly
+<URL>/prepare-notok
+
+Note, read-only could be modeled as a DELETE request from the
+participant to the coordinator, same as BTP.
+
+404 will be returned if the participant is not available and can be
+assumed to have rolled back.
+
+Performing a GET on <URL>/prepare will return 400.
+
+Performing a PUT on <URL>/prepare will return 401.
+
+Performing a POST on <URL>/commit will cause the participant to commit
+on behalf of the transaction. It will return a URL if successful, which
+indicates the outcome. That URL can be probed (via GET) and will simply
+return the same (implicit) information:
+
+<URL>/committed
+<URL>/rolledback
+<URL>/heuristic<blah>
+
+These URLs remain available for an implementation specific period of time.
+
+Performing a GET on <URL>/commit will return 400.
+
+Performing a PUT on <URL>/commit will return 401.
+
+Performing a POST when the transaction has terminated will return a 404.
+
+Performing a POST on <URL>/rollback will cause the participant to roll
+back on behalf of the transaction. It will return a URL if successful,
+which indicates the outcome. That URL can be probed (via GET) and will
+simply return the same (implicit) information:
+
+<URL>/rolledback
+<URL>/heuristic<blah>
+
+Performing a GET on <URL>/rollback will return 400.
+
+Performing a PUT on <URL>/rollback will return 401.
+
+Performing a POST when the transaction has terminated will return a 404.
+
+The usual rules of heuristic decisions apply here (i.e., participant
+can't forget the choice until it is told to).
+
+Performing a POST on <URL>/forget will cause the participant to forget
+any heuristic decision it made on behalf of the transaction.
+
+Performing a GET on <URL>/forget will return an indication of the
+heuristic if it still exists, or 404 if it does not.
+
+Performing a PUT on <URL>/forget will return a 401.
+
+We haven't covered Synchronizations. We also haven't covered one-phase
+commit.
+

Added: labs/jbosstm/workspace/resttx/jbossjta-properties.xml
===================================================================
--- labs/jbosstm/workspace/resttx/jbossjta-properties.xml	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/jbossjta-properties.xml	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<transaction-service>
+    <properties depends="common" name="arjuna">
+        <property
+            name="com.arjuna.ats.internal.arjuna.inventory.staticInventoryImple.RESTRecord" value="org.jboss.jbossts.rts.resource.RESTRecordSetup" />
+        <!--
+        Transaction Reaper Timeout (default is 120000 ms).
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.coordinator.txReaperTimeout" value="120000"/>
+        <!--
+        Transaction Reaper Mode, can be: PERIODIC or DYNAMIC. Default is DYNAMIC.
+      -->
+        <property name="com.arjuna.ats.arjuna.coordinator.txReaperMode" value="DYNAMIC"/>
+        <!--
+        Transaction Reaper Cancel Wait Period (default is 500 ms, min is 10 msecs).
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.coordinator.txReaperCancelWaitPeriod" value="500"/>
+        <!--
+        Transaction Reaper Cancel Fail Wait Period (default is 500 ms, min is 10 msecs).
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.coordinator.txReaperCancelFailWaitPeriod" value="500"/>
+        <!--
+        Transaction Reaper Zombie Max (default is 8).
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.coordinator.txReaperZombieMax" value="8"/>
+        <!--
+        (default is NO)
+      -->
+        <property name="com.arjuna.ats.arjuna.coordinator.asyncCommit" value="NO"/>
+        <!--
+        (default is NO)
+      -->
+        <property name="com.arjuna.ats.arjuna.coordinator.asyncPrepare" value="NO"/>
+        <!--
+        (default is YES)
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.coordinator.commitOnePhase" value="YES"/>
+        <!--
+        (default is defaultStore)
+      -->
+        <property name="com.arjuna.ats.arjuna.objectstore.localOSRoot" value="defaultStore"/>
+        <!--
+        default is under user.home - must be writeable!)
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.objectstore.objectStoreDir" value="PutObjectStoreHere" />
+        <property name="com.arjuna.ats.arjuna.coordinator.transactionLog" value="OFF"/>
+        <!--
+        (default is ON)
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.objectstore.objectStoreSync" value="ON"/>
+        <!--
+        (default is ShadowNoFileLockStore)
+      -->
+        <property name="com.arjuna.ats.arjuna.objectstore.objectStoreType" value="ShadowNoFileLockStore"/>
+        <!--
+        (default is 255)
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.objectstore.hashedDirectories" value="255"/>
+        <!--
+        (default is ON)
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.objectstore.transactionSync" value="ON"/>
+        <!--
+        (Must be unique across all Arjuna instances.)
+      -->
+        <property name="com.arjuna.ats.arjuna.xa.nodeIdentifier" value="1"/>
+        <!--
+        Base port number for determining a unique number to associate with an instance of the transaction service
+        (which is needed in order to support multiple instances on the same machine).
+        Use the value 0 to allow the system to select the first available port number.
+        If the port number is non-zero and the port is in use then the value will be incremented until either a successful binding
+        to the loopback address is created or until the the maximum number of ports (specified by the
+        com.arjuna.ats.internal.arjuna.utils.SocketProcessIdMaxPorts property) have been tried or until the port number
+        reaches the maximum possible port number.
+      -->
+        <property
+            name="com.arjuna.ats.internal.arjuna.utils.SocketProcessIdPort" value="0"/>
+        <!--
+        The maximum number of ports to try starting from the value specified by the property
+        com.arjuna.ats.internal.arjuna.utils.SocketProcessIdPort. Any non-numeric or value less than 1 will
+        defautl to 1.
+      -->
+        <property
+            name="com.arjuna.ats.internal.arjuna.utils.SocketProcessIdMaxPorts" value="1"/>
+        <!--
+        Run the TransactionStatusManager to allow out-of-process recovery managers to query
+        the status of transactions owned by this coordinator.  Default is YES.
+        This can be set to NO in cases where an ObjectStore is used only by one transaction manager
+        and the recovery manager for that store is in the same JVM. In any other cases disabling the
+        TransactionStatusManager may cause crash recovery to misbehave.
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.coordinator.transactionStatusManagerEnable" value="NO"/>
+        <!-- property
+        name="com.arjuna.ats.arjuna.coordinator.actionStore"
+		value="HashedActionStore"
+		value="JDBCActionStore"
+      -->
+        <!-- property
+        name="com.arjuna.ats.arjuna.objectstore.jdbcTxDbAccess"
+		value="JDBCAccess"
+      -->
+        <!-- property
+        name="com.arjuna.ats.arjuna.objectstore.objectStoreType"
+		value="ShadowNoFileLockStore"
+		value="JDBCStore"
+      -->
+        <!-- property
+        name="com.arjuna.ats.arjuna.objectstore.jdbcUserDbAccess"
+		value="JDBCAccess"
+      -->
+        <!-- property
+        name="com.arjuna.ats.arjuna.objectstore.jdbcPoolSizeInitial"
+		value="1"
+      -->
+        <!-- property
+        name="com.arjuna.ats.arjuna.objectstore.jdbcPoolSizeMaximum"
+		value="1"
+      -->
+        <!-- property
+        name="com.arjuna.ats.arjuna.objectstore.jdbcPoolPutConnections"
+		value="false"
+      -->
+        <!-- property
+        name="com.arjuna.ats.arjuna.internal.arjuna.objectstore.cacheStore.size"
+		value=""
+      -->
+        <!-- property
+        name="com.arjuna.ats.arjuna.internal.arjuna.objectstore.cacheStore.period"
+		value=""
+      -->
+        <!--
+        The location for creating temporary files, e.g., Uids.
+        Default is under user.home.
+        IMPORTANT: make sure the directory is lockable, e.g., /tmp on Unix
+        may not be!
+      -->
+        <!--
+        <property
+          name="com.arjuna.ats.arjuna.common.varDir"
+          value="var"/>
+      -->
+    </properties>
+    <properties name="common">
+        <!-- CLF 2.0 properties -->
+        <property name="com.arjuna.common.util.logging.DebugLevel"
+            type="System" value="0x00000000"/>
+        <property name="com.arjuna.common.util.logging.FacilityLevel"
+            type="System" value="0xffffffff"/>
+        <property name="com.arjuna.common.util.logging.VisibilityLevel"
+            type="System" value="0xffffffff"/>
+        <property name="com.arjuna.common.util.logger" type="System" value="log4j"/>
+    </properties>
+    <properties depends="arjuna" name="txoj">
+        <!--
+        (default is LockStore of installation - must be writeable!)
+      -->
+        <!--
+        <property
+          name="com.arjuna.ats.txoj.lockstore.lockStoreDir"
+          value="LockStore"/>
+      -->
+        <!--
+        (default is BasicLockStore)
+      -->
+        <property name="com.arjuna.ats.txoj.lockstore.lockStoreType" value="BasicLockStore"/>
+        <!--
+        (default is NO)
+      -->
+        <property name="com.arjuna.ats.txoj.lockstore.multipleLockStore" value="NO"/>
+        <!--
+        (default is YES)
+      -->
+        <property name="com.arjuna.ats.txoj.lockstore.singleLockStore" value="YES"/>
+        <!--
+        (default is YES)
+      -->
+        <property
+            name="com.arjuna.ats.txoj.lockstore.allowNestedLocking" value="YES"/>
+    </properties>
+    <properties depends="arjuna" name="jta">
+        <!--
+        Support subtransactions in the JTA layer?
+        Default is NO.
+      -->
+        <property name="com.arjuna.ats.jta.supportSubtransactions" value="NO"/>
+        <property name="com.arjuna.ats.jta.jtaTMImplementation" value="com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple"/>
+        <!--
+			com.arjuna.ats.internal.jta.transaction.jts.TransactionManagerImple
+			-->
+        <property name="com.arjuna.ats.jta.jtaUTImplementation" value="com.arjuna.ats.internal.jta.transaction.arjunacore.UserTransactionImple"/>
+        <!--
+			com.arjuna.ats.internal.jta.transaction.jts.UserTransactionImple
+			-->
+    </properties>
+    <properties depends="arjuna,txoj,jta" name="recoverymanager">
+        <!--
+        Properties used only by the RecoveryManager.
+      -->
+        <!--
+        Periodic recovery settings.
+        Time values in this section are in seconds.
+      -->
+        <!--
+        Interval in seconds between initiating the periodic recovery modules.
+        Default is 120 seconds.
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.recovery.periodicRecoveryPeriod" value="120"/>
+        <!--
+        Interval in seconds between first and second pass of periodic recovery.
+        Default is 10 seconds.
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.recovery.recoveryBackoffPeriod" value="10"/>
+        <!--
+        The port number on which the recovery manager listens.
+      -->
+        <property name="com.arjuna.ats.arjuna.recovery.recoveryPort" value="4712"/>
+        <!--
+        The address on which the recovery manager listens.
+        If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+      -->
+        <property name="com.arjuna.ats.arjuna.recovery.recoveryAddress" value=""/>
+        <!--
+        Periodic recovery modules to use.  Invoked in sort-order of names.
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.recovery.recoveryExtension1" value="com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule"/>
+        <property
+            name="com.arjuna.ats.arjuna.recovery.recoveryExtension2" value="com.arjuna.ats.internal.txoj.recovery.TORecoveryModule"/>
+        <property
+            name="com.arjuna.ats.arjuna.recovery.recoveryExtension3" value="com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule"/>
+        <!--
+        Expired entry removal
+      -->
+        <!--
+        Expiry scanners to use (order of invocation is random).
+        Names must begin with "com.arjuna.ats.arjuna.recovery.expiryScanner"
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.recovery.expiryScannerTransactionStatusManager" value="com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner"/>
+        <!--
+        Interval, in hours, between running the expiry scanners.
+        This can be quite long. The absolute value determines the interval -
+        if the value is negative, the scan will NOT be run until after one
+        interval has elapsed. If positive the first scan will be immediately
+        after startup. Zero will prevent any scanning.
+        Default  = 12 = run immediately, then every 12 hours.
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.recovery.expiryScanInterval" value="12"/>
+        <!--
+        This is the interval, in hours, after which a process that cannot be contacted will be considered dead.
+        It should be long enough to avoid accidentally removing valid entries due to short lived
+        transient errors such as network downtime. Zero = Never removed.  Default is 12.
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerExpiryTime" value="12"/>
+        <!--
+        Use this to fix the port on which the TransactionStatusManager listens,
+        The default behaviour is to use any free port.
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerPort" value="0"/>
+        <!--
+        Use this to fix the address on which the TransactionStatusManager binds,
+        The default behaviour is to use the loopback address (ie localhost).
+        If running within an AS then the address the AS is bound to (jboss.bind.address) takes precedence
+      -->
+        <property
+            name="com.arjuna.ats.arjuna.recovery.transactionStatusManagerAddress" value=""/>
+        <!--
+          For cases where the recovery manager is in process with the transaction manager and nothing else uses
+          the ObjectStore, it is possible to disable the socket based recovery listener by setting this to NO.
+          Caution: use of this property can allow multiple recovery processes to run on the same ObjectStore
+          if you are not careful. That in turn can lead to incorrect transaction processing. Use with care.
+        -->
+        <property name="com.arjuna.ats.arjuna.recovery.recoveryListener" value="NO"/>
+    </properties>
+    <properties depends="jta" name="jdbc">
+        <!--
+           property name="com.arjuna.ats.jdbc.isolationLevel" value="TRANSACTION_SERIALIZABLE"/>
+        -->
+    </properties>
+</transaction-service>

Added: labs/jbosstm/workspace/resttx/pom.xml
===================================================================
--- labs/jbosstm/workspace/resttx/pom.xml	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/pom.xml	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+   <modelVersion>4.0.0</modelVersion>
+   <groupId>org.jboss.jbossts.rts</groupId>
+   <artifactId>rest-tx</artifactId>
+   <version>0.1</version>
+   <packaging>war</packaging>
+   <name/>
+   <description/>
+
+   <repositories>
+      <repository>
+         <id>java.net</id>
+         <url>http://download.java.net/maven/1</url>
+         <layout>legacy</layout>
+      </repository>
+      <repository>
+         <id>maven repo</id>
+         <name>maven repo</name>
+         <url>http://repo1.maven.org/maven2/</url>
+      </repository>
+      <!-- For resteasy -->
+      <repository>
+         <id>jboss</id>
+         <name>jboss repo</name>
+         <url>http://repository.jboss.org/maven2</url>
+      </repository>
+      <repository>
+         <id>scannotation</id>
+         <url>http://scannotation.sf.net/maven2</url>
+      </repository>
+   </repositories>
+
+   <dependencies>
+      <dependency>
+        <groupId>jboss.jbossts</groupId>
+        <artifactId>jbossjts</artifactId>
+        <version>4.4.0.GA</version>
+      </dependency>
+    <dependency>
+      <groupId>jboss.jbossts</groupId>
+      <artifactId>jbossts-common</artifactId>
+      <version>4.4.0.GA</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+      <version>1.1</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-httpclient</groupId>
+      <artifactId>commons-httpclient</artifactId>
+      <version>3.1</version>
+    </dependency>
+<!--
+    <dependency>
+      <groupId>apache-log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.14</version>
+    </dependency>
+-->
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.5</version>
+    </dependency>
+
+      <dependency>
+         <groupId>org.jboss.resteasy</groupId>
+         <artifactId>resteasy-jaxrs</artifactId>
+         <version>1.0-beta-8</version>
+
+         <!-- filter out unwanted jars -->
+         <exclusions>
+            <exclusion>
+               <groupId>commons-httpclient</groupId>
+               <artifactId>commons-httpclient</artifactId>
+            </exclusion>
+<!--
+            <exclusion>
+               <groupId>tjws</groupId>
+               <artifactId>webserver</artifactId>
+            </exclusion>
+-->
+            <exclusion>
+               <groupId>javax.servlet</groupId>
+               <artifactId>servlet-api</artifactId>
+            </exclusion>
+         </exclusions>
+      </dependency>
+   </dependencies>
+
+   <build>
+      <finalName>rest-tx</finalName>
+      <plugins>
+         <plugin>
+            <groupId>org.mortbay.jetty</groupId>
+            <artifactId>maven-jetty6-plugin</artifactId>
+            <version>6.0.0beta17</version>
+            <configuration>
+               <!-- By default the artifactId is taken, override it with something simple -->
+               <contextPath>/</contextPath>
+               <scanIntervalSeconds>2</scanIntervalSeconds>
+               <connectors>
+                  <connector
+                          implementation="org.mortbay.jetty.nio.SelectChannelConnector">
+                     <port>9095</port>
+                     <host>localhost</host>
+                     <maxIdleTime>60000</maxIdleTime>
+                  </connector>
+               </connectors>
+               <systemProperties>
+                  <systemProperty>
+                     <name>log4j.configuration</name>
+                     <value>file:./src/test/resources/log4j.xml</value>
+                  </systemProperty>
+                  <systemProperty>
+                     <name>com.arjuna.ats.arjuna.common.propertiesFile</name>
+                     <value>jbossjta-properties.xml</value>
+                  </systemProperty>
+               </systemProperties>
+            </configuration>
+         </plugin>
+         <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-compiler-plugin</artifactId>
+            <configuration>
+               <source>1.5</source>
+               <target>1.5</target>
+            </configuration>
+         </plugin>
+      </plugins>
+   </build>
+</project>

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/client/TxTest.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/client/TxTest.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/client/TxTest.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,114 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.rts.client;
+
+import org.jboss.resteasy.util.HttpResponseCodes;
+import org.jboss.jbossts.rts.util.TxUtil;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+
+import junit.framework.Assert;
+
+/**
+ * TODO
+ */
+public class TxTest
+{
+    protected final static Logger log = Logger.getLogger(TxTest.class);
+
+    public static void main(String[] args) throws IOException
+    {
+        modifyResource(2);
+    }
+
+    private static void modifyResource(int txCnt) throws IOException
+    {
+        String[] txUrls = new String[txCnt];
+        String status;
+
+        log.debug("Modifying resource ...");
+        String[] partUrls = {
+                TxUtil.getBaseURI() + "participant/work/p1",
+                TxUtil.getBaseURI() + "participant/work/p2",
+                TxUtil.getBaseURI() + "participant/work/p3",
+                TxUtil.getBaseURI() + "participant/work/p4"
+        };
+        String value = TxUtil.doGet(HttpResponseCodes.SC_OK, partUrls[0], "name=prop1");
+
+        log.debug("resouce value (no tx): " + value);
+
+        log.debug("starting a new transaction: " + TxUtil.txURI + "/begin/12345");
+        txUrls[0] = TxUtil.doPost(HttpResponseCodes.SC_CREATED, TxUtil.txURI + "/begin/12345");
+
+
+        // get the value of the resource
+        value = TxUtil.doGet(HttpResponseCodes.SC_OK, partUrls[0], "name=prop1");
+        log.debug("resouce value (in tx): " + value);
+        // modify the resource (with implicit enlistment)
+        TxUtil.doPost(HttpResponseCodes.SC_OK, partUrls[0], "name=prop1", "value=prop1 new value", "context=" + txUrls[0], "fault=move_to=x");
+        TxUtil.doPost(HttpResponseCodes.SC_OK, partUrls[1], "name=prop2", "value=prop2 new value", "context=" + txUrls[0], "fault=xcommit_fail");
+
+    // *********** start a second tx
+        if (txCnt > 1)
+        {
+            txUrls[1] = TxUtil.doPost(HttpResponseCodes.SC_CREATED, TxUtil.txURI + "/begin/1234567");
+            TxUtil.doPost(HttpResponseCodes.SC_OK, partUrls[2], "name=prop3", "value=prop3 new value", "context=" + txUrls[1]);
+            TxUtil.doPost(HttpResponseCodes.SC_OK, partUrls[3], "name=prop4", "value=prop4 new value", "context=" + txUrls[1]);
+        }
+    //  ************
+        // implicitly enlist a participant
+        log.debug("modifing within tx");
+        value = TxUtil.doGet(HttpResponseCodes.SC_OK, partUrls[0], "name=prop1", "context=" + txUrls[0]);
+        // get its status
+//        status = t.doGet(client, HttpResponseCodes.SC_OK, partUrls[0], "name=prop1", "context=" + txUrls[0]);
+
+//        log.debug("participant status: " + status);
+        log.debug("resouce value (in tx): " + value);
+
+        // prepare via participant
+//        value = t.doPost(client, HttpResponseCodes.SC_CREATED, TxUtil.getBaseURI() + "participant/1/prepare", "context=" + txUrl);
+//        TxUtil.log("prepare url: ", value);
+
+        // list transactions
+        String txns = TxUtil.doGet(HttpResponseCodes.SC_OK, TxUtil.txURI);
+        log.info(txns);
+
+        // commit via coordinator
+        log.debug("commiting " + txUrls[0]);
+        TxUtil.doPut(HttpResponseCodes.SC_OK, txUrls[0] + "/commit", "fault=xcommit_halt");
+
+        // get its status
+        log.debug("looking up status of " + txUrls[0]);
+        status = TxUtil.doGet(HttpResponseCodes.SC_OK, txUrls[0]);
+        log.info("Status: " + status);
+
+        if (txCnt > 1)
+            TxUtil.doPut(HttpResponseCodes.SC_OK, txUrls[1] + "/commit", "fault=xcommit_halt");
+
+        // list transactions
+        txns = TxUtil.doGet(HttpResponseCodes.SC_OK, TxUtil.txURI);
+        log.info(txns);
+    }
+
+
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/Participant.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/Participant.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/Participant.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,219 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.rts.example;
+
+import org.jboss.jbossts.rts.provider.TMUnavailableException;
+import org.jboss.jbossts.rts.provider.HttpResponseException;
+import org.jboss.jbossts.rts.provider.TransactionStatusException;
+import org.jboss.jbossts.rts.util.TxUtil;
+import org.jboss.jbossts.rts.service.*;
+import org.jboss.resteasy.util.HttpResponseCodes;
+import org.apache.log4j.Logger;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.Response;
+import java.util.*;
+
+/**
+ * TODO
+ */
+ at Path("/participant")
+public class Participant implements TransactionalParticipant
+{
+    protected final static Logger log = Logger.getLogger(Participant.class);
+
+    @GET
+    @Path("{pId}/tx/{tid}")
+    public String getStatus(@PathParam("pId")String pid, @PathParam("tid") @DefaultValue("")String tid)
+    {
+        log.debug("particpant: status: " + pid + "/tx/" + tid);
+        return getWork(pid, tid).getStatus();
+    }
+
+    @POST
+    @Path("{pId}/tx/{tid}/prepare/")
+    public Response prepare(@PathParam("pId")String pid, @PathParam("tid") @DefaultValue("")String tid)
+    {
+        log.debug("particpant: prepare: " + pid + "/tx/" + tid + "/prepare");
+        return TxUtil.postResponse(getWork(pid, TxUtil.txURI + "/" + tid).prepare());
+    }
+
+    @POST
+    @Path("{pId}/tx/{tid}/commit/")
+    public Response commit(@PathParam("pId")String pid, @PathParam("tid") @DefaultValue("")String tid)
+    {
+        log.debug("particpant: commit: " + pid + "/tx/" + tid + "/commit");
+
+        if (tid == null)
+            throw new TransactionStatusException("no transaction");
+
+        if (!work.containsKey(pid + '/' + tid))   // resource must have moved
+            return TxUtil.response(HttpResponseCodes.SC_NOT_FOUND);
+
+        Work w = getWork(pid, TxUtil.txURI + "/" + tid);
+
+        return TxUtil.postResponse(w.commit(getWork(pid, "")));
+    }
+    @POST
+    @Path("{pId}/tx/{tid}/rollback/")
+    public Response rollback(@PathParam("pId")String pid, @PathParam("tid") @DefaultValue("")String tid)
+    {
+        log.debug("particpant: rollback: " + pid + "/tx/" + tid + "/rollback");
+        return TxUtil.postResponse(getWork(pid, TxUtil.txURI + "/" + tid).rollback());
+    }
+
+    @POST
+    @Path("{pId}/tx/{tid}/forget/")
+    public Response forget(@PathParam("pId")String pid, @PathParam("tid") @DefaultValue("")String tid)
+    {
+        log.debug("particpant: forget: " + pid + "/tx/" + tid + "/forget");
+        return TxUtil.postResponse(getWork(pid, TxUtil.txURI + "/" + tid).forget());
+    }
+
+    // define example urls for getting and modifying the resource
+
+    @POST
+    @Path("work/{pId}")
+    public String modifyResource(@PathParam("pId")String pid, @QueryParam("context") @DefaultValue("")String ctx,
+                                 @QueryParam("name") String name, @QueryParam("value") String value, @QueryParam("fault") String fault)
+    {
+        log.debug("particpant: modify " + pid + " ctx: " + ctx + " fault: " + fault);
+        getWork(pid, ctx).put(name, value);
+        
+        if (fault != null)
+            getWork(pid, ctx).put("fault", fault);
+
+        return value;
+    }
+
+    @GET
+    @Path("work/{pId}")
+    public String getResource(@PathParam("pId")String pid, @QueryParam("name") String name, @QueryParam("context") @DefaultValue("") String ctx)
+    {
+        log.debug("particpant: get: work/" + pid + " - ctx: " + ctx);
+        return getWork(pid, ctx).get(name);
+    }
+
+    void removeParticipant(Work w)
+    {
+        work.remove(w.getId());
+    }
+
+    void moveParticipant(Work w, String newUrl)
+    {
+        log.info("particpant: moving participant " + w.pid + " to " + newUrl);
+        Work newWork = new Work(w);
+
+        work.remove(w.getId());
+        newWork.pid = "p99";
+        String url = makeParticipantUrl(newWork);
+
+        // remove context from old work
+        w.ctx = null;
+
+        String id = newWork.ctx == null ? newWork.pid : newWork.pid + newWork.ctx;
+        work.put(id, newWork);
+
+        try
+        {
+            TxUtil.doPut(HttpResponseCodes.SC_OK, TxUtil.rcURI + "/" + w.recCoordId, "URL=" + url);
+        }
+        catch (HttpResponseException e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private String makeParticipantUrl(Work w)
+    {
+        // use the same server as the coordinator TODO make this configurable       
+        return TxUtil.getBaseURI() + new StringBuilder("participant/").append(w.pid).append("/tx/").append(w.ctx).toString().replaceAll("//", "/");
+    }
+
+    private Work getWork(String pid, String ctx)
+    {
+        assert ctx != null;
+        assert pid != null;
+        log.debug("getWork(" + pid + ", " + ctx + ')');
+        int i = ctx.indexOf(TxUtil.txURI);
+        if (i == -1 || i + TxUtil.txURI.length() == ctx.length())
+            ctx = "";
+        else
+            ctx = ctx.substring(i + TxUtil.txURI.length());
+        
+        String id = pid + ctx;
+        Work w = work.get(id);
+
+        if (w == null)
+        {
+            w = new Work(this, pid, ctx);
+
+            if (ctx.length() != 0)
+            {
+                try
+                {
+                    // enlist as a participant in this transaction context
+                    String pUrl = makeParticipantUrl(w);
+                    w.recCoordId = TxUtil.doPut(HttpResponseCodes.SC_OK, TxUtil.txURI + ctx, "URL=" + pUrl);
+                }
+                catch (HttpResponseException e)
+                {
+                    throw new TMUnavailableException("Unable to contact TM: " + e.getMessage());
+                }
+            }
+
+            work.put(id, w);
+        }
+
+        return w;
+    }
+
+    public Participant()
+    {
+        timer = new Timer();
+
+        int delay = 5000;   // delay for 5 sec.
+        int period = 1000;  // repeat every sec.
+        Timer timer = new Timer();
+
+        timer.scheduleAtFixedRate(new TimerTask() {
+            public void run() {
+                Iterator<String> keys = work.keySet().iterator();
+
+                while (keys.hasNext())
+                {
+                    String key = keys.next();
+                    if (work.get(key).isComplete())
+                        keys.remove();
+                }
+            }
+        }, delay, period);
+    }
+
+    protected void finalize() throws Throwable
+    {
+        super.finalize();
+        timer.cancel();
+    }
+
+    private Map<String, Work> work = new HashMap<String, Work>();
+    private Timer timer;    
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/Work.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/Work.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/example/Work.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,152 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.rts.example;
+
+import org.jboss.jbossts.rts.provider.TransactionStatusException;
+import org.jboss.jbossts.rts.provider.ResourceNotFoundException;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * TODO
+ */
+public class Work
+{
+    String pid; // = "participant/" + ++id;
+    String ctx;
+    String recCoordId;
+    Participant bean;
+    String status;
+    Map<String, String> properties = new HashMap<String, String>();
+
+    public Work(Participant bean, String pid, String ctx)
+    {
+        this.pid = pid;
+        this.ctx = ctx;
+        this.bean = bean;
+    }
+
+    public Work(Work w)
+    {
+        this.pid = w.pid;
+        this.ctx = w.ctx;
+        this.recCoordId = w.recCoordId;
+        this.status = w.status;
+        this.bean = w.bean;
+        properties.putAll(w.properties);
+    }
+
+    public boolean isReadonly()
+    {
+        return properties.size() == 0;
+    }
+
+    public void put(String name, String value)
+    {
+        properties.put(name, value);
+    }
+
+    public String get(String name)
+    {
+        return properties.get(name);
+    }
+
+    public String getStatus()
+    {
+//            if (ctx == null) return "notx";
+        return pid + "/participant/" + status;
+    }
+
+    public String setStatus(String status)
+    {
+        this.status = status;
+
+        return getStatus();
+    }
+
+    public String prepare()
+    {
+        validateRequest(true);
+        
+        return setStatus(isReadonly() ? PREPARE_RO : PREPARE_OK); // prepare-notok
+    }
+
+    static final String PREPARE_OK = "prepare-ok";
+    static final String PREPARE_RO = "prepare-readonly";
+    static final String ROLLEDBACK = "rolledback";
+    static final String COMMIT_OK = "commit-ok";
+    static final String HEURISTIC = "heuristic";
+
+    public String rollback()
+    {
+        validateRequest(false);
+        bean.removeParticipant(this);
+
+        return setStatus(ROLLEDBACK); // heuristic
+    }
+
+    public String commit(Work mergeTo)
+    {
+        validateRequest(false);
+
+        for (Map.Entry<String, String> e : mergeTo.properties.entrySet())
+            mergeTo.put(e.getKey(), e.getValue());
+
+        bean.removeParticipant(this);
+
+        return setStatus(COMMIT_OK); // or rolledback or heuristic
+    }
+
+    public String forget()
+    {
+        validateRequest(false);
+        bean.removeParticipant(this);
+        
+        return setStatus(ROLLEDBACK);
+    }
+
+    private void validateRequest(boolean prepare)
+    {
+        if (ctx == null)
+            throw new TransactionStatusException("no transaction");
+
+        String fault = properties.get("fault");
+
+        if ((prepare && "prepare_fail".equals(fault)) || (!prepare && "commit_fail".equals(fault)))
+            throw new ResourceNotFoundException("Simulated failure");
+        else if ((prepare && "prepare_halt".equals(fault)) || (!prepare && "commit_halt".equals(fault)))
+            Runtime.getRuntime().halt(1);
+
+        if (prepare && fault != null && fault.startsWith("move_to"))
+            bean.moveParticipant(this, fault);
+    }
+
+    public boolean isComplete()
+    {
+        return COMMIT_OK.equals(status) || ROLLEDBACK.equals(status);
+    }
+
+    public String getId()
+    {
+        return ctx == null ? pid : pid + ctx;
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/HttpResponseException.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/HttpResponseException.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/HttpResponseException.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.rts.provider;
+
+/**
+ * TODO
+ */
+public class HttpResponseException extends Error
+{
+    private int expectedResponse;
+    private int actualResponse;
+    private String body;
+
+    public HttpResponseException(Throwable cause, String body, int expectedResponse, int actualResponse)
+    {
+        super(cause);
+        this.body = body;
+        this.expectedResponse = expectedResponse;
+        this.actualResponse = actualResponse;
+    }
+
+    public int getExpectedResponse()
+    {
+        return expectedResponse;
+    }
+
+    public int getActualResponse()
+    {
+        return actualResponse;
+    }
+
+    public String getBody()
+    {
+        return body;
+    }
+
+    public String getMessage()
+    {
+        if (getCause() != null)
+            return super.getMessage();
+
+        if (expectedResponse != actualResponse)
+            return "Unexpected status. Expected " +  expectedResponse + " got " + actualResponse;
+
+        throw new Error("Invalid HttpResponseException thrown - there's no error and status is fine");
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/NotFoundMapper.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/NotFoundMapper.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/NotFoundMapper.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.rts.provider;
+
+import org.jboss.resteasy.util.HttpResponseCodes;
+
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.core.Response;
+
+/**
+ * TODO
+ */
+ at Provider
+public class NotFoundMapper implements ExceptionMapper<ResourceNotFoundException>
+{
+   public Response toResponse(ResourceNotFoundException exception)
+   {
+      return Response.status(HttpResponseCodes.SC_NOT_FOUND).build();
+   }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/ResourceNotFoundException.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/ResourceNotFoundException.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/ResourceNotFoundException.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,33 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.rts.provider;
+
+/**
+ * TODO
+ */
+public class ResourceNotFoundException extends RuntimeException
+{
+
+    public ResourceNotFoundException(String message)
+    {
+        super(message);
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TMUnavailableException.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TMUnavailableException.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TMUnavailableException.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,32 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.rts.provider;
+
+/**
+ * TODO
+ */
+public class TMUnavailableException extends RuntimeException
+{
+    public TMUnavailableException(String message)
+    {
+        super(message);
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TMUnavailableMapper.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TMUnavailableMapper.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TMUnavailableMapper.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.rts.provider;
+
+import org.jboss.resteasy.util.HttpResponseCodes;
+
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.core.Response;
+
+/**
+ * TODO
+ */
+ at Provider
+public class TMUnavailableMapper implements ExceptionMapper<TMUnavailableException>
+{
+   public Response toResponse(TMUnavailableException exception)
+   {
+      return Response.status(HttpResponseCodes.SC_SERVICE_UNAVAILABLE).build();
+   }
+}
\ No newline at end of file

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TransactionStatusException.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TransactionStatusException.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TransactionStatusException.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,32 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.rts.provider;
+
+/**
+ * TODO
+ */
+public class TransactionStatusException extends RuntimeException
+{
+    public TransactionStatusException(String message)
+    {
+        super(message);
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TransactionStatusMapper.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TransactionStatusMapper.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/provider/TransactionStatusMapper.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.rts.provider;
+
+import org.jboss.resteasy.util.HttpResponseCodes;
+
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.core.Response;
+
+/**
+ * TODO
+ */
+ at Provider
+public class TransactionStatusMapper implements ExceptionMapper<TransactionStatusException>
+{
+   public Response toResponse(TransactionStatusException exception)
+   {
+      return Response.status(HttpResponseCodes.SC_CONFLICT).build();
+   }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/RESTRecord.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/RESTRecord.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/RESTRecord.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,331 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.rts.resource;
+
+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.common.Uid;
+import com.arjuna.ats.arjuna.ObjectType;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.ats.arjuna.gandiva.ClassName;
+
+import org.jboss.jbossts.rts.util.TxUtil;
+import org.jboss.jbossts.rts.provider.HttpResponseException;
+import org.jboss.resteasy.util.HttpResponseCodes;
+import org.apache.log4j.Logger;
+
+/**
+ * TODO
+ */
+public class RESTRecord extends AbstractRecord
+{
+    protected final static Logger log = Logger.getLogger(RESTRecord.class);
+    private String url;
+    private String coordinatorID;
+    private String statusUrl = "";
+    private String txId;
+    private boolean prepared;
+
+    public RESTRecord(String url, String txId)
+    {
+        super(new Uid());
+        log.debug("RESTRecord url: " + url + " tx: " + txId);
+        this.url = url;
+        this.txId = txId;
+        coordinatorID = get_uid().fileStringForm();
+    }
+
+    public static AbstractRecord create()
+    {
+        log.debug("RESTRecord.create");
+        return new RESTRecord();
+    }
+    public RESTRecord()
+    {
+        super();
+        log.debug("RESTRecord()");
+    }
+
+    protected RESTRecord(Uid u)
+    {
+        super(u, null, ObjectType.ANDPERSISTENT);
+    }
+
+    public int typeIs()
+    {
+        return RecordType.USER_DEF_FIRST0;
+    }
+
+    public ClassName className()
+    {
+        return new ClassName("RecordType.USER_DEF_FIRST0");
+    }
+
+    public Object value()
+    {
+        return statusUrl;
+    }
+
+    public void setValue(Object o)
+    {
+    }
+
+    public int nestedAbort()
+    {
+        return TwoPhaseOutcome.FINISH_OK;
+    }
+
+    public int nestedCommit()
+    {
+        return TwoPhaseOutcome.FINISH_OK;
+    }
+
+    /*
+    * Not subtransaction aware.
+    */
+    public int nestedPrepare()
+    {
+        return TwoPhaseOutcome.PREPARE_OK; // do nothing
+    }
+
+    public int topLevelPrepare()
+    {
+        if (fault.equals(Fault.prepare_halt))
+            Runtime.getRuntime().halt(1);
+        
+        if (fault.equals(Fault.h_hazard))
+            return TwoPhaseOutcome.HEURISTIC_HAZARD;
+
+        if (url == null || txId == null)
+            return TwoPhaseOutcome.PREPARE_READONLY;
+
+        try
+        {
+            String txUrl = TxUtil.txURI + '/' + txId;
+            log.debug("preparing " + url);
+            this.statusUrl = TxUtil.doPost(HttpResponseCodes.SC_CREATED, url + "/prepare", "context=" + txUrl);
+            prepared = true;
+            return TwoPhaseOutcome.PREPARE_OK;
+        }
+        catch (HttpResponseException e)
+        {
+            log.info("participant " + url + " prepare error: " + e.getMessage());
+            statusUrl = this.url + "/prepare-notok";
+
+            return TwoPhaseOutcome.PREPARE_NOTOK;
+        }
+    }
+
+    public int topLevelAbort()
+    {
+        if (fault.equals(Fault.abort_halt))
+            Runtime.getRuntime().halt(1);
+
+        // TODO
+        if (url == null || txId == null)
+            return TwoPhaseOutcome.FINISH_ERROR;
+
+        return TwoPhaseOutcome.FINISH_OK;
+    }
+
+    public int topLevelCommit()
+    {
+        if (url == null || txId == null)
+            return TwoPhaseOutcome.PREPARE_READONLY;
+
+        if (!prepared)
+            return TwoPhaseOutcome.NOT_PREPARED;
+
+        return topLevelOnePhaseCommit();
+    }
+
+    public int nestedOnePhaseCommit()
+    {
+        return TwoPhaseOutcome.FINISH_ERROR;
+    }
+
+    /**
+     * For commit_one_phase we can do whatever we want since the transaction
+     * outcome is whatever we want. Therefore, we do not need to save any
+     * additional recoverable state, such as a reference to the transaction
+     * coordinator, since it will not have an intentions list anyway.
+     */
+
+    public int topLevelOnePhaseCommit()
+    {
+        if (fault.equals(Fault.commit_halt))
+            Runtime.getRuntime().halt(1);
+        
+        if (txId == null)
+            return TwoPhaseOutcome.FINISH_ERROR;
+
+//        String txUrl = TxUtil.TX_URI_PREFIX + '/' + txId;
+
+        try
+        {
+            log.debug("commiting " + this.url);
+            if (!statusUrl.endsWith("prepare-readonly"))
+                statusUrl = TxUtil.doPost(HttpResponseCodes.SC_CREATED, this.url + "/commit"); //, "context=" + txUrl));
+
+            return TwoPhaseOutcome.FINISH_OK;
+        }
+        catch (HttpResponseException e)
+        {
+            if (e.getActualResponse() == HttpResponseCodes.SC_NOT_FOUND)
+            {
+                // the participant may have moved so check the coordinator url
+                if (hasParticpantMoved())
+                {
+                    log.debug("participant has moved commit to new url " + this.url);
+                    try
+                    {
+                        this.statusUrl = TxUtil.doPost(HttpResponseCodes.SC_CREATED, this.url + "/commit"); //, "context=" + txUrl));
+
+                        return TwoPhaseOutcome.FINISH_OK;
+                    }
+                    catch (HttpResponseException e1)
+                    {
+                        e = e1;
+                    }
+                }
+            }
+
+            log.info("participant " + this.url + " commit error: " + e.getMessage());
+            this.statusUrl = this.url + "/commit-notok";
+
+            return TwoPhaseOutcome.FINISH_ERROR;
+        }
+    }
+
+    private boolean hasParticpantMoved()
+    {
+        String url = TxUtil.doGet(HttpResponseCodes.SC_OK, TxUtil.getBaseURI() + "recovery-coordinator/" + coordinatorID);
+
+        if (url != null && !url.equals(this.url))
+        {
+            this.url = url;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean save_state(OutputObjectState os, int t)
+    {
+        try
+        {
+            os.packString(txId);
+            os.packBoolean(prepared);
+            os.packString(url);
+            os.packString(coordinatorID);
+            os.packString(statusUrl);
+
+            return super.save_state(os, t);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+
+            return false;
+        }
+    }
+
+    public boolean restore_state(InputObjectState os, int t)
+    {
+        try
+        {
+            txId = os.unpackString();
+            prepared = os.unpackBoolean();
+            url = os.unpackString();
+            coordinatorID = os.unpackString();
+            statusUrl = os.unpackString();
+
+            return super.restore_state(os, t);
+        }
+        catch (Exception e)
+        {
+            return false;
+        }
+    }
+
+    public String type()
+    {
+        return RESTRecord.typeName();
+    }
+
+    public static String typeName()
+    {
+        return "/StateManager/AbstractRecord/RESTRecord";
+    }
+
+    public boolean doSave()
+    {
+        return true;
+    }
+
+    public void merge(AbstractRecord a)
+    {
+    }
+
+    public void alter(AbstractRecord a)
+    {
+    }
+
+    public boolean shouldAdd(AbstractRecord a)
+    {
+        return (a.typeIs() == typeIs());
+    }
+
+    public boolean shouldAlter(AbstractRecord a)
+    {
+        return false;
+    }
+
+    public boolean shouldMerge(AbstractRecord a)
+    {
+        return false;
+    }
+
+    public boolean shouldReplace(AbstractRecord a)
+    {
+        return false;
+    }
+
+    enum Fault {abort_halt, prepare_halt, commit_halt, h_commit, h_rollback, h_hazard, h_mixed, none}
+    Fault fault = Fault.none;
+
+    public void setFault(String name)
+    {
+        for (Fault f : Fault.values())
+        {
+            if (f.name().equals(name))
+            {
+                fault = f;
+                return;
+            }
+        }
+
+        fault = Fault.none;
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/RESTRecordSetup.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/RESTRecordSetup.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/RESTRecordSetup.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.rts.resource;
+
+import com.arjuna.ats.arjuna.gandiva.inventory.InventoryElement;
+import com.arjuna.ats.arjuna.gandiva.ClassName;
+import com.arjuna.ats.arjuna.gandiva.ObjectName;
+
+/**
+ * TODO
+ */
+public class RESTRecordSetup implements InventoryElement
+{
+    public Object createVoid()
+    {
+        return RESTRecord.create();
+    }
+
+    public Object createClassName(ClassName className)
+    {
+        return null;
+    }
+
+    public Object createObjectName(ObjectName objectName)
+    {
+        return null;
+    }
+
+    public Object createResources(Object[] resources)
+    {
+        return null;
+    }
+
+    public Object createClassNameResources(ClassName className, Object[] resources)
+    {
+        return null;
+    }
+
+    public Object createObjectNameResources(ObjectName objectName, Object[] resources)
+    {
+        return null;
+    }
+
+    public ClassName className()
+    {
+        return new ClassName("RecordType.USER_DEF_FIRST0");
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/Transaction.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/Transaction.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/Transaction.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,144 @@
+package org.jboss.jbossts.rts.resource;
+
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
+import com.arjuna.ats.arjuna.coordinator.AddOutcome;
+import com.arjuna.ats.arjuna.AtomicAction;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlAttribute;
+
+ at XmlRootElement(name = "transaction")
+public class Transaction extends AtomicAction
+{
+    private long age = System.currentTimeMillis();
+    private String initiator;
+
+    public Transaction()
+    {
+        super();
+    }
+
+    public Transaction(String initiator)
+    {
+        super();
+        this.initiator = initiator;
+    }
+
+    @XmlElement
+    public String getInitiator()
+    {
+        return initiator;
+    }
+
+    @XmlElement
+    public String getAge()
+    {
+        return Long.toString(System.currentTimeMillis() - age);
+    }
+
+    @XmlAttribute
+    public String getStatus()
+    {
+        return ActionStatus.stringForm(super.status());
+    }
+
+    public String enlistParticipant(String url)
+    {
+        RESTRecord p = new RESTRecord(url, get_uid().fileStringForm());
+        String coordinatorId = p.get_uid().fileStringForm();
+        
+        if (add(p) != AddOutcome.AR_REJECTED)
+            return coordinatorId;
+
+        return null;
+    }
+
+    public void setFault(String fault)
+    {
+        if (pendingList == null)
+            return;
+
+        // only add faults for pending records
+        AbstractRecord r = pendingList.peekFront();
+
+        while (r != null)
+        {
+            if (r instanceof RESTRecord)
+                ((RESTRecord) r).setFault(fault);
+
+            r = pendingList.peekNext(r);
+        }
+    }
+
+    public boolean isTransactionInMidFlight()
+    {
+        switch ( status() )
+        {
+            case ActionStatus.RUNNING    :
+            case ActionStatus.ABORT_ONLY :
+            case ActionStatus.PREPARING  :
+            case ActionStatus.COMMITTING :
+            case ActionStatus.ABORTING   :
+            case ActionStatus.PREPARED   :
+                return true;
+
+                //case ActionStatus.INVALID:
+            default:
+                return false;
+        }
+//        return !isFinished();
+    }
+
+    public boolean isFinished()
+    {
+        switch ( status() )
+        {
+            case ActionStatus.COMMITTED  :
+            case ActionStatus.H_COMMIT   :
+            case ActionStatus.H_MIXED    :
+            case ActionStatus.H_HAZARD   :
+            case ActionStatus.ABORTED    :
+            case ActionStatus.H_ROLLBACK :
+                return true;
+
+                //case ActionStatus.INVALID: throw ...
+            default:
+                return false;
+        }
+    }
+
+    public boolean isAlive()
+    {
+        switch ( status() )
+        {
+            case ActionStatus.RUNNING    :
+            case ActionStatus.ABORT_ONLY :
+            case ActionStatus.PREPARING  :
+            case ActionStatus.COMMITTING :
+            case ActionStatus.ABORTING   :
+            case ActionStatus.PREPARED   :
+                return true;
+
+                //case ActionStatus.INVALID:
+            default:
+                return false;
+        }
+    }
+
+    public boolean hasHeuristic()
+    {
+        switch ( status() )
+        {
+            case ActionStatus.H_COMMIT   :
+            case ActionStatus.H_MIXED    :
+            case ActionStatus.H_HAZARD   :
+            case ActionStatus.H_ROLLBACK :
+                return true;
+
+            default:
+                return false;
+        }
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/TransactionList.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/TransactionList.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/resource/TransactionList.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.rts.resource;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlElement;
+import java.util.List;
+
+ at XmlRootElement(name="listing")
+public class TransactionList
+{
+   private List<Transaction> transactions;
+
+   public TransactionList()
+   {
+   }
+
+   public TransactionList(List<Transaction> transactions)
+   {
+      this.transactions = transactions;
+   }
+
+   @XmlElement(name="transactions")
+   public List<Transaction> getTransactions()
+   {
+      return transactions;
+   }
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/Coordinator.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/Coordinator.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/Coordinator.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,329 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.rts.service;
+
+import org.jboss.jbossts.rts.resource.TransactionList;
+import org.jboss.jbossts.rts.resource.Transaction;
+import org.jboss.jbossts.rts.provider.ResourceNotFoundException;
+import org.jboss.jbossts.rts.provider.TransactionStatusException;
+import org.jboss.jbossts.rts.util.TxUtil;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.Response;
+import org.jboss.resteasy.util.HttpResponseCodes;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.log4j.Logger;
+
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.common.Uid;
+
+/**
+ * The transaction coordinator URL is:
+ *  http://<machine>/transaction-coordinator
+ */
+ at Path("/")
+public class Coordinator
+{
+    protected final static Logger log = Logger.getLogger(Coordinator.class);
+
+    private HashMap<String, Transaction> transactions = new HashMap<String, Transaction>();
+    // each participant may only be enlisted in one transaction
+    private HashMap<String, String> participants = new HashMap<String, String>();
+
+    /**
+     * Performing a GET on /transaction-coordinator returns a list of all transactions know to
+     * the coordinator (active and recovery).
+     * @return list of all transactions know to the coordinator (active and recovery).
+     */
+    @GET
+    @Path("transaction-coordinator")
+    @Produces("application/json")
+    public TransactionList getAllTransactions()
+    {
+        log.debug("coordinator: list: transaction-coordinator");
+        return getTransactions(null);
+    }
+
+    /**
+     * Lookup the status of a single transaction.
+     * @param id the transaction id
+     * @return a list of transactions containing the status of the requested item
+     *  (which will be empty if the tx does not exist)
+     */
+    @GET
+    @Path("transaction-coordinator/{id}")
+    @Produces("application/json")
+    public TransactionList getTransactionStatus(@PathParam("id") String id)
+    {
+        log.debug("coordinator: status: transaction-coordinator/" + id);
+        final Uid uid = new Uid(id);
+        TransactionFilter statusFilter = new TransactionFilter() {
+            public boolean accept(Transaction t)
+            {
+                return uid.equals(t.get_uid());
+            }
+        };
+
+        return getTransactions(statusFilter);
+    }
+
+    @GET
+    @Path("transaction-coordinator.html")
+    @Produces("text/html")
+    public Response getAllTransactionsText() throws Exception
+    {
+        log.debug("coordinator: html list");
+        TransactionList listing = getTransactions(null);
+
+        return Response.ok(TxUtil.stringFormat(listing)).build();
+    }
+
+    /**
+     * Perform a GET on /transaction-coordinator/recovery
+     * @return a list of transactions that are in recovery.
+     */
+    @GET
+    @Path("transaction-coordinator/recovery")
+    @Produces("application/json")
+    public TransactionList getRecoveringTransactions()
+    {
+        log.debug("coordinator: transaction-coordinator/recovery");
+        return getTransactions(recoveringFilter);
+    }
+
+    /**
+     * Perform a GET on /transaction-coordinator/active
+     * @return a list of inflight transaction ids
+     */
+    @GET
+    @Path("transaction-coordinator/active")
+    @Produces("application/json")
+    public TransactionList getActiveTransactions()
+    {
+        log.debug("coordinator: transaction-coordinator/active");
+        return getTransactions(activeTxFilter);
+    }
+    /**
+     * Performing a DELETE on any of the transaction-coordinator URLs will return a 401.
+     * @return 401
+     */
+    @DELETE
+    @Path("transaction-coordinator/{id}")
+//    @RolesAllowed(value = "NONE")
+    public Response deleteTransaction(@PathParam("id") String id)
+    {
+        log.debug("coordinator: delete: transaction-coordinator/" + id);
+        return TxUtil.response(HttpResponseCodes.SC_UNAUTHORIZED);
+    }
+
+    /**
+     * Start a new transaction (which will be deleted when the tx timeout value is reached).
+     * Calls to a timed out tx will return 404 (in which case the caller may assume rollback)
+     * @param clientId the unique identity of the client (can be a URL too)
+     * @param timeout period in milliseconds
+     * @return a <URL> of the form /transaction-coordinator/<TxId>
+     */
+    @POST
+    @Path("transaction-coordinator/begin/{clientId}")
+    public Response beginTransaction(@PathParam("clientId")String clientId, @QueryParam("timeout") @DefaultValue("0")int timeout)
+    {
+        log.debug("coordinator: begin: transaction-coordinator/begin/" + clientId + "?timeout=" + timeout);
+        Transaction tx = new Transaction(clientId);
+        String uid = tx.get_uid().fileStringForm();
+
+        transactions.put(uid, tx);
+        int status = tx.begin(timeout);
+
+        try
+        {
+            if (status == ActionStatus.RUNNING)
+                return TxUtil.postResponse(TxUtil.txURI + '/' + uid);
+
+            throw new TransactionStatusException("Transaction failed to start: " + status);
+        }
+        finally
+        {
+            AtomicAction.suspend();
+        }
+    }
+
+    /**
+     * trigger a commit of the transaction. Upon termination, the URL is implicitly
+     * deleted. If it no longer exists, then 404 will be returned. The invoker
+     * cannot know for sure whether the transaction committed or rolled back.
+     * This is the same problem with JTS: to be sure you should register a 2PC participant.
+     * @param txId transaction id
+     * @return http response code
+     */
+    @PUT
+    @Path("transaction-coordinator/{TxId}/commit")
+    public Response commitTransaction(@PathParam("TxId")String txId, @QueryParam("fault") @DefaultValue("")String fault)
+    {
+        log.debug("coordinator: commit: transaction-coordinator/" + txId + "/commit");
+        Transaction tx = getTransaction(txId);
+
+        tx.setFault(fault);
+        AtomicAction.resume(tx);
+        int res = tx.commit(true);
+        log.debug("commit result: " + res);
+
+        if (tx.isTransactionInMidFlight())
+        {
+            AtomicAction.suspend();
+            throw new TransactionStatusException("Transaction failed to commit: ");
+        }
+
+        if (!tx.hasHeuristic())
+            transactions.remove(txId);
+
+        return TxUtil.response(HttpResponseCodes.SC_OK);
+    }
+
+    /**
+     * Register a participant in a tx
+     * @param txId id of transaction
+     * @param url participant id (unique within the scope of txId)
+     * @return unique resource ref for the participant
+     */
+    @PUT
+    @Path("transaction-coordinator/{TxId}")
+    public Response enlistParticipant(@PathParam("TxId")String txId, @QueryParam("URL")String url)
+    {
+        log.debug("coordinator: enlist: transaction-coordinator/" + txId + "?URL=" + url);
+        Transaction tx = getTransaction(txId);
+        String coordinatorId = tx.enlistParticipant(url);
+
+        if (coordinatorId == null)
+            return TxUtil.response(HttpResponseCodes.SC_NOT_ACCEPTABLE);
+
+        log.debug("... enlisting participant " + url + " in tx " + txId);
+        participants.put(coordinatorId, url);
+
+        String prefix = ""; // "http://<machine>/recovery-coordinator/"
+        return Response.ok(coordinatorId).build();
+//        return prefix + coordinatorId;
+    }
+
+    /**
+     * Get the participant url (registered during enlistParticipant) corresponding to a resource reference
+     * if the coordinator crashes - the participant list will be empty but this is ok if commit hasn't been
+     * called since the TM uses presumed abort sematics.
+     * @param enlistmentId the resource reference
+     * @return the participant url
+     */
+    @GET
+    @Path("recovery-coordinator/{RecCoordId}")
+    public Response lookupParticipant(@PathParam("RecCoordId")String enlistmentId)
+    {
+        log.debug("coordinator: lookup: transaction-coordinator/" + enlistmentId);
+
+        String p = participants.get(enlistmentId);
+
+        if (p == null)
+            return TxUtil.response(HttpResponseCodes.SC_NOT_FOUND);
+
+        return Response.ok(p).build();
+    }
+
+    /**
+     * PUT /recovery-coordinator/<RecCoordId>/<new participant URL>  - overwrite the old <participant URL> with <new participant URL>
+     *   (as with JTS, this will also trigger off a recovery attempt on the associated transaction)
+     * A participant may use this url to notifiy the coordinator that he has moved to a new location.
+     * @param url
+     */
+    @PUT
+    @Path("recovery-coordinator/{RecCoordId}")
+    public Response replaceParticipant(@PathParam("RecCoordId")String enlistmentId, @QueryParam("URL")String url)
+    {
+        log.debug("coordinator: replace: recovery-coordinator/" + enlistmentId + "?URL=" + url);
+        participants.put(enlistmentId, url);
+
+        return TxUtil.response(HttpResponseCodes.SC_OK);
+    }
+
+    @POST
+    @Path("recovery-coordinator/{RecCoordId}")
+    public Response postParticipant(@PathParam("RecCoordId")String enlistmentId)
+    {
+        log.debug("coordinator: replace via Post: recovery-coordinator/" + enlistmentId);
+        return Response.status(HttpResponseCodes.SC_UNAUTHORIZED).build();
+    }
+
+    @DELETE
+    @Path("recovery-coordinator/{RecCoordId}")
+    public Response deleteParticipant(@PathParam("RecCoordId")String enlistmentId)
+    {
+        log.debug("coordinator: replace vi Delete: recovery-coordinator/" + enlistmentId);
+        return TxUtil.response(HttpResponseCodes.SC_UNAUTHORIZED);
+    }
+
+    private Transaction getTransaction(String txId)
+    {
+        Transaction tx = transactions.get(txId);
+
+        if (tx == null)
+            throw new ResourceNotFoundException("Transaction id not found");
+
+        return tx;
+    }
+
+    private TransactionList getTransactions(TransactionFilter filter)
+    {
+        List<Transaction> list = new ArrayList<Transaction>();
+
+        if (filter == null)
+        {
+            list.addAll(transactions.values());
+        }
+        else
+        {
+            for (Transaction t : transactions.values())
+                if (filter.accept(t))
+                    list.add(t);
+        }
+
+        return new TransactionList(list);
+    }
+
+    TransactionFilter activeTxFilter = new TransactionFilter() {
+        public boolean accept(Transaction t)
+        {
+            return t.isTransactionInMidFlight();
+        }
+    };
+
+    TransactionFilter recoveringFilter = new TransactionFilter() {
+        public boolean accept(Transaction t)
+        {
+            return !t.isTransactionInMidFlight();
+        }
+    };
+
+    interface TransactionFilter
+    {
+        boolean accept(Transaction t);
+    }
+}
+

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/TMApplication.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/TMApplication.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/TMApplication.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,72 @@
+package org.jboss.jbossts.rts.service;
+
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+
+import javax.ws.rs.core.Application;
+import javax.ws.rs.ext.ExceptionMapper;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
+
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.jboss.resteasy.plugins.server.embedded.EmbeddedJaxrsServer;
+import org.jboss.jbossts.rts.provider.NotFoundMapper;
+import org.jboss.jbossts.rts.provider.TMUnavailableMapper;
+import org.jboss.jbossts.rts.provider.TransactionStatusMapper;
+
+public class TMApplication extends Application
+{
+    HashSet<Object> singletons = new HashSet<Object>();
+
+    public TMApplication()
+    {
+        try
+        {
+            // TODO move com/arjuna/ats/jbossatx/jt[as]/TransactionManagerService.isRecoveryManagerRunning
+            // to RecoveryManager and change logging
+            RecoveryManager.manager();
+        }
+        catch (Throwable e)
+        {
+            // did it really fail or was it already running?
+            System.err.println("INFO: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public Set<Class<?>> getClasses()
+    {
+        return new HashSet<Class<?>>();
+    }
+
+    @Override
+    public Set<Object> getSingletons()
+    {
+        if (singletons.isEmpty())
+            singletons.addAll(Arrays.asList(resources));
+
+        return singletons;
+    }
+
+    public static void initApplication(EmbeddedJaxrsServer server)
+    {
+        ResteasyProviderFactory pf = server.getFactory();
+
+        for (Class<?> m : mappers)
+            pf.addExceptionMapper((Class<? extends ExceptionMapper>) m);
+
+        for (Object obj : resources)
+            server.getRegistry().addSingletonResource(obj);
+    }
+
+    private static Class<?>[] mappers = {
+        NotFoundMapper.class,
+        TMUnavailableMapper.class,
+        TransactionStatusMapper.class
+    };
+    
+    private static Object[] resources = {
+            new Coordinator(),
+            new org.jboss.jbossts.rts.example.Participant()
+    };  
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/TransactionalParticipant.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/TransactionalParticipant.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/service/TransactionalParticipant.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,48 @@
+package org.jboss.jbossts.rts.service;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.Response;/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+
+public interface TransactionalParticipant
+{
+    @GET
+    @Path("{pId}/tx/{tid}")
+    String getStatus(@PathParam("pId")String pid, @QueryParam("context") String ctx);
+
+    @POST
+    @Path("{pId}/tx/transaction-coordinator/{tid}/prepare/")
+    Response prepare(@PathParam("pId")String pid, @PathParam("tid")String tid);
+
+    @POST
+    @Path("{pId}/tx/transaction-coordinator/{tid}/commit/")
+    Response commit(@PathParam("pId")String pid, @PathParam("tid")String tid);
+
+    @POST
+    @Path("{pId}/tx/transaction-coordinator/{tid}/rollback/")
+    Response rollback(@PathParam("pId")String pid, @PathParam("tid")String tid);
+
+    @POST
+    @Path("{pId}/tx/transaction-coordinator/{tid}/forget/")
+    Response forget(@PathParam("pId")String pid, @PathParam("tid")String tid);    
+
+// don't forget about one phase commit
+}

Added: labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/util/TxUtil.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/util/TxUtil.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/java/org/jboss/jbossts/rts/util/TxUtil.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,203 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, 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) 2008
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.rts.util;
+
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.*;
+import org.apache.log4j.Logger;
+import org.jboss.jbossts.rts.provider.HttpResponseException;
+import org.jboss.resteasy.plugins.providers.jaxb.json.JettisonMappedContext;
+import org.jboss.resteasy.util.HttpResponseCodes;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.JAXBException;
+import javax.ws.rs.core.Response;
+
+/**
+ * TODO
+ */
+public class TxUtil
+{
+    protected static final Logger log = Logger.getLogger(TxUtil.class);
+    
+    public static final String TX_URI_PREFIX = "transaction-coordinator";
+    public static final String RC_URI_PREFIX = "recovery-coordinator";
+    
+    private static HttpClient client = new HttpClient(new MultiThreadedHttpConnectionManager());
+
+    private static int serverPort = 9095;
+    private static String serverHost = "localhost";
+    private static String serverContext = "tx/";
+    private static String baseURI = "http://" + serverHost + ":" + serverPort + "/" + serverContext;
+
+    public static String txURI = TxUtil.getBaseURI() + TX_URI_PREFIX;
+    public static String rcURI = TxUtil.getBaseURI() + RC_URI_PREFIX;
+
+    private static void updateEndpoint()
+    {
+        baseURI = "http://" + serverHost + ":" + serverPort + "/" + serverContext;
+        txURI = baseURI + TX_URI_PREFIX;
+        rcURI = baseURI + RC_URI_PREFIX;
+        log.info("new baseURI: " + baseURI);
+    }
+
+    public static void setPort(int port)
+    {
+        serverPort = port;
+        updateEndpoint();
+    }
+
+    public static void setHost(String host)
+    {
+        serverHost = host;
+        updateEndpoint();
+    }
+
+    public static void setContext(String context)
+    {
+        serverContext = context;
+        updateEndpoint();
+    }
+
+    public static void setEndpoint(String host, int port, String prefix)
+    {
+        serverPort = port;
+        serverHost = host;
+        serverContext = prefix;
+        updateEndpoint();
+    }
+
+    public static String getBaseURI()
+    {
+        return baseURI;
+    }
+    
+    public static String doMethod(int expect, HttpMethod method, String ... nvParams) throws HttpResponseException
+    {
+//        log.debug("Method: " + method.getName() + " " + method.getURI().toString() + " expect response: " + expect);
+        int status = -1;
+
+        if (nvParams.length != 0)
+        {
+            NameValuePair[] params = new NameValuePair[nvParams.length];
+
+            for (int i = 0; i < nvParams.length; i++)
+            {
+                String[] pair = nvParams[i].split("=");
+                if (expect != -1)
+                    assert pair.length == 2;
+                params[i] = new NameValuePair(pair[0], pair[1]);
+            }
+
+            method.setQueryString(params);
+        }
+
+        try
+        {
+            log.info(method.getName() + ": " + method.getURI());
+            status = client.executeMethod(method);
+
+            if (expect > 0 && expect != status)
+                throw new HttpResponseException(null, getResponseBody(method), expect, status);
+
+            return new String(method.getResponseBody(), "US-ASCII");
+        }
+        catch (IOException e)
+        {
+            throw new HttpResponseException(e, "", expect, status);
+        }
+        finally
+        {
+            method.releaseConnection();
+        }
+    }
+
+    private static String getResponseBody(HttpMethod method)
+    {
+        try
+        {
+            return new String(method.getResponseBody(), "US-ASCII");
+        }
+        catch (IOException e)
+        {
+            return "";
+        }
+    }
+
+    public static String doGet(int expect, String url, String... nvParams) throws HttpResponseException
+    {
+        return doMethod(expect, new GetMethod(url), nvParams);
+    }
+
+    public static String doPost(int expect, String url, String... nvParams) throws HttpResponseException
+    {
+        HttpMethod method = new PostMethod(url);
+        String res = doMethod(expect, method, nvParams);
+        Header header = method.getResponseHeader("location");
+        System.out.println("post response location header: " + (header != null ? header.getValue() : "null"));
+        return header != null ? header.getValue() : res;
+    }
+
+    public static String doPut(int expect, String url, String... nvParams) throws HttpResponseException
+    {
+        return doMethod(expect, new PutMethod(url), nvParams);
+    }
+
+    public static String doDelete(int expect, String url, String... nvParams) throws HttpResponseException
+    {
+        return doMethod(expect, new DeleteMethod(url), nvParams);
+    }
+
+    public static String stringFormat(Object obj) throws JAXBException
+    {
+        JettisonMappedContext context = new JettisonMappedContext(obj.getClass());
+        StringWriter writer = new StringWriter();
+        Marshaller marshaller = context.createMarshaller();
+        marshaller.marshal(obj, writer);
+        
+        return writer.toString();
+    }
+
+    public static Response postResponse(String uri)
+    {
+        try
+        {
+            return Response.status(HttpResponseCodes.SC_CREATED).location(new URI(uri)).build();
+        }
+        catch (URISyntaxException e)
+        {
+            return Response.status(HttpResponseCodes.SC_INTERNAL_SERVER_ERROR).build();
+        }
+    }
+
+    public static Response response(int code)
+    {
+        return Response.status(code).build();
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/main/webapp/WEB-INF/web.xml
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/webapp/WEB-INF/web.xml	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/webapp/WEB-INF/web.xml	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+        "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+   <display-name>Archetype Created Web Application</display-name>
+
+   <context-param>
+      <param-name>javax.ws.rs.core.Application</param-name>
+      <param-value>org.jboss.jbossts.rts.service.TMApplication</param-value>
+   </context-param>
+
+   <context-param>
+      <param-name>resteasy.servlet.mapping.prefix</param-name>
+      <param-value>/tx</param-value>
+   </context-param>
+
+   <listener>
+      <listener-class>
+         org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
+      </listener-class>
+   </listener>
+
+   <servlet>
+      <servlet-name>Tx</servlet-name>
+      <servlet-class>
+         org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
+      </servlet-class>
+   </servlet>
+
+   <servlet-mapping>
+      <servlet-name>Tx</servlet-name>
+      <url-pattern>/tx/*</url-pattern>
+   </servlet-mapping>
+
+</web-app>
\ No newline at end of file

Added: labs/jbosstm/workspace/resttx/src/main/webapp/tx.html
===================================================================
--- labs/jbosstm/workspace/resttx/src/main/webapp/tx.html	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/main/webapp/tx.html	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,138 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+   <script type="text/javascript">
+
+      function createXHR()
+      {
+         var request = false;
+         try
+         {
+            request = new ActiveXObject('Msxml2.XMLHTTP');
+         }
+         catch (err2)
+         {
+            try
+            {
+               request = new ActiveXObject('Microsoft.XMLHTTP');
+            }
+            catch (err3)
+            {
+               try
+               {
+                  request = new XMLHttpRequest();
+               }
+               catch (err1)
+               {
+                  request = false;
+               }
+            }
+         }
+         return request;
+      }
+
+      function loadJSON(fname)
+      {
+         var xhr = createXHR();
+         xhr.open("GET", fname, true);
+         xhr.onreadystatechange = function()
+         {
+            if (xhr.readyState == 4)
+            {
+               if (xhr.status != 404)
+               {
+                  var data = eval("(" + xhr.responseText + ")");
+                  document.getElementById("zone").innerHTML = "<h2>Transactions:</h2>";
+                  for (i = 0; i < 3; i++)
+                  {
+                     document.getElementById("zone").innerHTML += data.listing.transactions[i]. at status + ', age <i>' + data.listing.transactions[i].age + "</i><br/>";
+                  }
+               }
+               else
+               {
+                  document.getElementById("zone").innerHTML = fname + " not found";
+               }
+            }
+         }
+         xhr.send(null);
+      }
+      function postJSON(fname)
+      {
+         var xhr = createXHR();
+         xhr.open("POST", fname, true);
+         xhr.onreadystatechange = function()
+         {
+            if (xhr.readyState == 4)
+            {
+               if (xhr.status != 404)
+               {
+                  var data = eval("(" + xhr.responseText + ")");
+                  document.getElementById("zone").innerHTML = "<h2>Transaction:</h2>";
+                  for (i = 0; i < 3; i++)
+                  {
+                     document.getElementById("zone").innerHTML += data.listing.transactions[i]. at status + ', age <i>' + data.listing.transactions[i].age + "</i><br/>";
+                  }
+               }
+               else
+               {
+                  document.getElementById("zone").innerHTML = fname + " not found";
+               }
+            }
+         }
+         xhr.send(null);
+      }
+
+   </script>
+   <title>Web-based transaction service</title></head>
+
+
+<body bgcolor="#FFFFFF">
+
+<p><font size="+3">Transaction Service</font></p>
+<hr>
+<FORM name="ajax" method="POST" action="">
+
+
+   <p>
+      <INPUT type="BUTTON" value=" Transactions " ONCLICK="loadJSON('resteasy/coordinator/transaction-coordinator')">
+   </p>
+   <p>
+   </p>
+   <p>
+      <INPUT type="BUTTON" value=" transaction-coordinator.html " ONCLICK="loadJSON('resteasy/coordinator/transaction-coordinator.html')">
+   </p>
+</FORM>
+
+<p><font size="+3">Start a transaction ...</font></p>
+<hr>
+<form method="POST" action="resteasy/coordinator/transaction-coordinator/begin">
+    ClientId:
+    <input type="text" name="clientId">
+    <br>
+    Timeout:
+    <input type="text" name="timeout">
+       <p>
+      <INPUT type="BUTTON" value=" Begin Transaction "/> <!-- ONCLICK="loadJSON('resteasy/coordinator/transaction-coordinator/begin')"> -->
+   </p>
+</form>
+
+<p><font size="+3">Form test ...</font></p>
+<hr>
+<form method="POST" action="resteasy/coordinator/transaction-coordinator/form">
+    ClientId:
+    <input type="text" name="clientId">
+    <br>
+    Timeout:
+    <input type="text" name="timeout">
+       <p>
+      <INPUT type="BUTTON" value=" Begin Transaction "/> <!-- ONCLICK="loadJSON('resteasy/coordinator/transaction-coordinator/begin')"> -->
+   </p>
+</form>
+
+
+<div id="zone"></div>
+
+</body>
+</html>
\ No newline at end of file

Added: labs/jbosstm/workspace/resttx/src/test/java/org/jboss/jbossts/rts/test/AppTest.java
===================================================================
--- labs/jbosstm/workspace/resttx/src/test/java/org/jboss/jbossts/rts/test/AppTest.java	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/test/java/org/jboss/jbossts/rts/test/AppTest.java	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,214 @@
+package org.jboss.jbossts.rts.test;
+
+import junit.framework.TestCase;
+
+import org.jboss.resteasy.plugins.server.tjws.TJWSEmbeddedJaxrsServer;
+import org.jboss.resteasy.util.HttpResponseCodes;
+import org.jboss.jbossts.rts.util.TxUtil;
+import org.jboss.jbossts.rts.service.TMApplication;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+
+/**
+ * TODO
+ */
+public class AppTest extends TestCase
+{
+    protected final static Logger log = Logger.getLogger(AppTest.class);
+    private static int PORT = 9096;
+    private static TJWSEmbeddedJaxrsServer tjws;
+
+    private String[] URLS;
+
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+
+        TxUtil.setEndpoint("localhost", PORT, "");
+
+        log.info("setUp: " + TxUtil.getBaseURI());
+        URLS = new String[] {
+                TxUtil.txURI + ".html", // list txns
+                TxUtil.txURI + "/begin/12345",   // start a tx on behalf of client id 12345
+                TxUtil.txURI + "/TX/commit",   // commit tx 123
+                TxUtil.txURI + "/0/commit", // commit a non existant tx
+                TxUtil.txURI + "/commit", // commit a non existant tx
+                TxUtil.txURI + "/TX",   // enlist a participant in tx 123
+        };
+
+        tjws = new TJWSEmbeddedJaxrsServer();
+        tjws.setPort(PORT);
+        tjws.setBindAddress("127.0.0.1");
+        TMApplication.initApplication(tjws);
+        tjws.start();
+        System.out.println("web server is running on port " + PORT);
+    }
+
+    protected void tearDown() throws Exception
+    {
+        log.info("stoppig server");
+        super.tearDown();
+
+        tjws.stop();
+    }
+
+    public void testTx() throws IOException
+    {
+        Integer[] opts = {0,1,2,3,4,5};
+        String tx;
+
+        for (Integer opt : opts)
+        {
+            log.info("Testing url " + URLS[opt]);
+
+            switch (opt)
+            {
+                case 0:
+                    // list transactons
+                    TxUtil.doGet(HttpResponseCodes.SC_OK, TxUtil.txURI + ".html");
+                    break;
+                case 1:
+                    // start a tx on behalf of client id 12345
+                    tx = TxUtil.doPost(HttpResponseCodes.SC_CREATED, TxUtil.txURI + "/begin/12345");
+                    log.info("Started tx " + tx);
+                    log.info("Commiting tx " + tx);
+                    TxUtil.doPut(HttpResponseCodes.SC_OK, tx + "/commit");
+                    log.info("Commiting tx " + tx + "/commit");
+                    TxUtil.doPut(HttpResponseCodes.SC_NOT_FOUND, tx + "/commit");
+                    break;
+                case 2:
+                    break;
+                case 3:
+                    // commit a non existant tx
+                    TxUtil.doPost(HttpResponseCodes.SC_NOT_FOUND, TxUtil.txURI + "/0/commit");
+                    break;
+                case 4:
+                    // commit a non existant tx
+                    TxUtil.doPost(HttpResponseCodes.SC_METHOD_NOT_ALLOWED, TxUtil.txURI + "/commit");
+                    break;
+                case 5:
+                    // start a tx on behalf of client id 12345
+                    tx = TxUtil.doPost(HttpResponseCodes.SC_CREATED, TxUtil.txURI + "/begin/12345");
+                    // enlist a participant
+                    log.info("Started tx " + tx + " ... enlisting");
+                    String pUrl = TxUtil.getBaseURI() + "particpant/123";
+                    String recCoordId = TxUtil.doPut(HttpResponseCodes.SC_OK, tx, "URL=" + pUrl);
+                    // commit the tx
+                    TxUtil.doPut(HttpResponseCodes.SC_OK, tx + "/commit");
+                    break;
+                default:
+            }
+        }
+    }
+
+    public void testParticipants() throws IOException
+    {
+        int txCnt = 2;
+        String[] txUrls = new String[txCnt];
+        String status;
+
+        log.debug("Modifying resource ...");
+        String[] partUrls = {
+                TxUtil.getBaseURI() + "participant/work/p1",
+                TxUtil.getBaseURI() + "participant/work/p2",
+                TxUtil.getBaseURI() + "participant/work/p3",
+                TxUtil.getBaseURI() + "participant/work/p4"
+        };
+        String value = TxUtil.doGet(HttpResponseCodes.SC_OK, partUrls[0], "name=prop1");
+
+        log.debug("resouce value (no tx): " + value);
+
+        log.debug("starting a new transaction");
+        txUrls[0] = TxUtil.doPost(HttpResponseCodes.SC_CREATED, TxUtil.txURI + "/begin/12345");
+
+
+        // get the value of the resource
+        value = TxUtil.doGet(HttpResponseCodes.SC_OK, partUrls[0], "name=prop1");
+        log.debug("resouce value (in tx): " + value);
+        // modify the resource (with implicit enlistment)
+        TxUtil.doPost(HttpResponseCodes.SC_OK, partUrls[0], "name=prop1", "value=prop1 new value", "context=" + txUrls[0]);
+        TxUtil.doPost(HttpResponseCodes.SC_OK, partUrls[1], "name=prop2", "value=prop2 new value", "context=" + txUrls[0], "fault=xcommit_fail");
+
+    // *********** start a second tx
+        if (txCnt > 1)
+        {
+            txUrls[1] = TxUtil.doPost(HttpResponseCodes.SC_CREATED, TxUtil.txURI + "/begin/1234567");
+            TxUtil.doPost(HttpResponseCodes.SC_OK, partUrls[2], "name=prop3", "value=prop3 new value", "context=" + txUrls[1]);
+            TxUtil.doPost(HttpResponseCodes.SC_OK, partUrls[3], "name=prop4", "value=prop4 new value", "context=" + txUrls[1]);
+        }
+    //  ************
+        // implicitly enlist a participant
+        log.debug("modifing within tx");
+        value = TxUtil.doGet(HttpResponseCodes.SC_OK, partUrls[0], "name=prop1", "context=" + txUrls[0]);
+        // get its status
+//        status = t.doGet(client, HttpResponseCodes.SC_OK, partUrls[0], "name=prop1", "context=" + txUrls[0]);
+
+//        log.debug("participant status: " + status);
+        log.debug("resouce value (in tx): " + value);
+
+        // prepare via participant
+//        value = t.doPost(client, HttpResponseCodes.SC_CREATED, "participant/1/prepare", "context=" + txUrl);
+//        TxUtil.log("prepare url: ", value);
+
+        // list transactions
+        String txns = TxUtil.doGet(HttpResponseCodes.SC_OK, TxUtil.txURI);
+        log.info(txns);
+
+        // commit via coordinator
+        TxUtil.doPut(HttpResponseCodes.SC_OK, txUrls[0] + "/commit", "fault=xcommit_halt");
+
+        // get its status
+        status = TxUtil.doGet(HttpResponseCodes.SC_OK, txUrls[0]);
+        log.info("Status: " + status);
+
+        if (txCnt > 1)
+            TxUtil.doPut(HttpResponseCodes.SC_OK, txUrls[1] + "/commit", "fault=xcommit_halt");
+
+        // list transactions
+        txns = TxUtil.doGet(HttpResponseCodes.SC_OK, TxUtil.txURI);
+        log.info(txns);
+    }
+
+    public void testParticipant() throws IOException
+    {
+        log.debug("starting a new transaction");
+        String txUrl = TxUtil.doPost(HttpResponseCodes.SC_CREATED, URLS[1]);
+
+        String pUrl = TxUtil.getBaseURI() + "particpants/123";
+        String getUrl = TxUtil.rcURI + '/';
+        // enlist url
+        log.debug("Enlisting " + txUrl + "?URL=" + pUrl + " with particpant url: " + pUrl);
+        String recCoordId = TxUtil.doPut(HttpResponseCodes.SC_OK, txUrl, "URL=" + pUrl);
+        // check that the url is correct
+        log.debug("Validating using url " + getUrl + recCoordId);
+        String origUrl = TxUtil.doGet(HttpResponseCodes.SC_OK, getUrl + recCoordId);
+        assertEquals(pUrl, origUrl);
+
+        // replace the url with a new one
+        pUrl = TxUtil.getBaseURI() + "particpants/123";
+        log.debug("replacing with " + pUrl);
+        TxUtil.doPut(HttpResponseCodes.SC_OK, getUrl + recCoordId, "URL=" + pUrl);
+
+        // check that the url was replaced
+        log.debug("Checking that it was replaced");
+        origUrl = TxUtil.doGet(HttpResponseCodes.SC_OK, getUrl + recCoordId);
+        assertEquals(pUrl, origUrl);
+
+        // POST and DELETE should fail
+        log.debug("checking POST and DELETE fail");
+        TxUtil.doPost(HttpResponseCodes.SC_UNAUTHORIZED, getUrl + recCoordId);
+        TxUtil.doDelete(HttpResponseCodes.SC_UNAUTHORIZED, getUrl + recCoordId);
+    }
+}

Added: labs/jbosstm/workspace/resttx/src/test/resources/log4j.xml
===================================================================
--- labs/jbosstm/workspace/resttx/src/test/resources/log4j.xml	                        (rev 0)
+++ labs/jbosstm/workspace/resttx/src/test/resources/log4j.xml	2008-11-27 16:37:30 UTC (rev 24123)
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+    <appender name="console" class="org.apache.log4j.ConsoleAppender">
+        <param name="Target" value="System.out"/>
+        <param name="Threshold" value="TRACE"/>
+
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%d{ABSOLUTE} {%8.8t} (%x) [%-5p,%-10c{1}] %m%n"/>
+        </layout>
+    </appender>
+
+    <appender name="file" class="org.apache.log4j.FileAppender">
+        <param name="File" value="logs/test.log"/>
+        <param name="Append" value="false"/>
+        <param name="Threshold" value="ERROR"/>
+
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%d [%t] %p - %m%n"/>
+        </layout>
+    </appender>
+
+    <category name="org.jboss.jbossts.rts">
+        <level value="TRACE"/>
+        <appender-ref ref="console"/>
+        <appender-ref ref="file"/>
+    </category>
+<!--
+    <category name="org.jboss.jbossts">
+        <level value="WARN"/>
+        <appender-ref ref="console"/>
+        <appender-ref ref="file"/>
+    </category>
+-->
+    <category name="com.arjuna">
+        <level value="WARN"/>
+        <appender-ref ref="console"/>
+        <appender-ref ref="file"/>
+    </category>
+
+    <category name="org.apache.commons.httpclient">
+        <level value="WARN"/>
+        <appender-ref ref="console"/>
+        <appender-ref ref="file"/>
+    </category>
+
+</log4j:configuration>




More information about the jboss-svn-commits mailing list