[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