[jboss-svn-commits] JBL Code SVN: r35465 - in labs/jbosstm/trunk: rest-tx and 38 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Fri Oct 8 10:54:35 EDT 2010


Author: mmusgrov
Date: 2010-10-08 10:54:32 -0400 (Fri, 08 Oct 2010)
New Revision: 35465

Added:
   labs/jbosstm/trunk/rest-tx/
   labs/jbosstm/trunk/rest-tx/README.txt
   labs/jbosstm/trunk/rest-tx/docs/
   labs/jbosstm/trunk/rest-tx/docs/REST-Atomic+v2+draft+4.pdf
   labs/jbosstm/trunk/rest-tx/docs/REST-Atomic+v2+draft+4.txt
   labs/jbosstm/trunk/rest-tx/docs/draft-nottingham-http-link-header-10.txt
   labs/jbosstm/trunk/rest-tx/pom.xml
   labs/jbosstm/trunk/rest-tx/tx/
   labs/jbosstm/trunk/rest-tx/tx/pom.xml
   labs/jbosstm/trunk/rest-tx/tx/src/
   labs/jbosstm/trunk/rest-tx/tx/src/main/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/HttpResponseException.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/HttpResponseMapper.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/NotFoundMapper.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/ResourceNotFoundException.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TMUnavailableException.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TMUnavailableMapper.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TransactionStatusException.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TransactionStatusMapper.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/RESTRecord.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/Transaction.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/ContextListener.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/Coordinator.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/TMApplication.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/LinkHolder.java
   labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/TxSupport.java
   labs/jbosstm/trunk/rest-tx/tx/src/test/
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/BaseTest.java
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/CoordinatorTest.java
   labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/SpecTest.java
   labs/jbosstm/trunk/rest-tx/tx/src/test/resources/
   labs/jbosstm/trunk/rest-tx/tx/src/test/resources/log4j.xml
   labs/jbosstm/trunk/rest-tx/webservice/
   labs/jbosstm/trunk/rest-tx/webservice/pom.xml
   labs/jbosstm/trunk/rest-tx/webservice/src/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/java/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/java/org/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/java/org/jboss/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/java/org/jboss/jbossts/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/java/org/jboss/jbossts/star/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/
   labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/jboss-web.xml
   labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/web.xml
   labs/jbosstm/trunk/rest-tx/webservice/src/test/
   labs/jbosstm/trunk/rest-tx/webservice/src/test/java/
   labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/
   labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/jboss/
   labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/jboss/jbossts/
   labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/jboss/jbossts/star/
   labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/jboss/jbossts/star/test/
   labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/jboss/jbossts/star/test/ClientIntegrationTest.java
   labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/
   labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/jndi.properties
   labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/log4j.xml
   labs/jbosstm/trunk/rest-tx/x
Log:
[JBTM-747] Implementation of draft 4 of the REST-star tx specification

Added: labs/jbosstm/trunk/rest-tx/README.txt
===================================================================
--- labs/jbosstm/trunk/rest-tx/README.txt	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/README.txt	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,40 @@
+
+A project that shows how to perform transactional work in a RESTful environment.
+It is based on draft 4 of a specification available from
+http://www.jboss.org/reststar/specifications/transactions.html
+with some modifications listed in ./docs/REST-Atomic+v2+draft+4.txt
+(changes are marked with lines that begin with the text =>)
+
+The project contains two (maven) modules:
+
+ - tx contains the implementation (the JAX-RS resources that implement the coordinator)
+ - webservice (packages the tx module artifact into a web archive for deployment to a web container)
+
+The unit tests use Jersey as the JAX-RS implementation.
+The integration tests use Jersey for the particpant and Resteasy for the coordinator.
+Jersey and Resteasy are JAX-RS (JSR311) implementations.
+
+There is a utility class called org.jboss.jbossts.star.util.TxSupport to help clients and participants
+conform to this specification. You may like to use it directly or else just read the code to get a feel
+for how to conform using your own code (perhaps you are using a different language or want to use a faster
+HTTP library instead of the default java.util.net.HttpURLConnection).
+
+To use the implementation you will need an initial URL for starting transactions. The default is
+TxSupport.TXN_MGR_URL and uses localhost on port 8080. To change it (and still use the TxSupport utility)
+call TxSupport.setTxnMgrUrl(...).
+
+Building
+========
+To build the coordinator jar and war
+
+	mvn clean install
+
+Module tx contains unit tests that show how to implement the client and transactional participants.
+The coordinator and particpant both run in a single embedded container (https://grizzly-servlet-container.dev.java.net/).
+
+To run the integration tests you will need to have a running instance of the JBoss application server
+(http://www.jboss.org/jbossas/). Start the with the default server and set the JBOSS_HOME environment variable
+appropriately. I have tested against AS trunk (6.0.0.20100721-M4) - I will update the project when AS6 is ready.
+The integration tests run the particpant in an embedded container and the coordinator in the JBoss AS:
+
+	mvn clean install -Premote

Added: labs/jbosstm/trunk/rest-tx/docs/REST-Atomic+v2+draft+4.pdf
===================================================================
(Binary files differ)


Property changes on: labs/jbosstm/trunk/rest-tx/docs/REST-Atomic+v2+draft+4.pdf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: labs/jbosstm/trunk/rest-tx/docs/REST-Atomic+v2+draft+4.txt
===================================================================
--- labs/jbosstm/trunk/rest-tx/docs/REST-Atomic+v2+draft+4.txt	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/docs/REST-Atomic+v2+draft+4.txt	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,505 @@
+
+147 There is a new media type to represent the status of a coordinator and its participants:
+148 application/txstatus, which supports a return type based on the scheme maintained at www.rest-
+149 star.org/... For example:
+    tx-status=TransactionActive
+=>txstatus=
+150
+    2.3.3 Client and transaction interactions
+151
+152 The transaction coordinator is represented by a URI. In the rest of this specification we shall
+153 assume it is http://www.fabrikam.com/transaciton-manager, but it could be any URI and its role
+154 need not be explicitly apparent within the structure of the URI.
+    2.3.3.1 Creating a transaction
+155
+156 Performing a POST on /transaction-manager with content as shown below will start a new
+157 transaction with a default timeout. A successful invocation will return 201 and the Location header
+158 MUST contain the URI of the newly created transaction resource, which we will refer to as
+159 transaction-coordinator in the rest of this specification. Two related URLs MUST also be returned,
+160 one for the terminator of the transaction to use (typically referred to as the client) and one used
+161 for registering durable participation in the transaction (typically referred to as the server).
+162 Although uniform URL structures are used in the examples, these linked URLs can be of arbitrary
+163 format.
+164
+    POST /transaction-manager HTTP/1.1
+165
+    From: foo at bar.com
+166
+    Content-Type: application/x-www-form-urlencoded
+167
+    Content-Length: 32
+168
+169
+170 The corresponding response would be:
+171
+    HTTP 1.1 201 Created
+172
+    Location: /transaction-coordinator/1234
+173
+    Link: /transaction-coordinator/1234/terminator;
+174
+    rel=”terminator”
+175
+    Link: /transaction-coordinator/1234/participant;
+176
+    rel=”durable participant”
+=> The link header rfc does not allow spaces in the rel attribute:
+=>    reg-rel-type   = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
+=> I've gone for durableparticipant
+177
+178
+179 Performing a HEAD on /transaction-coordinator/1234 MUST return the same link information.
+180
+    HEAD /transaction-coordinator/1234 HTTP/1.1
+181
+    From: foo at bar.com
+182
+183
+    HTTP/1.1 200 OK
+184
+    Link: /transaction-coordinator/1234/terminator;
+185
+    rel=”terminator”
+186
+    Link: /transaction-coordinator/1234/participant;
+187
+    rel=”durable participant”
+188
+189
+190 Performing a POST on transaction-manager as shown below will start a new transaction with the
+191 specified timeout in milliseconds.
+192
+    POST /transaction-manager HTTP/1.1
+193
+    From: foo at bar.com
+194
+    Content-Type: application/x-www-form-urlencoded
+195
+    Content-Length: --
+196
+197
+    timeout=1000
+198
+199
+200 If the transaction is terminated because of a timeout, the resources representing the created
+201 transaction are deleted. All further invocations on the transaction-coordinator or any of its related
+202 URIs MAY return 410 if the implementation records information about transactions that have
+203 rolled back, (not necessary for presumed rollback semantics) but at a minimum MUST return 401.
+204 The invoker can assume this was a rollback.
+205
+206 Performing a GET on that /transaction-manager returns a list of all transaction coordinator URIs
+207 known to the coordinator (active and in recovery).
+=> (each URI is separated by a semi-colon character)
+=> I have assumed that the content type consumed is text/plain
+    2.3.3.2 Obtaining the transaction status
+208
+209 Performing a GET on /transaction-coordinator/1234 returns the current status of the transaction,
+210 as described later.
+211
+    GET /transaction-coordinator/1234 HTTP/1.1
+212
+    Accept: application/txstatus+xml
+=> there is no content sent with the request so restraining the Accept header is pointless
+213
+214
+215 With an example response:
+216
+    HTTP/1.1 200 OK
+217
+    Content-Length: --
+218
+    Content-Type: application/txstatus
+219
+220
+    tx-status=TransactionActive
+221
+222
+223 Performing a DELETE on any of the /transaction-coordinator URIs will return a 403.
+    2.3.3.3 Terminating a transaction
+224
+225 The client can PUT one of the following to /transaction-coordinator/1234/terminator in order to
+226 control the outcome of the transaction; anything else MUST return a 400. Performing a PUT as
+=> NOTE that JAX-RS does not support HTTP methods TRACE, CONNECT and PATCH and these will therefore return
+=> 404. Perhaps (instead of 400) it would be better to say:
+=> ... anything else MUST return a 404
+227 shown below will trigger the commit of the transaction. Upon termination, the resource and all
+228 associated resources are implicitly deleted. For any subsequent invocation then an
+229 implementation MAY return 410 if the implementation records information about transactions that
+230 have rolled back, (not necessary for presumed rollback semantics) but at a minimum MUST
+=>   "have rolled back" should read " have completed"
+231 return 401. The invoker can assume this was a rollback. In order for an interested party to know
+=> if the implementation does not record information about completed transactions then such a request
+=> MUST return 404 (since by definition it is no longer aware of the resource)
+232 for sure the outcome of a transaction then it MUST be registered as a participant with the
+233 transaction coordinator.
+234
+    PUT /transaction-coordinator/1234/terminator HTTP/1.1
+235
+    From: foo at bar.com
+236
+    Content-Type: application/txstatus
+237
+    Content-Length: --
+238
+239
+    tx-status=TransactionCommit
+=> strictly speaking this is not a status
+=> better would be to set the state to TransactionCommitted
+240
+241
+242 If the transaction no longer exists then an implementation MAY return 410 if the implementation
+243 records information about transactions that have rolled back, (not necessary for presumed
+244 rollback semantics) but at a minimum MUST return 401.
+245
+246 The state of the transaction MUST be Active for this operation to succeed. If the transaction is in
+247 an invalid state for the operation then the implementation MUST 403. Otherwise the
+248 implementation MAY return 200 or 202. In the latter case the Location header SHOULD contain a
+249 URI upon which a GET may be performed to obtain the transaction outcome. It is implementation
+250 dependent as to how long this URI will remain valid. Once removed by an implementation then
+251 410 MUST be returned.
+252
+253 The transaction may be told to rollback with the following PUT request:
+254
+    PUT /transaction-coordinator/1234/terminator HTTP/1.1
+255
+    From: foo at bar.com
+256
+    Content-Type: application/txstatus
+257
+    Content-Length: --
+258
+259
+    tx-status=TransactionRollback
+=> TransactionRollback is not a status - better would be TransactionRolledback
+260
+    2.3.4 Transaction context propagation
+261
+262 When making an invocation on a resource that needs to participate in a transaction, the server
+263 URI (e.g., /transaction-coordinator/1234) needs to be transmitted to the resource. How this
+264 happens is outside the scope of this specification. It may occur as additional payload on the initial
+265 request, or it may be that the client sends the context out-of-band to the resource.
+266
+267 Note, a server SHOULD only use the transaction coordinator URIs it is given directly and not
+268 attempt to infer any others. For example, an implementation MAY decide to give the server
+269 access to only the root transaction coordinator URI and the participant URI, preventing it from
+270 terminating the transaction directly.
+    2.3.5 Coordinator and participant interactions
+271
+272 Once a resource has the transaction URI, it can register participation in the transaction. The
+273 participant is free to use whatever URI structure it desires for uniquely identifying itself; in the rest
+274 of this specification we shall assume it is /participant-resource.
+    2.3.5.1 Enlisting a two-phase aware participant
+275
+276 A participant is registered with /transaction-coordinator using POST on the participant Link URI
+277 obtained when the transaction was created originally:
+278
+    POST /transaction-coordinator/1234/participant HTTP/1.1
+279
+    From: foo at bar.com
+280
+    Content-Type: application/x-www-form-urlencoded
+281
+    Content-Length: --
+282
+283
+    participant=/participant-resource/+
+284
+    terminator=/participant-resource/terminator
+=> use links for participant and terminator
+=> Link: /participant-resource; rel=”participant”
+=> Link: /participant-resource/terminator; rel=”terminator”
+285
+286
+287 Performing a HEAD on a registered participant URI MUST return the terminator reference, as
+288 shown below:
+289
+    HEAD /participant-resource HTTP/1.1
+290
+    From: foo at bar.com
+291
+292
+    HTTP/1.1 200 OK
+293
+    Link: /participant-resource/terminator;
+294
+    rel=”terminator”
+295
+296
+297 If the transaction is not Active then the implementation MUST return 403. If the implementation
+=> I am assuming not Active also includes the case where the committment protocol has started.
+298 has seen this participant URI before then it MUST return 400. Otherwise the operation is
+299 considered a success and the implementation MUST return 201 and MAY use the Location
+300 header to give a participant specific URI that the participant MAY use later during prepare or for
+301 recovery purposes. The lifetime of this URI is the same as /transaction-coordinator. In the rest of
+302 this specification we shall refer to this URI as /participant-recovery (not to be confused with the
+303 /participant-resource URI) although the actual format is implementation dependant.
+304
+    HTTP/1.1 201 Created
+305
+    Location: /participant-recovery/1234
+306
+307
+308 Note, in a subsequent draft we shall discuss how a participant can also register alternative
+309 terminator resources for the various operations used during the commit protocol. In this draft we
+310 assume that a uniform approach is used for all participants.
+    2.3.5.2 Enlisting a two-phase unaware participant
+=> Why bother - the two-phase unaware participant still needs to be aware of prepare and commit i.e. its
+=> not much diffent from the two-phase aware participant
+311
+312 In order for a participant to be enlisted with a transaction it MUST be transaction aware in order
+313 that it can fulfill the requirements placed on it to ensure data consistency in the presence of
+314 failures or concurrent access. However, it is not necessary that a participant be modified such
+315 that it has a terminator resource as outlined previously: it simply needs a way to tell the
+316 coordinator which resource(s) with which to communicate when driving the two-phase protocol.
+317 This type of participant will be referred to as Two-Phase Unaware, though strictly speaking such
+318 a participant or service does need to understand the protocol as mentioned earlier.
+319
+320 During enlistment a service MUST provide URIs for prepare, commit, rollback and OPTIONAL
+=> the MUST only applies if it is not a two-phase aware participant
+321 commit-one-phase:
+322
+    POST /transaction-coordinator/1234/participant HTTP/1.1
+323
+    From: foo at bar.com
+324
+    Content-Type: application/x-www-form-urlencoded
+325
+    Content-Length: --
+326
+327
+    participant=/participant-resource+
+328
+    prepare=/participant-resource/prepare+
+329
+    commit=/participant-resource/commit+
+330
+    rollback=/participant-resource/rollback
+331
+332
+333 Performing a HEAD on a registered participant URI MUST return these references, as shown
+334 below:
+335
+    HEAD /participant-resource HTTP/1.1
+336
+    From: foo at bar.com
+337
+338
+    HTTP/1.1 200 OK
+339
+    Link: /participant-resource/prepare; rel=”prepare”
+340
+    Link: /participant-resource/commit; rel=”commit”
+341
+    Link: /participant-resource/rollback; rel=”rollback”
+342
+343
+344 A service that registers a participant MUST therefore either define a terminator relationship for the
+345 participant or the relationships/resources needed for the two-phase commit protocol.
+    2.3.5.3 Obtaining the status of a participant
+346
+347 Performing a GET on the /participant-resource URL MUST return the current status of the
+348 participant in the same way as for the /transaction-coordinator URI discussed earlier. Determining
+349 the status of a participant whose URI has been removed is similar to that discussed for
+350 /transaction-coordinator.
+    2.3.5.4 Terminating a participant
+351
+352 The coordinator drives the participant through the two-phase commit protocol by sending a PUT
+353 request to the participant terminator URI provided during enlistment, with Prepare, Commit,
+354 Rollback or CommitOnePhase as the message content, i.e., requesting the state of the resource
+355 to be changed accordingly:
+356
+    PUT /participant-resource HTTP/1.1
+=> PUT /participant-resource/terminator HTTP/1.1
+357
+    From: foo at bar.com
+358
+    Content-Type: application/txstatus
+359
+    Content-Length: --
+360
+361
+    tx-status=TransactionPrepare
+=> TransactionPrepare is not a status - better would be TransactionPrepared
+362
+363
+364 If the operation is successful then the implementation MUST return 200. A subsequent GET on
+365 the URI will return the current status of the participant as described previously. It is not always
+366 necessary to enquire as to the status of the participant once the operation has been successful.
+367
+368 If the operation fails then the implementation MUST return 409. It is implementation dependant as
+369 to whether the /participant-resource or related URIs remain valid, i.e., an implementation MAY
+370 delete the resource as a result of a failure. Depending upon the point in the two-phase commit
+371 protocol where such a failure occurs the transaction MUST be rolled back. If the participant is not
+372 in the correct state for the requested operation, e.g., Prepare when it has been already been
+373 prepared, then the implementation MUST return 409.
+374
+375 If the transaction coordinator receives any response other than 200 for Prepare then the
+376 transaction MUST rollback.
+377
+378 Note, read-only MAY be modeled as a DELETE request from the participant to the coordinator
+379 using the URI returned during registration in the Location header, as mentioned previously, i.e.,
+380 /participant-recovery. If GET is used to obtain the status of the participant after a 200 response is
+381 received to the original PUT for Prepare then the implementation MUST return 410 if the
+382 participant was read-only.
+=> the participant only knows it is read-only after prepare is called so DELETE won't work.
+=> the coordinator needs to know whether the participant is read-only at prepare time otherwise
+=> it cannot optimise 2PC processing. Hence the spec needs a status code for the participant
+=> to report that it is read-only: TransactionReadOnly
+=>
+=> But note that performing DELETE (on this recovery URL) prior to prepare makes sense and corresponds to a
+=> participant leaving the transaction. It should Such a delete would also have the consequence of deleting the
+=> participant URL too.
+383
+384 The usual rules of heuristic decisions apply here (i.e., the participant cannot forget the choice until
+385 it is told to by the coordinator).
+386
+387 Performing a PUT on /participant-resource/terminator with Forget will cause the participant to
+388 forget any heuristic decision it made on behalf of the transaction. If the operation succeeds then
+=> why not use DELETE. Then we can completely do away with RPC like instructions to tell resources to
+=> perform actions, ie:
+=>• instead of TransactionPrepare: tell the participant to update its state to TransactionPrepared
+=>• instead of TransactionCommit: tell the recipient to update its state to TransactionCommitted
+=>• instead of TransactionRollback: tell the recipient to update its state to TransactionRolledback
+=>• instead of TransactionForget: DELETE the participant URL
+
+389 200 MUST be returned and the implementation MAY delete the resource. Any other response
+390 means the coordinator MUST retry.
+    2.3.6 Recovery
+391
+392 In general it is assumed that failed actors in this protocol, i.e., coordinator or participants, will
+393 recover on the same URI as they had prior to the failure. If that is not possible them these
+394 endpoints SHOULD return a 301 status code or some other way of indicating that the participant
+395 has moved elsewhere.
+396
+397 However, sometimes it is possible that a participant may crash and recover on a different URI,
+398 e.g., the original machine is unavailable, or that for expediency it is necessary to move recovery
+399 to a different machine. In that case it may be that transaction coordinator is unable to complete
+400 the transaction, even during recovery. As a result this protocol defines a way for a recovering
+401 server to update the information maintained by the coordinator on behalf of these participants.
+402
+403 If the implementation uses the /participant-recovery URI described previously then a GET on
+404 /participant-recovery will return the original participant URI supplied when the participant was
+405 registered.
+=> The reason for doing a GET is usually to get the terminator so this spec should return the same
+=> content as was used during participant enlistment, line 275, (otherwise it would take 2 requests to get the
+=> terminator, a GET followed by a HEAD request on the returned participant URI). The same comment
+=> applies to performing PUT on /participant-recovery
+406
+407 Performing a PUT on /participant-recovery will overwrite the old participant URI with the new one
+408 supplied. This will also trigger off a recovery attempt on the associated transaction using the new
+409 participant URI.
+410
+    PUT /participant-recovery/1234 HTTP/1.1
+411
+    From: foo at bar.com
+412
+    Content-Type: application/x-www-form-urlencoded
+413
+    Content-Length: --
+414
+415
+    new-address=URI
+=> As per the previous comment the returned content should be the same as that returned during
+=> participant enlistment, namely:
+=>    participant=/participant-resource/+
+=>    terminator=/participant-resource/terminator
+416
+    2.3.7 Pre- and post- two-phase commit processing
+417
+418 Most modern transaction processing systems allow the creation of participants that do not take
+419 part in the two-phase commit protocol, but are informed before it begins and after it has
+420 completed. They are called Synchronizations, and are typically employed to flush volatile
+421 (cached) state, which may be being used to improve performance of an application, to a
+422 recoverable object or database prior to the transaction committing.
+423
+424 This additional protocol is accomplished in this specification by supporting an additional two-
+425 phase commit protocol that enclosed the protocol we have already discussed. This will be termed
+426 the Volatile Two Phase Commit protocol, as the participants involved in it are not required to be
+427 durable for the purposes of data consistency, whereas the other protocol will be termed the
+428 Durable Two Phase Commit protocol. The coordinator MUST not record any durable information
+429 on behalf of Volatile participants.
+430
+431 In this case the Volatile prepare phase executes prior to the Durable prepare: only if this prepare
+432 succeeds will the Durable protocol be executed. If the Durable protocol completes then this MAY
+433 be communicated to the Volatile participants through the commit or rollback phases. However,
+434 because the coordinator does not maintain any information about these participants and the
+435 Durable protocol has completed, this SHOULD be a best-effort approach only, i.e., such
+436 participants SHOULD NOT assume they will be informed about the transaction outcome. If that is
+437 a necessity then they should register with the Durable protocol instead.
+438
+439 The Volatile protocol is identical to the Durable protocol described already. The only differences
+440 are as discussed below:
+441
+442      • It is an OPTIONAL protocol. An implementation that supports the protocol MUST show this
+443           when the transaction is created through a Link relationship: it returns an additional Linked
+444           resource whose relationship is defined as “volatile participant”. Services MUST use this
+445           URI when registering volatile participants.
+446      • There is no recovery associated with the Volatile protocol. Therefore the /participant-
+447           recovery URI SHOULD NOT be used by an implementation.
+448      • There can be no heuristic outcomes associated with the Volatile protocol.
+449      • An implementation MAY allow registration in the Volatile protocol after the transaction has
+450           been asked to terminate as long as the Durable protocol has not started.
+451      • There is no one-phase commit optimization for the Volatile protocol.
+
+    2.3.9 Statuses
+475
+476 Participants SHOULD return the following statuses by performing a GET on the appropriate
+477 /transaction-coordinator or participant URI:
+478      • TransactionRollbackOnly: the status of the endpoint is that it will roll back eventually.
+479      • TransactionRollingBack: the endpoint is in the process of rolling back.
+480      • TransactionRolledBack: the endpoint has rolled back.
+481      • TransactionCommitting: the endpoint is in the process of committing. This does not mean
+482          that the final outcome will be Committed.
+483      • TransactionCommitted: the endpoint has committed.
+484      • TransactionHeuristicRollback: all of the participants rolled back when they were asked to
+485          commit.
+486      • TransactionHeuristicCommit: all of the participants committed when they were asked to
+487          rollback.
+488      • TransactionHeuristicHazard: some of the participants rolled back, some committed and the
+489          outcome of others is indeterminate.
+490      • TransactionHeuristicMixed: some of the participants rolled back whereas the remainder
+491          committed.
+492      • TransactionPreparing: the endpoint is preparing.
+493      • TransactionPrepared: the endpoint has prepared.
+494      • TransactionActive: the transaction is active, i.e., has not begun to terminate.
+=> the participant needs to be able to indicate that it is read-only
+=>      • TransactionReadOnly: the transaction is read-only, i.e., the participant did not change any 
+=> transactional resources.
+495
+496 The following status values are sent by the endpoints such as the coordinator to participants in
+497 order to drive them through the two-phase commit state machine:
+498      • TransactionPrepare: the participant should attempt to prepare on behalf of the transaction.
+499      • TransactionCommit: the recipient should attempt to commit. If the recipient is a participant
+500          and there has been no prepare instruction then this is a one-phase commit.
+501      • TransactionRollback: the recipient should attempt to rollback.
+=>      • TransactionForget: the recipient should forget any heuristic.
+
+
+=> Previous comments:
+
+Section 2.3.4 and 2.3.5
+
+The text talks about propagating the transaction URI but should instead talk about propagating the URI for participating
+in the transaction ie the one returned in the link header when the transaction resource was created, namely:
+
+	Link: /transaction-coordinator/1234/participant;
+
+In Section 2.3.5.1 a participant enlists by giving the coordinator two URIs:
+	participant=/participant-resource/+
+	terminator=/participant-resource/terminator
+
+But later in Section 2.3.5.4 on terminating a participant the text is using the participant URI (eg line 357)
+instead of the terminator URI from line 285 (for a two-phase aware participant)
+or the rel=”prepare” link from line 329 (for a two-phase unaware participant).
+
+When enlisting a participant (using POST to create a resource) Line 299 says the implementation "MAY use the Location
+header ..." but the HTTP specification says that the implementation "SHOULD use the Location header ..."
+
+Section 2.3.8 on Checked transactions, though interesting, has no relevance to the specification and is best deleted.
+
+
+===============
+
+The link rel attribute can't contain spaces (replacing space with - is ok though). The grammer is:
+	reg-rel-type   = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
+

Added: labs/jbosstm/trunk/rest-tx/docs/draft-nottingham-http-link-header-10.txt
===================================================================
--- labs/jbosstm/trunk/rest-tx/docs/draft-nottingham-http-link-header-10.txt	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/docs/draft-nottingham-http-link-header-10.txt	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,1599 @@
+                              
+Web Linking
+
+                  
+draft-nottingham-http-link-header-10
+
+
+Abstract
+
+   This document specifies relation types for Web links, and defines a
+   registry for them.  It also defines the use of such links in HTTP
+   headers with the Link header-field.
+
+Status of this Memo
+
+   This Internet-Draft is submitted in full conformance with the
+   provisions of BCP 78 and BCP 79.
+
+   Internet-Drafts are working documents of the Internet Engineering
+   Task Force (IETF).  Note that other groups may also distribute
+   working documents as Internet-Drafts.  The list of current Internet-
+   Drafts is at http://datatracker.ietf.org/drafts/current/.
+
+   Internet-Drafts are draft documents valid for a maximum of six months
+   and may be updated, replaced, or obsoleted by other documents at any
+   time.  It is inappropriate to use Internet-Drafts as reference
+   material or to cite them other than as "work in progress."
+
+   This Internet-Draft will expire on November 6, 2010.
+
+Copyright Notice
+
+   Copyright (c) 2010 IETF Trust and the persons identified as the
+   document authors.  All rights reserved.
+
+   This document is subject to BCP 78 and the IETF Trust's Legal
+   Provisions Relating to IETF Documents
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+
+
+
+Nottingham              Expires November 6, 2010                [Page 1]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   This document may contain material from IETF Documents or IETF
+   Contributions published or made publicly available before November
+   10, 2008.  The person(s) controlling the copyright in some of this
+   material may not have granted the IETF Trust the right to allow
+   modifications of such material outside the IETF Standards Process.
+   Without obtaining an adequate license from the person(s) controlling
+   the copyright in such materials, this document may not be modified
+   outside the IETF Standards Process, and derivative works of it may
+   not be created outside the IETF Standards Process, except to format
+   it for publication as an RFC or to translate it into languages other
+   than English.
+
+
+Table of Contents
+
+   1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  3
+   2.  Notational Conventions . . . . . . . . . . . . . . . . . . . .  3
+   3.  Links  . . . . . . . . . . . . . . . . . . . . . . . . . . . .  4
+   4.  Link Relation Types  . . . . . . . . . . . . . . . . . . . . .  4
+     4.1.  Registered Relation Types  . . . . . . . . . . . . . . . .  5
+     4.2.  Extension Relation Types . . . . . . . . . . . . . . . . .  6
+   5.  The Link Header Field  . . . . . . . . . . . . . . . . . . . .  6
+     5.1.  Target IRI . . . . . . . . . . . . . . . . . . . . . . . .  7
+     5.2.  Context IRI  . . . . . . . . . . . . . . . . . . . . . . .  7
+     5.3.  Relation Type  . . . . . . . . . . . . . . . . . . . . . .  8
+     5.4.  Target Attributes  . . . . . . . . . . . . . . . . . . . .  8
+     5.5.  Examples . . . . . . . . . . . . . . . . . . . . . . . . .  9
+   6.  IANA Considerations  . . . . . . . . . . . . . . . . . . . . . 10
+     6.1.  Link HTTP Header Registration  . . . . . . . . . . . . . . 10
+     6.2.  Link Relation Type Registry  . . . . . . . . . . . . . . . 10
+     6.3.  Link Relation Application Data Registry  . . . . . . . . . 16
+   7.  Security Considerations  . . . . . . . . . . . . . . . . . . . 17
+   8.  Internationalisation Considerations  . . . . . . . . . . . . . 18
+   9.  References . . . . . . . . . . . . . . . . . . . . . . . . . . 18
+     9.1.  Normative References . . . . . . . . . . . . . . . . . . . 18
+     9.2.  Informative References . . . . . . . . . . . . . . . . . . 19
+   Appendix A.  Link Relation Registry Format . . . . . . . . . . . . 20
+     A.1.  Relax NG Grammar . . . . . . . . . . . . . . . . . . . . . 21
+     A.2.  Example  . . . . . . . . . . . . . . . . . . . . . . . . . 22
+   Appendix B.  Notes on Using the Link Header with the HTML4
+                Format  . . . . . . . . . . . . . . . . . . . . . . . 22
+   Appendix C.  Notes on Using the Link Header with the Atom
+                Format  . . . . . . . . . . . . . . . . . . . . . . . 23
+   Appendix D.  Acknowledgements  . . . . . . . . . . . . . . . . . . 24
+   Appendix E.  Document history  . . . . . . . . . . . . . . . . . . 24
+   Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 28
+
+
+
+
+
+Nottingham              Expires November 6, 2010                [Page 2]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+1. Introduction
+
+
+   A means of indicating the relationships between resources on the Web,
+   as well as indicating the type of those relationships, has been
+   available for some time in HTML [W3C.REC-html401-19991224], and more
+   recently in Atom [RFC4287].  These mechanisms, although conceptually
+   similar, are separately specified.  However, links between resources
+   need not be format-specific; it can be useful to have typed links
+   that are independent of their serialisation, especially when a
+   resource has representations in multiple formats.
+
+   To this end, this document defines a framework for typed links that
+   isn't specific to a particular serialisation or application.  It does
+   so by re-defining the link relation registry established by Atom to
+   have a broader domain, and adding to it the relations that are
+   defined by HTML.
+
+   Furthermore, an HTTP header-field for conveying typed links was
+   defined in Section 19.6.2.4 of [RFC2068], but removed from [RFC2616],
+   due to a lack of implementation experience.  Since then, it has been
+   implemented in some User-Agents (e.g., for stylesheets), and several
+   additional use cases have surfaced.
+
+   Because it was removed, the status of the Link header is unclear,
+   leading some to consider minting new application-specific HTTP
+   headers instead of reusing it.  This document addresses this by re-
+   specifying the Link header as one such serialisation, with updated
+   but backwards-compatible syntax.
+
+
+2. Notational Conventions
+
+
+   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+   document are to be interpreted as described in BCP 14, [RFC2119], as
+   scoped to those conformance targets.
+
+   This document uses the Augmented Backus-Naur Form (ABNF) notation of
+   [RFC2616], and explicitly includes the following rules from it:
+   quoted-string, token, SP (space), LOALPHA, DIGIT.
+
+   Additionally, the following rules are included from [RFC3986]: URI
+   and URI-Reference; from [RFC4288]: type-name and subtype-name; from
+   [W3C.REC-html401-19991224]: MediaDesc; from [RFC5646]: Language-Tag;
+   and from [I-D.reschke-rfc2231-in-http], ext-value and parmname.
+
+
+
+
+
+
+Nottingham              Expires November 6, 2010                [Page 3]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+3. Links
+
+
+   In this specification, a link is a typed connection between two
+   resources that are identified by IRIs [RFC3987], and is comprised of:
+   o  A context IRI, and
+   o  a link relation type (Section 4), and
+   o  a target IRI, and
+   o  optionally, target attributes.
+
+   A link can be viewed as a statement of the form "{context IRI} has a
+   {relation type} resource at {target IRI}, which has {target
+   attributes}."
+
+   Note that in the common case, the context IRI will also be a URI
+   [RFC3986], because many protocols (such as HTTP) do not support
+   dereferencing IRIs.  Likewise, the target IRI will be converted to a
+   URI (see [RFC3987], Section 3.1) in serialisations that do not
+   support IRIs (e.g., the Link header).
+
+   This specification does not place restrictions on the cardinality of
+   links; there can be multiple links from and to a particular IRI, and
+   multiple links of different types between two given IRIs.  Likewise,
+   the relative ordering of links in any particular serialisation, or
+   between serialisations (e.g., the Link header and in-content links)
+   is not specified or significant in this specification; applications
+   that wish to consider ordering significant can do so.
+
+   Target attributes are a set of key/value pairs that describe the link
+   or its target; for example, a media type hint.  This specification
+   does not attempt to coordinate their names or use, but does provide
+   common target attributes for use in the Link HTTP header.
+
+   Finally, this specification does not define a general syntax for
+   expressing links, nor mandate a specific context for any given link;
+   it is expected that serialisations of links will specify both
+   aspects.  One such serialisation is communication of links through
+   HTTP headers, specified in Section 5.
+
+
+4. Link Relation Types
+
+
+   In the simplest case, a link relation type identifies the semantics
+   of a link.  For example, a link with the relation type "copyright"
+   indicates that the resource identified by the target IRI is a
+   statement of the copyright terms applying to the current context IRI.
+
+   Link relation types can also be used to indicate that the target
+   resource has particular attributes, or exhibits particular
+
+
+
+Nottingham              Expires November 6, 2010                [Page 4]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   behaviours; for example, a "service" link implies that the identified
+   resource is part of a defined protocol (in this case, a service
+   description).
+
+   Relation types are not to be confused with media types [RFC4288];
+   they do not identify the format of the representation that results
+   when the link is dereferenced.  Rather, they only describe how the
+   current context is related to another resource.
+
+   Relation types SHOULD NOT infer any additional semantics based upon
+   the presence or absence of another link relation type, or its own
+   cardinality of occurrence.  An exception to this is the combination
+   of the "alternate" and "stylesheet" registered relation types, which
+   has special meaning in HTML4 for historical reasons.
+
+   There are two kinds of relation types: registered and extension.
+
+4.1. Registered Relation Types
+
+
+   Well-defined relation types can be registered as tokens for
+   convenience and/or to promote reuse by other applications.  This
+   specification establishes an IANA registry of such relation types;
+   see Section 6.2.
+
+   Registered relation type names MUST conform to the reg-rel-type rule,
+   and MUST be compared character-by-character in a case-insensitive
+   fashion.  They SHOULD be appropriate to the specificity of the
+   relation type; i.e., if the semantics are highly specific to a
+   particular application, the name should reflect that, so that more
+   general names are available for less specific use.
+
+   Registered relation types MUST NOT constrain the media type of the
+   context IRI, and MUST NOT constrain the available representation
+   media types of the target IRI.  However, they can specify the
+   behaviours and properties of the target resource (e.g., allowable
+   HTTP methods, request and response media types which must be
+   supported).
+
+   Additionally, specific applications of linking may require additional
+   data to be included in the registry.  For example, Web browsers might
+   want to know what kinds of links should be downloaded when they
+   archive a Web page; if this application-specific information is in
+   the registry, new link relation types can control this behaviour
+   without unnecessary coordination.
+
+   To accommodate this, per-entry application data can be added to the
+   Link Relation Type Registry, by registering it in the Link Relation
+   Application Data Registry (Section 6.3).
+
+
+
+Nottingham              Expires November 6, 2010                [Page 5]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+4.2. Extension Relation Types
+
+
+   Applications that don't wish to register a relation type can use an
+   extension relation type, which is a URI [RFC3986] that uniquely
+   identifies the relation type.  Although the URI can point to a
+   resource that contains a definition of the semantics of the relation
+   type, clients SHOULD NOT automatically access that resource to avoid
+   overburdening its server.
+
+   When extension relation types are compared, they MUST be compared as
+   strings (after converting to URIs if serialised in a different
+   format, such as a Curie [W3C.CR-curie-20090116]) in a case-
+   insensitive fashion, character-by-character.  Because of this, all-
+   lowercase URIs SHOULD be used for extension relations.
+
+   Note that while extension relation types are required to be URIs, a
+   serialisation of links can specify that they are expressed in another
+   form, as long as they can be converted to URIs.
+
+
+5. The Link Header Field
+
+
+   The Link entity-header field provides a means for serialising one or
+   more links in HTTP headers.  It is semantically equivalent to the
+   <LINK> element in HTML, as well as the atom:link feed-level element
+   in Atom [RFC4287].
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Nottingham              Expires November 6, 2010                [Page 6]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+  Link           = "Link" ":" #link-value
+  link-value     = "<" URI-Reference ">" *( ";" link-param )
+  link-param     = ( ( "rel" "=" relation-types )
+                 | ( "anchor" "=" <"> URI-Reference <"> )
+                 | ( "rev" "=" relation-types )
+                 | ( "hreflang" "=" Language-Tag )
+                 | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
+                 | ( "title" "=" quoted-string )
+                 | ( "title*" "=" ext-value )
+                 | ( "type" "=" ( media-type | quoted-mt ) )
+                 | ( link-extension ) )
+  link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
+                 | ( ext-name-star "=" ext-value )
+  ext-name-star  = parmname "*" ; reserved for RFC2231-profiled
+                                ; extensions. Whitespace NOT
+                                ; allowed in between.
+  ptoken         = 1*ptokenchar
+  ptokenchar     = "!" | "#" | "$" | "%" | "&" | "'" | "("
+                 | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
+                 | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
+                 | "[" | "]" | "^" | "_" | "`" | "{" | "|"
+                 | "}" | "~"
+  media-type     = type-name "/" subtype-name
+  quoted-mt      = <"> media-type <">
+  relation-types = relation-type
+                 | <"> relation-type *( 1*SP relation-type ) <">
+  relation-type  = reg-rel-type | ext-rel-type
+  reg-rel-type   = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
+  ext-rel-type   = URI
+
+5.1. Target IRI
+
+
+   Each link-value conveys one target IRI as a URI-Reference (after
+   conversion to one, if necessary; see [RFC3987], Section 3.1) inside
+   angle brackets ("<>").  If the URI-Reference is relative, parsers
+   MUST resolve it as per [RFC3986], Section 5.  Note that any base IRI
+   from the message's content is not applied.
+
+5.2. Context IRI
+
+
+   By default, the context of a link conveyed in the Link header field
+   is the IRI of the requested resource.
+
+   When present, the anchor parameter overrides this with another URI,
+   such as a fragment of this resource, or a third resource (i.e., when
+   the anchor value is an absolute URI).  If the anchor parameter's
+   value is a relative URI, parsers MUST resolve it as per [RFC3986],
+   Section 5.  Note that any base URI from the body's content is not
+
+
+
+Nottingham              Expires November 6, 2010                [Page 7]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   applied.
+
+   Consuming implementations can choose to ignore links with an anchor
+   parameter.  For example, the application in use may not allow the
+   context IRI to be assigned to a different resource.  In such cases,
+   the entire link is to be ignored; consuming implementations MUST NOT
+   process the link without applying the anchor.
+
+   Note that depending on HTTP status code and response headers, the
+   context IRI might be "anonymous" (i.e., no context IRI is available).
+   For instance, this is the case on a 404 response to a GET request.
+
+5.3. Relation Type
+
+
+   The relation type of a link is conveyed in the "rel" parameter's
+   value.  The "rel" parameter MUST NOT appear more than once in a given
+   link-value; occurrences after the first MUST be ignored by parsers.
+
+   The "rev" parameter has been used in the past to indicate that the
+   semantics of the relationship are in the reverse direction.  I.e., a
+   link from A to B with REL="X" expresses the same relationship as a
+   link from B to A with REV="X". "rev" is deprecated by this
+   specification because it often confuses authors and readers; in most
+   cases using a separate relation type is preferable.
+
+   Note that extension relation types are REQUIRED to be absolute URIs
+   in Link headers, and MUST be quoted if they contain a semicolon (";")
+   or comma (",") (as these characters are used as delimiters in the
+   header itself).
+
+5.4. Target Attributes
+
+
+   The "hreflang", "media", "title", "title*", "type" and any link-
+   extension link-params are considered to be target attributes for the
+   link.
+
+   The "hreflang" parameter, when present, is a hint indicating what the
+   language of the result of dereferencing the link should be.  Note
+   that this is only a hint; for example, it does not override the
+   Content-Language header of a HTTP response obtained by actually
+   following the link.  Multiple hreflang parameters on a single link-
+   value indicate that multiple languages are available from the
+   indicated resource.
+
+   The "media" parameter, when present, is used to indicate intended
+   destination medium or media for style information (see
+   [W3C.REC-html401-19991224], Section 6.13).  Note that this may be
+   updated by [W3C.CR-css3-mediaqueries-20090915]).  Its value MUST be
+
+
+
+Nottingham              Expires November 6, 2010                [Page 8]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   quoted if it contains a semicolon (";") or comma (","), and there
+   MUST NOT be more than one media parameter in a link-value.
+
+   The "title" parameter, when present, is used to label the destination
+   of a link such that it can be used as a human-readable identifier
+   (e.g. a menu entry) in the language indicated by the Content-Language
+   header (if present).  The "title" parameter MUST NOT appear more than
+   once in a given link-value; occurrences after the first MUST be
+   ignored by parsers.
+
+   The "title*" parameter can be used to encode this label in a
+   different character set, and/or contain language information as per
+   [I-D.reschke-rfc2231-in-http].  The "title*" parameter MUST NOT
+   appear more than once in a given link-value; occurrences after the
+   first MUST be ignored by parsers.  If the parameter does not contain
+   language information, its language is indicated by the Content-
+   Language header (when present).
+
+   If both the "title" and "title*" parameters appear in a link-value,
+   processors SHOULD use the "title*" parameter's value.
+
+   The "type" parameter, when present, is a hint indicating what the
+   media type of the result of dereferencing the link should be.  Note
+   that this is only a hint; for example, it does not override the
+   Content-Type header of a HTTP response obtained by actually following
+   the link.  There MUST NOT be more than one type parameter in a link-
+   value.
+
+5.5. Examples
+
+
+   For example:
+
+   Link: <http://example.com/TheBook/chapter2>; rel="previous";
+         title="previous chapter"
+
+   indicates that "chapter2" is previous to this resource in a logical
+   navigation path.
+
+   Similarly,
+
+   Link: </>; rel="http://example.net/foo"
+
+   indicates that the root resource ("/") is related to this resource
+   with the extension relation type "http://example.net/foo".
+
+   The example below shows an instance of the Link header encoding
+   multiple links, and also the use of RFC 2231 encoding to encode both
+   non-ASCII characters and language information.
+
+
+
+Nottingham              Expires November 6, 2010                [Page 9]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   Link: </TheBook/chapter2>;
+         rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
+         </TheBook/chapter4>;
+         rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel
+
+   Here, both links have titles encoded in UTF-8, use the German
+   language ("de"), and the second link contains the Unicode code point
+   U+00E4 ("LATIN SMALL LETTER A WITH DIAERESIS").
+
+   Note that link-values can convey multiple links between the same
+   target and context IRIs; for example:
+
+       Link: <http://example.org/>;
+             rel="start http://example.net/relation/other"
+
+   Here, the link to "http://example.org/" has the registered relation
+   type "start" and the extension relation type
+   "http://example.net/relation/other".
+
+
+6. IANA Considerations
+
+
+6.1. Link HTTP Header Registration
+
+
+   This specification updates the Message Header Registry entry for
+   "Link" in HTTP [RFC3864] to refer to this document.
+
+   Header field: Link
+   Applicable protocol: http
+   Status: standard
+   Author/change controller:
+       IETF  (iesg at ietf.org)
+       Internet Engineering Task Force
+   Specification document(s):
+      [ this document ]
+
+6.2. Link Relation Type Registry
+
+
+   This specification establishes the Link Relation Type Registry, and
+   updates Atom [RFC4287] to refer to it in place of the "Registry of
+   Link Relations".
+
+   [[ Note to IESG: Entries in the Atom registry that are not listed
+   below at the time that IANA implements this change (i.e., those that
+   are registered before this document comes into effect) should be
+   referred to the Designated Expert. ]]
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 10]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+6.2.1. Registering new Link Relation Types
+
+
+   Relation types are registered on the advice of a Designated Expert
+   (appointed by the IESG or their delegate), with a Specification
+   Required (using terminology from [RFC5226]).
+
+   The requirements for registered relation types are described in
+   Section 4.1.
+
+   Registration requests consist of the completed registration template
+   below, typically published in an RFC or Open Standard (in the sense
+   described by [RFC2026], Section 7).  However, to allow for the
+   allocation of values prior to publication, the Designated Expert may
+   approve registration once they are satisfied that a specification
+   will be published.
+
+   Note that relation types can be registered by third parties, if the
+   Designated Expert determines that an unregistered relation type is
+   widely deployed and not likely to be registered in a timely manner.
+
+   The registration template is:
+
+   o  Relation Name:
+   o  Description:
+   o  Reference:
+   o  Notes: [optional]
+   o  Application Data: [optional]
+
+   Registration requests should be sent to the [TBD]@ietf.org mailing
+   list, marked clearly in the subject line (e.g,.  "NEW RELATION
+   REQUEST").
+
+   Within at most 14 days of the request, the Designated Expert(s) will
+   either approve or deny the registration request, communicating this
+   decision to the review list.  Denials should include an explanation
+   and, if applicable, suggestions as to how to make the request
+   successful.
+
+   Decisions (or lack thereof) made by the Designated Expert can be
+   first appealed to Application Area Directors (contactable using
+   app-ads at tools.ietf.org email address or directly by looking up their
+   email addresses on http://www.iesg.org/ website) and, if the
+   appellant is not satisfied with the response, to the full IESG (using
+   the iesg at iesg.org mailing list).
+
+   When a registration request is successful, the Designated Expert(s)
+   will update the registry XML file (using the format described in
+   Appendix A including the MIT license) and send it to the [TBD-2]@
+
+
+
+Nottingham              Expires November 6, 2010               [Page 11]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   ietf.org mailing list (which SHOULD NOT be centrally archived, so as
+   to avoid load issues from automated agents, and only accept posts
+   from the Designated Expert(s)), so that implementers interested in
+   receiving a machine-readable registry can do so.  Simultaneously,
+   they will send a text (not XML) version of the registry to IANA for
+   publication.
+
+   IANA should only accept registry updates from the Designated
+   Expert(s), and should direct all requests for registration to the
+   review mailing list.
+
+6.2.2. Initial Registry Contents
+
+
+   The Link Relation Type registry's initial contents are:
+
+   o  Relation Name: alternate
+   o  Description: Designates a substitute for the link's context.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: appendix
+   o  Description: Refers to an appendix.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: bookmark
+   o  Description: Refers to a bookmark or entry point.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: chapter
+   o  Description: Refers to a chapter in a collection of resources.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: contents
+   o  Description: Refers to a table of contents.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: copyright
+   o  Description: Refers to a copyright statement that applies to the
+      link's context.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: current
+   o  Description: Refers to a resource containing the most recent
+      item(s) in a collection of resources.
+   o  Reference: [RFC5005]
+
+   o  Relation Name: describedby
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 12]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   o  Description: Refers to a resource providing information about the
+      link's context.
+   o  Documentation: <http://www.w3.org/TR/powder-dr/#assoc-linking>
+
+   o  Relation Name: edit
+   o  Description: Refers to a resource that can be used to edit the
+      link's context.
+   o  Reference: [RFC5023]
+
+   o  Relation Name: edit-media
+   o  Description: Refers to a resource that can be used to edit media
+      associated with the link's context.
+   o  Reference: [RFC5023]
+
+   o  Relation Name: enclosure
+   o  Description: Identifies a related resource that is potentially
+      large and might require special handling.
+   o  Reference: [RFC4287]
+
+   o  Relation Name: first
+   o  Description: An IRI that refers to the furthest preceding resource
+      in a series of resources.
+   o  Reference: [this document]
+   o  Notes: this relation type registration did not indicate a
+      reference.  Originally requested by Mark Nottingham in December
+      2004.
+
+   o  Relation Name: glossary
+   o  Description: Refers to a glossary of terms.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: help
+   o  Description: Refers to a resource offering help (more information,
+      links to other sources information, etc.)
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: hub
+   o  Description: Refers to a hub that enables registration for
+      notification of updates to the context.
+   o  Reference: <http://pubsubhubbub.googlecode.com/> <http://
+      pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.3.html>
+   o  Notes: this relation type was requested by Brett Slatkin.
+
+   o  Relation Name: index
+   o  Description: Refers to an index.
+   o  Reference: [W3C.REC-html401-19991224]
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 13]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   o  Relation Name: last
+   o  Description: An IRI that refers to the furthest following resource
+      in a series of resources.
+   o  Reference: [this document]
+   o  Notes: this relation type registration did not indicate a
+      reference.  Originally requested by Mark Nottingham in December
+      2004.
+
+   o  Relation Name: latest-version
+   o  Description: Points to a resource containing the latest (e.g.,
+      current) version of the context.
+   o  Reference: [RFC5829]
+
+   o  Relation Name: license
+   o  Description: Refers to a license associated with the link's
+      context.
+   o  Reference: [RFC4946]
+
+   o  Relation Name: next
+   o  Description: Refers to the next resource in a ordered series of
+      resources.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: next-archive
+   o  Description: Refers to the immediately following archive resource.
+   o  Reference: [RFC5005]
+
+   o  Relation Name: payment
+   o  Description: indicates a resource where payment is accepted.
+   o  Reference: [this document]
+   o  Notes: this relation type registration did not indicate a
+      reference.  Requested by Joshua Kinberg and Robert Sayre.  It is
+      meant as a general way to facilitate acts of payment, and thus
+      this specification makes no assumptions on the type of payment or
+      transaction protocol.  Examples may include a web page where
+      donations are accepted or where goods and services are available
+      for purchase. rel="payment" is not intended to initiate an
+      automated transaction.  In Atom documents, a link element with a
+      rel="payment" attribute may exist at the feed/channel level and/or
+      the entry/item level.  For example, a rel="payment" link at the
+      feed/channel level may point to a "tip jar" URI, whereas an entry/
+      item containing a book review may include a rel="payment" link
+      that points to the location where the book may be purchased
+      through an online retailer.
+
+   o  Relation Name: prev
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 14]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   o  Description: Refers to the previous resource in an ordered series
+      of resources.  Synonym for "previous".
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: predecessor-version
+   o  Description: Points to a resource containing the predecessor
+      version in the version history.
+   o  Reference: [RFC5829]
+
+   o  Relation Name: previous
+   o  Description: Refers to the previous resource in an ordered series
+      of resources.  Synonym for "prev".
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: prev-archive
+   o  Description: Refers to the immediately preceding archive resource.
+   o  Reference: [RFC5005]
+
+   o  Relation Name: related
+   o  Description: Identifies a related resource.
+   o  Reference: [RFC4287]
+
+   o  Relation Name: replies
+   o  Description: Identifies a resource that is a reply to the context
+      of the link.
+   o  Reference: [RFC4685]
+
+   o  Relation Name: section
+   o  Description: Refers to a section in a collection of resources.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: self
+   o  Description: Conveys an identifier for the link's context.
+   o  Reference: [RFC4287]
+
+   o  Relation Name: service
+   o  Description: Indicates a URI that can be used to retrieve a
+      service document.
+   o  Reference: [RFC5023]
+   o  Notes: When used in an Atom document, this relation type specifies
+      Atom Publishing Protocol service documents by default.  Requested
+      by James Snell.
+
+   o  Relation Name: start
+   o  Description: Refers to the first resource in a collection of
+      resources.
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 15]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: stylesheet
+   o  Description: Refers to an external style sheet.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: subsection
+   o  Description: Refers to a resource serving as a subsection in a
+      collection of resources.
+   o  Reference: [W3C.REC-html401-19991224]
+
+   o  Relation Name: successor-version
+   o  Description: Points to a resource containing the successor version
+      in the version history.
+   o  Reference: [RFC5829]
+
+   o  Relation Name: up
+   o  Description: Refers to a parent document in a hierarchy of
+      documents.
+   o  Reference: [this document]
+   o  Notes: this relation type registration did not indicate a
+      reference.  Requested by Noah Slater.
+
+   o  Relation Name: version-history
+   o  Description: points to a resource containing the version history
+      for the context.
+   o  Reference: [RFC5829]
+
+   o  Relation Name: via
+   o  Description: Identifies a resource that is the source of the
+      information in the link's context.
+   o  Reference: [RFC4287]
+
+   o  Relation Name: working-copy
+   o  Description: Points to a working copy for this resource.
+   o  Reference: [RFC5829]
+
+   o  Relation Name: working-copy-of
+   o  Description: Points to the versioned resource from which this
+      working copy was obtained.
+   o  Reference: [RFC5829]
+
+6.3. Link Relation Application Data Registry
+
+
+   This specification also establishes the Link Relation Application
+   Field Registry, to allow entries in the Link Relation Type Registry
+   to be extended with application-specific data (hereafter, "app data")
+   specific to all instances of a given link relation type.
+
+
+
+Nottingham              Expires November 6, 2010               [Page 16]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   Application data is registered on the advice of a Designated Expert
+   (appointed by the IESG or their delegate), with a Specification
+   Required (using terminology from [RFC5226]).
+
+   Registration requests consist of the completed registration template
+   below;
+
+   o  Application Name:
+   o  Description:
+   o  Default Value:
+   o  Notes: [optional]
+
+   The Description SHOULD identify the value space of the app data.  The
+   Default Value MUST be appropriate to entries which the app data does
+   not apply to.
+
+   Entries that pre-date the addition of app data will automatically be
+   considered to have the default value for that app data; if there are
+   exceptions, the modification of such entries should be coordinated by
+   the Designated Expert(s), in consultation with the author of the
+   proposed app data as well as the registrant of the existing entry (if
+   possible).
+
+   Registration requests should be sent to the [TBD]@ietf.org mailing
+   list, marked clearly in the subject line (e.g,.  "NEW APP DATA").
+
+   Within at most 14 days of the request, the Designated Expert will
+   either approve or deny the registration request, communicating this
+   decision to the review list.  Denials should include an explanation
+   and, if applicable, suggestions as to how to make the request
+   successful.  Registration requests that are undetermined for a period
+   longer than 21 days can be brought to the IESG's attention (using the
+   iesg at iesg.org mailing list) for resolution.
+
+   When a registration request is successful, the Designated Expert will
+   forward it to IANA for publication.  IANA should only accept registry
+   updates from the Designated Expert(s), and should direct all requests
+   for registration to the review mailing list.
+
+
+7. Security Considerations
+
+
+   The content of the Link header-field is not secure, private or
+   integrity-guaranteed, and due caution should be exercised when using
+   it.  Use of TLS with HTTP ([RFC2818] and [RFC2817]) is currently the
+   only end-to-end way to provide such protection.
+
+   Applications that take advantage of typed links should consider the
+
+
+
+Nottingham              Expires November 6, 2010               [Page 17]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   attack vectors opened by automatically following, trusting, or
+   otherwise using links gathered from HTTP headers.  In particular,
+   Link headers that use the "anchor" parameter to associate a link's
+   context with another resource should be treated with due caution.
+
+   The Link entity-header field makes extensive use of IRIs and URIs.
+   See [RFC3987] for security considerations relating to IRIs.  See
+   [RFC3986] for security considerations relating to URIs.  See
+   [RFC2616] for security considerations relating to HTTP headers.
+
+
+8. Internationalisation Considerations
+
+
+   Target IRIs may need to be converted to URIs in order to express them
+   in serialisations that do not support IRIs.  This includes the Link
+   HTTP header.
+
+   Similarly, the anchor parameter of the Link header does not support
+   IRIs, and therefore IRIs must be converted to URIs before inclusion
+   there.
+
+   Relation types are defined as URIs, not IRIs, to aid in their
+   comparison.  It is not expected that they will be displayed to end
+   users.
+
+
+9. References
+
+
+9.1. Normative References
+
+
+   [I-D.reschke-rfc2231-in-http]
+              Reschke, J., "Character Set and Language Encoding for
+              Hypertext Transfer Protocol (HTTP) Header Field
+              Parameters", draft-reschke-rfc2231-in-http-12 (work in
+              progress), April 2010.
+
+   [RFC2026]  Bradner, S., "The Internet Standards Process -- Revision
+              3", BCP 9, RFC 2026, October 1996.
+
+   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
+              Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+   [RFC2616]  Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
+              Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
+              Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
+
+   [RFC3864]  Klyne, G., Nottingham, M., and J. Mogul, "Registration
+              Procedures for Message Header Fields", BCP 90, RFC 3864,
+
+
+
+Nottingham              Expires November 6, 2010               [Page 18]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+              September 2004.
+
+   [RFC3986]  Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
+              Resource Identifier (URI): Generic Syntax", STD 66,
+              RFC 3986, January 2005.
+
+   [RFC3987]  Duerst, M. and M. Suignard, "Internationalized Resource
+              Identifiers (IRIs)", RFC 3987, January 2005.
+
+   [RFC4288]  Freed, N. and J. Klensin, "Media Type Specifications and
+              Registration Procedures", BCP 13, RFC 4288, December 2005.
+
+   [RFC5226]  Narten, T. and H. Alvestrand, "Guidelines for Writing an
+              IANA Considerations Section in RFCs", BCP 26, RFC 5226,
+              May 2008.
+
+   [RFC5646]  Phillips, A. and M. Davis, "Tags for Identifying
+              Languages", BCP 47, RFC 5646, September 2009.
+
+9.2. Informative References
+
+
+   [RFC2068]  Fielding, R., Gettys, J., Mogul, J., Nielsen, H., and T.
+              Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1",
+              RFC 2068, January 1997.
+
+   [RFC2817]  Khare, R. and S. Lawrence, "Upgrading to TLS Within
+              HTTP/1.1", RFC 2817, May 2000.
+
+   [RFC2818]  Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000.
+
+   [RFC4287]  Nottingham, M., Ed. and R. Sayre, Ed., "The Atom
+              Syndication Format", RFC 4287, December 2005.
+
+   [RFC4685]  Snell, J., "Atom Threading Extensions", RFC 4685,
+              September 2006.
+
+   [RFC4946]  Snell, J., "Atom License Extension", RFC 4946, July 2007.
+
+   [RFC5005]  Nottingham, M., "Feed Paging and Archiving", RFC 5005,
+              September 2007.
+
+   [RFC5023]  Gregorio, J. and B. de hOra, "The Atom Publishing
+              Protocol", RFC 5023, October 2007.
+
+   [RFC5829]  Brown, A., Clemm, G., and J. Reschke, "Link Relation Types
+              for Simple Version Navigation between Web Resources",
+              RFC 5829, April 2010.
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 19]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   [W3C.CR-css3-mediaqueries-20090915]
+              van Kesteren, A., Glazman, D., Lie, H., and T. Celik,
+              "Media Queries", W3C Candidate Recommendation CR-css3-
+              mediaqueries-20090915, September 2009,
+              <http://www.w3.org/TR/2009/
+              CR-css3-mediaqueries-20090915/>.
+
+              Latest version available at
+              <http://www.w3.org/TR/css3-mediaqueries/>.
+
+   [W3C.CR-curie-20090116]
+              Birbeck, M. and S. McCarron, "CURIE Syntax 1.0", W3C
+              Candidate Recommendation CR-curie-20090116, January 2009,
+              <http://www.w3.org/TR/2009/CR-curie-20090116>.
+
+              Latest version available at <http://www.w3.org/TR/curie>.
+
+   [W3C.REC-html401-19991224]
+              Le Hors, A., Raggett, D., and I. Jacobs, "HTML 4.01
+              Specification", W3C Recommendation REC-html401-19991224,
+              December 1999,
+              <http://www.w3.org/TR/1999/REC-html401-19991224>.
+
+              Latest version available at
+              <http://www.w3.org/TR/html401>.
+
+   [W3C.REC-rdfa-syntax-20081014]
+              Adida, B., Birbeck, M., McCarron, S., and S. Pemberton,
+              "RDFa in XHTML: Syntax and Processing", W3C
+              Recommendation REC-rdfa-syntax-20081014, October 2008,
+              <http://www.w3.org/TR/2008/REC-rdfa-syntax-20081014>.
+
+              Latest version available at
+              <http://www.w3.org/TR/rdfa-syntax>.
+
+   [W3C.REC-xhtml-basic-20080729]
+              Baker, M., Ishikawa, M., Stark, P., Matsui, S., Wugofski,
+              T., and T. Yamakami, "XHTML[TM] Basic 1.1", W3C
+              Recommendation REC-xhtml-basic-20080729, July 2008,
+              <http://www.w3.org/TR/2008/REC-xhtml-basic-20080729>.
+
+              Latest version available at
+              <http://www.w3.org/TR/xhtml-basic>.
+
+
+Appendix A. Link Relation Registry Format
+
+
+   To facilitate applications that wish to use registry data in an
+
+
+
+Nottingham              Expires November 6, 2010               [Page 20]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   automated fashion, this specification defines an XML-based format for
+   the registry entries.
+
+   Each registered relation type is represented by a RelationType
+   element, and if any of the app data values are other than the default
+   value identified in the Application Data Registry, they will be
+   represented by appdata elements.
+
+   Note that this format is NOT that which IANA publishes the registry
+   in, because doing so would subject IANA's servers to, potentially,
+   very high load (e.g., if Web browsers were to automatically update
+   their copies of the registry).  Instead, this format is published to
+   the [TBD-2]@ietf.org mailing list, so that interested implementors
+   can subscribe and distribute the machine-readable document using
+   their own infrastructure.
+
+A.1. Relax NG Grammar
+
+
+   element RelationTypes {
+     element RelationType {
+       attribute name { text },
+       attribute reference { text },
+       element description { text },
+       element notes { text }?,
+       element appdata {
+         attribute name { text },
+         text
+       }*
+     }+
+   }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 21]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+A.2. Example
+
+
+ <RelationTypes>
+   <!--
+   Copyright (c) <year> The IETF Trust
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+   SOFTWARE.
+   -->
+   <RelationType name="example"
+                 reference="http://www.example.org/example_spec">
+     <description>This is an example relation type.</description>
+     <appdata name="foo">This is the value of Foo.</appdata>
+   </RelationType>
+   <!-- ... -->
+ </RelationTypes>
+
+
+Appendix B. Notes on Using the Link Header with the HTML4 Format
+
+
+   HTML motivated the original syntax of the Link header, and many of
+   the design decisions in this document are driven by a desire to stay
+   compatible with these uses.
+
+   In HTML4, the link element can be mapped to links as specified here
+   by using the "href" attribute for the target URI, and "rel" to convey
+   the relation type, as in the Link header.  The context of the link is
+   the URI associated with the entire HTML document.
+
+   All of the link relation types defined by HTML4 have been included in
+   the link relation type registry, so they can be used without
+
+
+
+Nottingham              Expires November 6, 2010               [Page 22]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   modification.  However, there are several potential ways to serialise
+   extension relation types into HTML4, including
+
+   o  As absolute URIs, or
+   o  using the document-wide "profile" attribute's URI as a prefix for
+      relation types, or
+   o  using the RDFa [W3C.REC-rdfa-syntax-20081014] convention of
+      mapping token prefixes to URIs (in a manner similar to XML name
+      spaces) (note that RDFa is only defined to work in XHTML
+      [W3C.REC-xhtml-basic-20080729], but is sometimes used in HTML4).
+
+   Individual applications of linking will therefore need to define how
+   their extension links should be serialised into HTML4.
+
+   Surveys of existing HTML content have shown that unregistered link
+   relation types that are not URIs are (perhaps inevitably) common.
+   Consuming HTML implementations should not consider such unregistered
+   short links to be errors, but rather relation types with a local
+   scope (i.e., their meaning is specific and perhaps private to that
+   document).
+
+   HTML4 also defines several attributes on links that are not
+   explicitly defined by the Link header.  These attributes can be
+   serialised as link-extensions to maintain fidelity.
+
+   Finally, the HTML4 specification gives a special meaning when the
+   "alternate" and "stylesheet" relation types coincide in the same
+   link.  Such links should be serialised in the Link header using a
+   single list of relation-types (e.g., rel="alternate stylesheet") to
+   preserve this relationship.
+
+
+Appendix C. Notes on Using the Link Header with the Atom Format
+
+
+   Atom conveys links in the atom:link element, with the "href"
+   attribute indicating the target IRI and the "rel" attribute
+   containing the relation type.  The context of the link is either a
+   feed IRI or an entry ID, depending on where it appears; generally,
+   feed-level links are obvious candidates for transmission as a Link
+   header.
+
+   When serialising an atom:link into a Link header, it is necessary to
+   convert target IRIs (if used) to URIs.
+
+   Atom defines extension relation types in terms of IRIs.  This
+   specification re-defines them as URIs, to simplify and reduce errors
+   in their comparison.
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 23]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   Atom allows registered link relation types to be serialised as
+   absolute URIs.  Such relation types SHOULD be converted to the
+   appropriate registered form (e.g.,
+   "http://www.iana.org/assignments/relation/self" to "self") so that
+   they are not mistaken for extension relation types.
+
+   Furthermore, Atom link relation types are always compared in a case-
+   sensitive fashion; therefore, registered link relation types SHOULD
+   be converted to their registered form (usually, lower case) when
+   serialised in an Atom document.
+
+   Note also that while the Link header allows multiple relations to be
+   serialised in a single link, atom:link does not.  In this case, a
+   single link-value may map to several atom:link elements.
+
+   As with HTML, atom:link defines some attributes that are not
+   explicitly mirrored in the Link header syntax, but they can also be
+   used as link-extensions to maintain fidelity.
+
+
+Appendix D. Acknowledgements
+
+
+   This specification lifts the idea and definition for the Link header
+   from RFC2068; credit for it belongs entirely to the authors of and
+   contributors to that document.  The link relation type registrations
+   themselves are sourced from several documents; see the applicable
+   references.
+
+   The author would like to thank the many people who commented upon,
+   encouraged and gave feedback to this specification, especially
+   including Frank Ellermann, Roy Fielding, Eran Hammer-Lahav, and
+   Julian Reschke.
+
+
+Appendix E. Document history
+
+
+   [[ to be removed by the RFC editor before publication as an RFC. ]]
+
+   -10 (result of IESG review)
+
+   o  Clarified media BNF.
+   o  Added various security considerations.
+   o  Updated registration procedures.
+   o  Added more detail to 'payment' relation.
+   o  Corrected 'hub' relation.
+
+   -09
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 24]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   o  Corrected ptoken / ptokenchar BNF.
+   o  Disallow multiple title* parameters.
+   o  Prefer title* over title when available.
+   o  Remove "\" from ptokenchar.
+   o  Explain why mailing list isn't archived.
+   o  Define default language for title and title*, based on Content-
+      Language (when present).
+   o  Adjust MAY requirements.
+
+   -08
+
+   o  Licensed machine-readable data under MIT.
+   o  Clarified URI comparison for extension relation types.
+   o  Various editorial tweaks (thanks, Julian!).
+   o  Changed "fields" to "appdata" to avoid confusion, and add example
+      to clarify.
+   o  Defined REV according to HTML2, deprecated.
+   o  Clarified allowable characters in link-extensions.
+   o  Changed RFC2231 reference to draft-reschke-rfc2231-in-http.
+   o  Added hub, latest-version, predecessor-version, successor-version,
+      version-history, working-copy and working-copy-of relation types
+      to initial registry.
+   o  Adjusted text regarding when anchor parameter is appropriate.
+
+   -07
+
+   o  Allowed multiple spaces between relation types.
+   o  Relaxed requirements for registered relations.
+   o  Removed Defining New Link Serialisations appendix.
+   o  Added Field registry.
+   o  Added registry XML format.
+   o  Changed registration procedure to use mailing list(s), giving the
+      Designated Experts more responsibility for the smooth running of
+      the registry.
+   o  Loosened prohibition against media-specific relation types to
+      SHOULD NOT.
+   o  Disallowed registration of media-specific relation types (can
+      still be used as extension types).
+   o  Clarified that parsers are responsible for resolving relative
+      URIs.
+   o  Fixed ABNF for extended-initial-value.
+   o  Fixed title* parameter quoting in example.
+   o  Added notes for registered relations that lack a reference.
+   o  Added hreflang parameter.
+   o  Clarified status of 'rev'.
+   o  Removed advice to use @profile in HTML4.
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 25]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   o  Clarified what multiple *title and hreflang attributes mean.
+   o  Disallowed multiple type, rel and title attributes.
+   o  Removed text about absolute URI form of registered relations.
+   o  Required registered relations to conform to sgml-name (now just
+      rel-relation-type).
+   o  Required registered relations to be lowercase.
+   o  Made comparison of extension relations case insensitive.
+   o  Clarified requirements on registered relation types regarding
+      media types, etc.
+   o  Allowed applications to ignore links with anchor parameters if
+      they're concerned.
+   o  Made 'rev' text a bit less confusing.
+   o  Extension relation URIs SHOULD be all-lowercase.
+   o  Added media parameter.
+   o  Required applications to specifically call out use of anchor
+      parameter.
+
+   -06
+
+   o  Added "up" and "service" relation types.
+   o  Fixed "type" attribute syntax and added prose.
+   o  Added note about RDFa and XHTML to HTML4 notes.
+   o  Removed specific location for the registry, since IANA seems to
+      have its own ideas about that.
+
+   -05
+
+   o  Clarified how to resolve relative URIs in the 'anchor' parameter.
+   o  Tweaked language about dereferencing relation type URIs.
+   o  Separated out examples.
+   o  Made target-parameters more explicit in the model.
+   o  Discourage special semantics between different relations, or based
+      upon cardinality.
+   o  Grandfathered in special semantics of 'alternate stylesheet' for
+      HTML4.
+   o  Note that extension types can be serialised in ways other than as
+      URIs, as long as they can be converted to URIs.
+   o  Change default context of a link header to that of the requested
+      resource.
+   o  Use this document as reference for relations that don't have a
+      formal definition other than the registry entries; avoids circular
+      references.
+   o  Noted that ordering of links is not significant or defined in this
+      spec, but may be in specific applications.
+   o  Adjusted uses of 'application' to 'serialisation' where
+      appropriate.
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 26]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   o  Added 'Defining New Link Serialisations' section.
+   o  Added note about case sensitivity when comparing registered
+      relation types in Atom.
+
+   -04
+
+   o  Defined context as a resource, rather than a representation.
+   o  Removed concept of link directionality; relegated to a deprecated
+      Link header extension.
+   o  Relation types split into registered (non-URI) and extension
+      (URI).
+   o  Changed wording around finding URIs for registered relation types.
+   o  Changed target and context URIs to IRIs (but not extension
+      relation types).
+   o  Add RFC2231 encoding for title parameter, explicit BNF for title*.
+   o  Add i18n considerations.
+   o  Specify how to compare relation types.
+   o  Changed registration procedure to Designated Expert.
+   o  Softened language around presence of relations in the registry.
+   o  Added describedby relation.
+   o  Re-added 'anchor' parameter, along with security consideration for
+      third-party anchors.
+   o  Softened language around HTML4 attributes that aren't directly
+      accommodated.
+   o  Various tweaks to abstract, introduction and examples.
+
+   -03
+
+   o  Inverted focus from Link headers to link relations.
+   o  Specified was a link relation type is.
+   o  Based on discussion, re-added 'rev'.
+   o  Changed IESG Approval to IETF Consensus for relation registrations
+      (i.e., require a document).
+   o  Updated RFC2434 reference to RFC5226.
+   o  Registered relations SHOULD conform to sgml-name.
+   o  Cautioned against confusing relation types with media types.
+
+   -02
+
+   o  Dropped XLink language.
+   o  Removed 'made' example.
+   o  Removed 'rev'.  Can still be used as an extension.
+   o  Added HTML reference to introduction.
+   o  Required relationship values that have a ; or , to be quoted.
+   o  Changed base URI for relation values.
+   o  Noted registry location.
+
+
+
+
+
+Nottingham              Expires November 6, 2010               [Page 27]
+
+ 
+Internet-Draft                 Web Linking                      May 2010
+
+
+   o  Added advisory text about HTML profile URIs.
+   o  Disallowed registration of relations that only differ in case.
+   o  Clarified language about IRIs in Atom.
+   o  Added descriptions for 'first', 'last', and 'payment', referring
+      to current IANA registry entries, as these were sourced from
+      e-mail.  Will this cause self-referential implosion?
+   o  Explicitly updates RFC4287.
+   o  Added 'type' parameter.
+   o  Removed unnecessary advice about non-HTML relations in HTML
+      section.
+
+   -01
+
+   o  Changed syntax of link-relation to one or more URI; dropped
+      Profile.
+   o  Dropped anchor parameter; can still be an extension.
+   o  Removed Link-Template header; can be specified by templates spec
+      or elsewhere.
+   o  Straw-man for link relation registry.
+
+   -00
+
+   o  Initial draft; normative text lifted from RFC2068.
+
+
+Author's Address
+
+   Mark Nottingham
+
+   Email: mnot at mnot.net
+   URI:   http://www.mnot.net/
+
+

Added: labs/jbosstm/trunk/rest-tx/pom.xml
===================================================================
--- labs/jbosstm/trunk/rest-tx/pom.xml	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/pom.xml	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+		JBoss, Home of Professional Open Source Copyright 2009, Red Hat
+		Middleware LLC, and others contributors as indicated by the @authors
+		tag. All rights reserved. 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.
+		-->
+<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>1.0-M1-SNAPSHOT</version>
+	<packaging>pom</packaging>
+	<name>RESTful Transactions</name>
+	<url>http://www.jboss.org/jbosstm</url>
+
+	<modules>
+		<module>tx</module>
+		<module>webservice</module>
+	</modules>
+
+	<properties>
+		<version.artifactId>1.0-M1-SNAPSHOT</version.artifactId>
+		<artifactId.name>rest-tx</artifactId.name>
+		<version.org.jboss.resteasy>2.0.0.GA</version.org.jboss.resteasy>
+	</properties>
+
+</project>

Added: labs/jbosstm/trunk/rest-tx/tx/pom.xml
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/pom.xml	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/pom.xml	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	JBoss, Home of Professional Open Source Copyright 2010, Red Hat
+	Middleware LLC, and others contributors as indicated by the @authors
+	tag. All rights reserved. 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.
+-->
+
+<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">
+    <parent>
+        <groupId>org.jboss.jbossts.rts</groupId>
+        <artifactId>rest-tx</artifactId>
+        <version>1.0-M1-SNAPSHOT</version>
+    </parent>
+
+   <modelVersion>4.0.0</modelVersion>
+   <artifactId>rest-tx-api</artifactId>
+   <packaging>jar</packaging>
+   <name>RESTful API for Atomic Transactions</name>
+   <description>Provides Transactions for web applications</description>
+
+    <properties>
+        <version.org.jboss.resteasy>2.0.0.GA</version.org.jboss.resteasy>
+        <version.artifactId>1.0-M1-SNAPSHOT</version.artifactId>
+        <artifactId.name>rest-tx</artifactId.name>
+        <version.org.jboss.jbossas>6.0.0.20100721-M4</version.org.jboss.jbossas>
+    </properties>
+
+   <repositories>
+        <repository>
+            <id>jboss-public-repository-group</id>
+            <name>JBoss Public Maven Repository Group</name>
+            <url>https://repository.jboss.org/nexus/content/groups/public/</url>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+        </repository>
+<!-- Jersey -->
+        <repository>
+            <id>maven2-repository.dev.java.net</id>
+            <name>Java.net Repository for Maven</name>
+            <url>http://download.java.net/maven/2/</url>
+            <layout>default</layout>
+        </repository>
+<!-- end Jersey -->       
+   </repositories>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.jboss.jbossas</groupId>
+                <artifactId>jboss-as-component-matrix</artifactId>
+                <version>6.0.0.20100721-M4</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+   <dependencies>
+
+<!-- Jersey -->
+
+	<dependency>
+    	<groupId>asm</groupId>
+    	<artifactId>asm-all</artifactId>
+    	<version>3.1</version>
+    	<scope>test</scope>
+	</dependency>
+       
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-server</artifactId>
+            <version>1.5-SNAPSHOT</version>
+    		<scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.jersey-test-framework</groupId>
+            <artifactId>jersey-test-framework-grizzly</artifactId>
+            <version>1.5-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.grizzly</groupId>
+            <artifactId>grizzly-servlet-webserver</artifactId>
+            <version>1.9.18-i</version>
+    		<scope>test</scope>
+        </dependency>
+
+<!-- end Jersey -->
+	  <dependency>
+		<groupId>org.jboss.jbossts</groupId>
+		<artifactId>jbossjts</artifactId>
+		<scope>provided</scope>
+	  </dependency>
+	<dependency>
+	  <groupId>commons-logging</groupId>
+	  <artifactId>commons-logging</artifactId>
+	  <scope>provided</scope>
+	</dependency>
+	<dependency>
+	  <groupId>junit</groupId>
+	  <artifactId>junit</artifactId>
+	  <scope>test</scope>
+	</dependency>
+
+    <dependency>
+      <groupId>org.codehaus.jettison</groupId>
+      <artifactId>jettison</artifactId>
+	  <scope>provided</scope>
+	</dependency>
+
+	<dependency>
+		 <groupId>org.jboss.resteasy</groupId>
+		 <artifactId>resteasy-jaxrs</artifactId>
+		 <version>${version.org.jboss.resteasy}</version>
+	     <scope>provided</scope>
+
+		 <!-- filter out unwanted jars -->
+		 <exclusions>
+			<exclusion>
+			   <groupId>commons-httpclient</groupId>
+			   <artifactId>commons-httpclient</artifactId>
+			</exclusion>
+			<exclusion>
+			   <groupId>javax.servlet</groupId>
+			   <artifactId>servlet-api</artifactId>
+			</exclusion>
+		 </exclusions>
+	</dependency>
+	<dependency>
+		  <groupId>org.jboss.resteasy</groupId>
+		  <artifactId>resteasy-jaxb-provider</artifactId>
+		  <version>${version.org.jboss.resteasy}</version>
+		  <scope>provided</scope>
+	</dependency>
+	<dependency>
+		  <groupId>org.jboss.resteasy</groupId>
+		  <artifactId>resteasy-jettison-provider</artifactId>
+		  <version>${version.org.jboss.resteasy}</version>
+		  <scope>provided</scope>
+	</dependency>
+	<dependency>
+		 <groupId>org.jboss.resteasy</groupId>
+		 <artifactId>jaxrs-api</artifactId>
+		 <version>${version.org.jboss.resteasy}</version>
+		  <scope>provided</scope>
+	</dependency>
+	<dependency>
+	  <groupId>commons-httpclient</groupId>
+	  <artifactId>commons-httpclient</artifactId>
+		  <scope>provided</scope>
+	</dependency>
+	<dependency>
+		<groupId>org.jboss.resteasy</groupId>
+		<artifactId>tjws</artifactId>
+		<version>${version.org.jboss.resteasy}</version>
+		  <scope>provided</scope>
+	</dependency>
+
+   </dependencies>
+
+   <build>
+	  <plugins>
+		 <plugin>
+			<groupId>org.apache.maven.plugins</groupId>
+			<artifactId>maven-compiler-plugin</artifactId>
+			<configuration>
+			   <source>1.6</source>
+			   <target>1.6</target>
+			</configuration>
+		 </plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-source-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>create_sources_jar</id>
+						<goals> <goal>jar</goal> </goals>
+						<phase>prepare-package</phase>
+					</execution>
+				</executions>
+				<configuration>
+					<forceCreation>true</forceCreation>
+					<includePom>true</includePom>
+				</configuration>
+			</plugin>
+
+     <plugin>
+       <groupId>org.apache.maven.plugins</groupId>
+       <artifactId>maven-jar-plugin</artifactId>
+       <version>2.2</version>
+       <executions>
+         <execution>
+           <goals>
+             <goal>test-jar</goal>
+           </goals>
+         </execution>
+       </executions>
+     </plugin>
+
+	  </plugins>
+   </build>
+</project>

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/HttpResponseException.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/HttpResponseException.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/HttpResponseException.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,75 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.star.provider;
+
+/**
+ * Exception for wrapping unexpected http response codes
+ */
+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 HttpResponseException(Throwable cause, String body, int[] expectedResponses, int actualResponse)
+    {
+        this(cause, body, (expectedResponses != null && expectedResponses.length != 0 ? expectedResponses[0] : -1), actualResponse);
+    }
+    
+    public HttpResponseException(int expectedResponse, int actualResponse)
+    {
+        this(null, null, expectedResponse, 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/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/HttpResponseMapper.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/HttpResponseMapper.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/HttpResponseMapper.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,37 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.provider;
+
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.core.Response;
+
+/**
+ * Maps an HttpResponseException to the HTTP code embedded in the exception
+ */
+ at Provider
+public class HttpResponseMapper implements ExceptionMapper<HttpResponseException>
+{
+   public Response toResponse(HttpResponseException exception)
+   {
+      return Response.status(exception.getActualResponse()).build();
+   }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/NotFoundMapper.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/NotFoundMapper.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/NotFoundMapper.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.star.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;
+
+/**
+ * 404 mapper
+ */
+ at Provider
+public class NotFoundMapper implements ExceptionMapper<ResourceNotFoundException>
+{
+   public Response toResponse(ResourceNotFoundException exception)
+   {
+      return Response.status(HttpResponseCodes.SC_NOT_FOUND).build();
+   }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/ResourceNotFoundException.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/ResourceNotFoundException.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/ResourceNotFoundException.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,33 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.star.provider;
+
+/**
+ * 404 exception
+ */
+public class ResourceNotFoundException extends RuntimeException
+{
+
+    public ResourceNotFoundException(String message)
+    {
+        super(message);
+    }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TMUnavailableException.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TMUnavailableException.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TMUnavailableException.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,32 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.star.provider;
+
+/**
+ * Unable to contact the REST based Transaction Coordinator
+ */
+public class TMUnavailableException extends RuntimeException
+{
+    public TMUnavailableException(String message)
+    {
+        super(message);
+    }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TMUnavailableMapper.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TMUnavailableMapper.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TMUnavailableMapper.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.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;
+
+/**
+ * Map service unavailable exceptions
+ */
+ at Provider
+public class TMUnavailableMapper implements ExceptionMapper<TMUnavailableException>
+{
+   public Response toResponse(TMUnavailableException exception)
+   {
+      return Response.status(HttpResponseCodes.SC_SERVICE_UNAVAILABLE).build();
+   }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TransactionStatusException.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TransactionStatusException.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TransactionStatusException.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,32 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.star.provider;
+
+/**
+ * transaction status exception
+ */
+public class TransactionStatusException extends RuntimeException
+{
+    public TransactionStatusException(String message)
+    {
+        super(message);
+    }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TransactionStatusMapper.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TransactionStatusMapper.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/provider/TransactionStatusMapper.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+ package org.jboss.jbossts.star.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;
+
+/**
+ * map transaction status exceptions
+ */
+ at Provider
+public class TransactionStatusMapper implements ExceptionMapper<TransactionStatusException>
+{
+   public Response toResponse(TransactionStatusException exception)
+   {
+      return Response.status(HttpResponseCodes.SC_CONFLICT).build();
+   }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/RESTRecord.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/RESTRecord.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/RESTRecord.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,502 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.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.state.OutputObjectState;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+
+import org.jboss.jbossts.star.util.LinkHolder;
+import org.jboss.jbossts.star.provider.HttpResponseException;
+import org.jboss.jbossts.star.util.TxSupport;
+import org.jboss.resteasy.util.HttpResponseCodes;
+import org.apache.log4j.Logger;
+
+/**
+ * Log record for driving participants through 2PC and recoverery
+ */
+public class RESTRecord extends AbstractRecord
+{
+    protected final static Logger log = Logger.getLogger(RESTRecord.class);
+    private String terminateUrl;
+    private String participantUrl;
+
+    private String coordinatorUrl;
+
+    private String coordinatorID;
+    private String status;
+    private String txId;
+    private boolean prepared;
+    private String recoveryUrl;
+
+    public RESTRecord(String coordinatorUrl, String participantUrl, String terminateUrl, String txId)
+    {
+        super(new Uid());
+
+        if (log.isTraceEnabled())
+            log.trace("RESTRecord(" + coordinatorUrl + ", " + participantUrl + ", " + terminateUrl + ", " + txId + ')');
+
+        this.participantUrl = participantUrl;
+        this.terminateUrl = terminateUrl;
+        this.coordinatorUrl = coordinatorUrl;
+        this.txId = txId;
+
+        coordinatorID = get_uid().fileStringForm();
+        status = "";
+        recoveryUrl = "";
+    }
+
+    String getParticipant()
+    {
+        return terminateUrl;
+    }
+
+    public int typeIs()
+    {
+        return RecordType.USER_DEF_FIRST0;
+    }
+
+    public Object value()
+    {
+        return status;
+    }
+
+    public void setValue(Object o)
+    {
+    }
+
+    public int nestedAbort()
+    {
+        return TwoPhaseOutcome.FINISH_OK;
+    }
+
+    public int nestedCommit()
+    {
+        return TwoPhaseOutcome.FINISH_OK;
+    }
+
+    /*
+    * Not sub-transaction aware.
+    */
+    public int nestedPrepare()
+    {
+        return TwoPhaseOutcome.PREPARE_OK; // do nothing
+    }
+
+	private void check_suspend(Fault f)
+	{
+        if (fault.equals(f))
+		{
+            try
+            {
+            	log.info(f + ": for 10 seconds");
+                Thread.sleep(10000);
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+	}
+
+	private void check_halt(Fault f)
+	{
+        if (fault.equals(f))
+		{
+            log.info(f + ": halt VM");
+            Runtime.getRuntime().halt(1);
+		}
+	}
+
+    private int statusToOutcome()
+    {
+        return statusToOutcome(status);
+    }
+
+    private int statusToOutcome(String status)
+    {
+        if (TxSupport.H_COMMIT.equals(status))
+            return TwoPhaseOutcome.HEURISTIC_COMMIT;
+        else if (TxSupport.H_ROLLBACK.equals(status))
+            return TwoPhaseOutcome.HEURISTIC_ROLLBACK;
+        else if (TxSupport.H_MIXED.equals(status))
+            return TwoPhaseOutcome.HEURISTIC_MIXED;
+        else if (TxSupport.H_HAZARD.equals(status))
+            return TwoPhaseOutcome.HEURISTIC_HAZARD;
+        else if (TxSupport.ABORT_ONLY.equals(status))
+            return TwoPhaseOutcome.FINISH_OK;
+        else if (TxSupport.ABORTED.equals(status))
+            return TwoPhaseOutcome.FINISH_OK;
+        else if (TxSupport.COMMITTED.equals(status))
+            return TwoPhaseOutcome.FINISH_OK;
+        else if (TxSupport.PREPARED.equals(status))
+            return TwoPhaseOutcome.PREPARE_OK;
+        else if (TxSupport.READONLY.equals(status))
+            return TwoPhaseOutcome.PREPARE_READONLY;
+
+/*
+        else if (TxSupport.PREPARING.equals(status))
+            return TwoPhaseOutcome.PREPARE_NOTOK;
+        else if (TxSupport.RUNNING.equals(status))
+            return TwoPhaseOutcome.FINISH_ERROR;
+        else if (TxSupport.UNKNOWN.equals(status))
+            return TwoPhaseOutcome.FINISH_ERROR;
+        else if (TxSupport.ABORTING.equals(status))
+            return TwoPhaseOutcome.FINISH_ERROR;
+        else if (TxSupport.COMMITTING.equals(status))
+            return TwoPhaseOutcome.FINISH_ERROR;
+*/
+        else
+            return TwoPhaseOutcome.FINISH_ERROR;
+    }
+
+    public boolean forgetHeuristic() {
+        if (log.isTraceEnabled())
+            log.trace("forgetting heuristic for " + terminateUrl);
+
+        try {
+            new TxSupport().httpRequest(new int[] {HttpResponseCodes.SC_OK, HttpResponseCodes.SC_NO_CONTENT},
+                    this.participantUrl, "DELETE", null, null, null);
+            status = "";
+        } catch (HttpResponseException e) {
+            return false;
+        }
+
+        return super.forgetHeuristic();
+    }
+
+    public int topLevelPrepare()
+    {
+        if (log.isTraceEnabled())
+            log.trace("prepare " + terminateUrl);
+
+        check_halt(Fault.prepare_halt);
+        check_suspend(Fault.prepare_suspend);
+
+        if (fault.equals(Fault.h_hazard))
+            return TwoPhaseOutcome.HEURISTIC_HAZARD;
+
+        if (terminateUrl == null || txId == null)
+            return TwoPhaseOutcome.PREPARE_READONLY;
+
+        try
+        {
+            status = TxSupport.getStatus(new TxSupport().httpRequest(new int[] {HttpResponseCodes.SC_OK}, this.terminateUrl, "PUT",
+                    TxSupport.STATUS_MEDIA_TYPE, TxSupport.toStatusContent(TxSupport.PREPARED), null));
+
+            prepared = true;
+
+            int outcome = statusToOutcome();
+
+            if (outcome != TwoPhaseOutcome.FINISH_ERROR)
+                return outcome;
+        }
+        catch (HttpResponseException e)
+        {
+            if (checkFinishError(e.getActualResponse(), false)) {
+                status = TxSupport.toStatusContent(TxSupport.PREPARED);
+                return TwoPhaseOutcome.PREPARE_OK;
+            } else {
+                status = TxSupport.toStatusContent(TxSupport.ABORTED);
+            }
+        }
+
+        return TwoPhaseOutcome.PREPARE_NOTOK;
+    }
+
+    public int topLevelAbort()
+    {
+        if (log.isTraceEnabled())
+            log.debug("trace " + terminateUrl);
+
+        check_halt(Fault.abort_halt);
+        check_suspend(Fault.abort_suspend);
+
+        if (terminateUrl == null || txId == null)
+            return TwoPhaseOutcome.FINISH_ERROR;
+
+        try {
+            status = TxSupport.getStatus(new TxSupport().httpRequest(new int[] {HttpResponseCodes.SC_OK}, this.terminateUrl, "PUT", TxSupport.STATUS_MEDIA_TYPE,
+                    TxSupport.toStatusContent(TxSupport.ABORTED), null));
+        } catch (HttpResponseException e) {
+
+            if (checkFinishError(e.getActualResponse(), false))
+                return TwoPhaseOutcome.FINISH_OK;
+        }
+
+        return statusToOutcome();
+    }
+
+    public int topLevelCommit()
+    {
+        if (log.isTraceEnabled())
+            log.trace("commit " + terminateUrl);
+
+        if (terminateUrl == 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()
+    {
+        check_halt(Fault.commit_halt);
+        check_suspend(Fault.commit_suspend);
+
+        if (txId == null)
+            return TwoPhaseOutcome.FINISH_ERROR;
+
+        try
+        {
+            if (log.isTraceEnabled())
+                log.trace("committing " + this.terminateUrl);
+            
+            if (!TxSupport.isReadOnly(status))
+                status = TxSupport.getStatus(new TxSupport().httpRequest(new int[] {HttpResponseCodes.SC_OK}, this.terminateUrl, "PUT", TxSupport.STATUS_MEDIA_TYPE,
+                        TxSupport.toStatusContent(TxSupport.COMMITTED), null));   // ONE_PHASE_COMMIT_CONTENT
+            else
+                status = TxSupport.COMMITTED;
+
+            if (log.isTraceEnabled())
+                log.trace("COMMIT OK at terminateUrl: " + this.terminateUrl);
+        }
+        catch (HttpResponseException e)
+        {
+            checkFinishError(e.getActualResponse(), true);
+        }
+
+        return statusToOutcome(status);
+    }
+
+    private boolean checkFinishError(int expected, boolean commit) throws HttpResponseException
+    {
+        if (expected == HttpResponseCodes.SC_NOT_FOUND)
+        {
+            // the participant may have moved so check the coordinator terminateUrl
+            if (hasParticipantMoved())
+            {
+                if (log.isDebugEnabled())
+                    log.debug("participant has moved commit to new terminateUrl " + this.terminateUrl);
+
+                try
+                {
+                    TxSupport.getStatus(new TxSupport().httpRequest(new int[] {HttpResponseCodes.SC_OK}, this.terminateUrl, "PUT", TxSupport.STATUS_MEDIA_TYPE,
+                        TxSupport.toStatusContent(commit ? TxSupport.COMMITTED : TxSupport.ABORTED), null));
+                    if (log.isDebugEnabled())
+                        log.debug("Finish OK at new terminateUrl: " + this.terminateUrl);
+
+                    status = (commit ? TxSupport.COMMITTED : TxSupport.ABORTED);
+
+                    return true;
+                }
+                catch (HttpResponseException e1)
+                {
+                    if (log.isTraceEnabled())
+                        log.trace("Finish still failing at new URI: " + e1);
+
+                    if (log.isInfoEnabled())
+                        log.debug("participant " + this.terminateUrl + " commit error: " + e1.getMessage());
+                }
+            }
+        }
+
+        status = TxSupport.RUNNING;
+
+        return false;
+    }
+
+    /**
+     * A participant tells the coordinator if it changes its URL.
+     * To see if this has happened perform a GET on the recovery terminateUrl which returns the
+     * last known location of the participant.
+     * @return true if the participant did move
+     */
+    private boolean hasParticipantMoved()
+    {
+        try
+        {
+            if (log.isTraceEnabled())
+                log.trace("seeing if participant has moved: " + coordinatorID + " recoveryUrl: " + recoveryUrl);
+
+            if (recoveryUrl.length() == 0)
+                    return false;
+
+            // get the latest participant terminateUrl by probing the recovery terminateUrl:
+            String content = new TxSupport().httpRequest(new int[] {HttpResponseCodes.SC_OK}, recoveryUrl, "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+            String terminator = new LinkHolder(content).get(TxSupport.TERMINATOR_LINK);
+            String participant = new LinkHolder(content).get(TxSupport.PARTICIPANT_LINK);
+
+            if (terminator != null && !terminator.equals(this.terminateUrl))
+            {
+                // participant has moved so remember the new location
+                this.participantUrl = participant;
+                this.terminateUrl = terminator;
+
+            	if (log.isTraceEnabled())
+                	log.trace("... yes it has - new terminateUrl is " + terminator);
+
+                return true;
+            }
+        }
+        catch (HttpResponseException e)
+        {
+            if (log.isTraceEnabled())
+                log.trace("participant has not moved: " + e);
+        }
+
+        return false;
+    }
+
+    public boolean save_state(OutputObjectState os, int t)
+    {
+        try
+        {
+            os.packString(txId);
+            os.packBoolean(prepared);
+            os.packString(participantUrl);
+            os.packString(terminateUrl);
+            os.packString(coordinatorUrl);
+            os.packString(recoveryUrl);
+            os.packString(coordinatorID);
+            os.packString(status);
+
+            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();
+            participantUrl = os.unpackString();
+            terminateUrl = os.unpackString();
+            coordinatorUrl = os.unpackString();
+            recoveryUrl = os.unpackString();
+            coordinatorID = os.unpackString();
+            status = os.unpackString();
+
+            if (log.isInfoEnabled())
+                log.info("restore_state " + terminateUrl);
+
+            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;
+    }
+
+    public void setRecoveryUrl(String recoveryUrl) {
+        this.recoveryUrl = recoveryUrl;
+    }
+
+    // TODO remove fault injection code - use byteman instead
+    enum Fault {
+		abort_halt, abort_suspend, prepare_halt,
+		prepare_suspend, commit_halt, commit_suspend,
+		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))
+            {
+                log.trace("setFault: " + f + " terminateUrl: " + terminateUrl);
+
+                fault = f;
+                return;
+            }
+        }
+
+        fault = Fault.none;
+    }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/Transaction.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/Transaction.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/resource/Transaction.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,276 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.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 com.arjuna.ats.arjuna.coordinator.RecordListIterator;
+import org.jboss.jbossts.star.util.TxSupport;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlAttribute;
+import java.util.Collection;
+
+ at XmlRootElement(name = "transaction")
+public class Transaction extends AtomicAction
+{
+    private long age = System.currentTimeMillis();
+    private String initiator;
+    private String recoveryUrl = null;
+
+    public Transaction()
+    {
+        super();
+    }
+
+    public Transaction(String initiator)
+    {
+        this();
+        this.initiator = initiator;
+    }
+
+    @XmlElement
+    public String getInitiator()
+    {
+        return initiator;
+    }
+
+    @XmlElement
+    public String getAge()
+    {
+        return Long.toString(System.currentTimeMillis() - age);
+    }
+
+    @XmlAttribute
+    public String getStatus()
+    {
+        return getStatus(status());
+    }
+
+    public String getStatus(int status)
+    {
+        switch (status) {
+        case ActionStatus.ABORT_ONLY:
+            return TxSupport.ABORT_ONLY;
+        case ActionStatus.ABORTING:
+            return TxSupport.ABORTING;
+        case ActionStatus.ABORTED:
+            return TxSupport.ABORTED;
+        case ActionStatus.COMMITTING:
+            return TxSupport.COMMITTING;
+        case ActionStatus.COMMITTED:
+            return TxSupport.COMMITTED;
+        case ActionStatus.H_ROLLBACK:
+            return TxSupport.H_ROLLBACK;
+        case ActionStatus.H_COMMIT:
+            return TxSupport.H_COMMIT;
+        case ActionStatus.H_HAZARD:
+            return TxSupport.H_HAZARD;
+        case ActionStatus.H_MIXED:
+            return TxSupport.H_MIXED;
+        case ActionStatus.PREPARING:
+            return TxSupport.PREPARING;
+        case ActionStatus.PREPARED:
+            return TxSupport.PREPARED;
+        case ActionStatus.RUNNING:
+            return TxSupport.RUNNING;
+        default:
+            return ""; //ActionStatus.stringForm(super.status());
+        }
+
+    }
+
+    public String getRecoveryUrl()
+    {
+        return recoveryUrl;
+    }
+
+    public String enlistParticipant(String coordinatorUrl, String participantUrl, String terminateUrl, String recoveryUrlBase) {
+        if (findParticipant(terminateUrl) != null)
+            return null;    // already enlisted
+
+        RESTRecord p = new RESTRecord(coordinatorUrl, participantUrl, terminateUrl, get_uid().fileStringForm());
+        String coordinatorId = p.get_uid().fileStringForm();
+        recoveryUrl = recoveryUrlBase + coordinatorId;
+        p.setRecoveryUrl(recoveryUrl);
+
+        if (add(p) != AddOutcome.AR_REJECTED)
+            return coordinatorId;
+
+        return null;
+    }
+
+    /**
+     * Determine whether a participant is enlisted in this transaction and the commitment
+     * is not running.
+     *
+     * @param participantUrl the participant url to search for
+     * @return false if the participant is not enlisted or if the commitment protocol is
+     * already running
+     */
+    public boolean isEnlisted(String participantUrl) {
+        return findParticipant(participantUrl) != null;
+    }
+
+    public boolean forgetParticipant(String participantUrl) {
+        RESTRecord rr = findParticipant(participantUrl);
+
+        if (rr != null)
+            return pendingList.remove(rr);
+
+        return false;
+    }
+
+    public void getParticipants(Collection<String> enlistmentIds)
+    {
+        if (pendingList == null)
+            return;
+
+        // only add faults for pending records
+        AbstractRecord r = pendingList.peekFront();
+
+        while (r != null)
+        {
+            if (r instanceof RESTRecord)
+                enlistmentIds.add(r.get_uid().fileStringForm());
+
+            r = pendingList.peekNext(r);
+        }
+    }
+
+    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 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 isFinishing()
+    {
+        switch ( status() )
+        {
+            case ActionStatus.PREPARING  :
+            case ActionStatus.COMMITTING   :
+            case ActionStatus.ABORTING    :
+                return true;
+            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 isRunning()
+    {
+        switch ( status() )
+        {
+            case ActionStatus.RUNNING    :
+                return true;
+            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;
+        }
+    }
+
+    public boolean isAborted() {
+        return status() == ActionStatus.ABORTED;
+    }
+
+
+    private RESTRecord findParticipant(String participantUrl) {
+        if (pendingList != null) {
+            RecordListIterator i = new RecordListIterator(pendingList);
+            AbstractRecord r;
+
+            while ((r = i.iterate()) != null) {
+                if (r instanceof RESTRecord) {
+                    RESTRecord rr = (RESTRecord) r;
+
+                    if (rr.getParticipant().equals(participantUrl))
+                        return rr;
+                }
+            }
+        }
+
+        return null;
+    }
+
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/ContextListener.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/ContextListener.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/ContextListener.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,36 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.service;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+public class ContextListener implements ServletContextListener
+{
+	public void contextInitialized(ServletContextEvent event)
+	{
+//        String value = event.getServletContext().getInitParameter("resteasy.servlet.mapping.prefix");
+        System.setProperty("resttx.context.path", event.getServletContext().getContextPath());
+	}
+
+	public void contextDestroyed(ServletContextEvent event) {
+	}
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/Coordinator.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/Coordinator.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/Coordinator.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,427 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.service;
+
+import org.jboss.jbossts.star.provider.ResourceNotFoundException;
+import org.jboss.jbossts.star.resource.Transaction;
+import org.jboss.jbossts.star.provider.TransactionStatusException;
+import org.jboss.jbossts.star.util.LinkHolder;
+import org.jboss.jbossts.star.util.TxSupport;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.*;
+
+import org.jboss.resteasy.util.HttpResponseCodes;
+
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.util.*;
+
+import org.apache.log4j.Logger;
+
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.AtomicAction;
+
+ at Path(TxSupport.TX_PATH)
+public class Coordinator
+{
+    protected final static Logger log = Logger.getLogger(Coordinator.class);
+
+    private static Map<String, Transaction> transactions = Collections.synchronizedMap(new HashMap());
+    // each participant may only be enlisted in one transaction
+    private static Map<String, LinkHolder> participants = Collections.synchronizedMap(new HashMap());
+
+    /**
+     * Performing a GET on the transaction-manager returns a list of all transaction URIs
+     * known to the coordinator (active and in recovery) separated by
+     * the @see TxSupport.URI_SEPARATOR character
+    */
+    @GET
+    @Path(TxSupport.TX_SEGMENT)
+    @Produces(TxSupport.PLAIN_MEDIA_TYPE)
+    public Response getAllTransactions(@Context UriInfo info)//, String content)
+    {
+        log.trace("coordinator: list: transaction-coordinator");
+        StringBuilder txns = new StringBuilder();
+        Iterator<String> i = transactions.keySet().iterator();
+
+        while (i.hasNext()) {
+            URI uri = TxSupport.getUri(info, info.getPathSegments().size(), i.next());
+
+            txns.append(uri.toString());
+
+            if (i.hasNext())
+                txns.append(TxSupport.URI_SEPARATOR);
+        }
+
+        /*
+         * Note, to return only recovering or only active txns use:
+         * getTransactions(recoveringFilter); or
+         * getTransactions(activeTxFilter);
+         */
+
+        return Response.ok(txns.toString()).build();
+    }
+
+    /**
+     * Performing a GET on the transaction url returns its status
+     * @see org.jboss.jbossts.star.util.TxSupport.TX_ACTIVE
+     *  etc for the format of the returned content
+     * @param id URL template parameter for the id of the transaction
+     * @return content representing the status of the transaction
+     */
+    @GET
+    @Path(TxSupport.TX_SEGMENT + "/{id}")
+    @Produces(TxSupport.STATUS_MEDIA_TYPE)
+    public Response getTransactionStatus(@PathParam("id") String id)
+    {
+        log.trace("coordinator: status: transaction-coordinator/" + id);
+        Transaction txn = transactions.get(id);
+
+        if (txn == null)
+            return Response.status(HttpResponseCodes.SC_NOT_FOUND).build();
+
+        return Response.ok(TxSupport.toStatusContent(txn.getStatus())).build();
+    }
+
+    /**
+     * Performing a DELETE on the transaction-coordinator URL will return a 403.
+     * @param id transaction id
+     * @return 403
+     */
+    @DELETE
+    @Path(TxSupport.TX_SEGMENT + "/{id}")
+    public Response deleteTransaction(@PathParam("id") String id)
+    {
+        return Response.status(HttpResponseCodes.SC_FORBIDDEN).build();
+    }
+
+    // Performing HEAD, GET, POST, DELETE and OPTIONS on the transaction
+    // url generates a 400 status code
+    @HEAD @Path(TxSupport.TX_SEGMENT + "/{TxId}/terminate")
+    public Response tt1(@PathParam("TxId")String txId) {
+        return Response.status(HttpResponseCodes.SC_BAD_REQUEST).build();
+    }
+    @GET @Path(TxSupport.TX_SEGMENT + "/{TxId}/terminate")
+    public Response tt2(@PathParam("TxId")String txId) {
+        return Response.status(HttpResponseCodes.SC_BAD_REQUEST).build();
+    }
+    @POST @Path(TxSupport.TX_SEGMENT + "/{TxId}/terminate")
+    public Response tt3(@PathParam("TxId")String txId) {
+        return Response.status(HttpResponseCodes.SC_BAD_REQUEST).build();
+    }
+    @DELETE @Path(TxSupport.TX_SEGMENT + "/{TxId}/terminate")
+    public Response tt4(@PathParam("TxId")String txId) {
+        return Response.status(HttpResponseCodes.SC_BAD_REQUEST).build();
+    }
+    @OPTIONS @Path(TxSupport.TX_SEGMENT + "/{TxId}/terminate")
+    public Response tt5(@PathParam("TxId")String txId) {
+        return Response.status(HttpResponseCodes.SC_BAD_REQUEST).build();
+    }
+
+    /**
+     *
+     * Performing a POST on Transaction Manager URL @see TxSupport.TX_SEGMENT with no content
+     * as shown below will start a new transaction with a default timeout.
+     * A successful invocation will return 201 and the Location header MUST contain the URI
+     * of the newly created transaction resource, which we will refer to as the transaction-coordinator
+     * in the rest of this specification. Two related URLs MUST also be returned,
+     * one for use by the terminator of the transaction (typically referred to as the client)
+     * and one used for registering durable participation in the transaction (typically referred
+     * to as the server). These linked URLs can be of arbitrary format.
+     * The rel names for the links are:
+     * @see TxSupport.TERMINATOR_LINK and @see TxSupport.PARTICIPANT_LINK
+     *
+     * @param info uri context
+     * @param headers http headers
+     * @param content empty if no transaction timeout is required otherwise the number of milliseconds
+     * after which the transaction is eligible for being timed out. The content should have the format
+     * @ see TxSupport.TIMEOUT_PROPERTY=<milliseconds>
+     * @return http status code
+     */
+    @POST
+    @Path(TxSupport.TX_SEGMENT + "/")
+    @Consumes(TxSupport.POST_MEDIA_TYPE)
+//    @Produces("application/vnd.rht.txstatus+text;version=0.1")
+    public Response beginTransaction(@Context UriInfo info, @Context HttpHeaders headers, @DefaultValue("") String content)
+    {
+        log.trace("coordinator: POST /transaction-manager content: " + content);
+        Transaction tx = new Transaction("coordinator");
+        int timeout = TxSupport.getIntValue(content, TxSupport.TIMEOUT_PROPERTY, 0); // default is 0 - never timeout
+        String uid = tx.get_uid().fileStringForm();
+
+        transactions.put(uid, tx);
+
+        // round up the timeout from milliseconds to seconds
+        if (timeout != 0) {
+            timeout = timeout / 1000;
+            if (timeout == 0)
+                timeout = 1;
+        }
+		
+        int status = tx.begin(timeout);
+
+        try
+        {
+            if (status == ActionStatus.RUNNING) {
+                URI uri1 = TxSupport.getUri(info, info.getPathSegments().size(), uid);
+                Response.ResponseBuilder builder = Response.created(uri1);
+
+                TxSupport.addLinkHeader(builder, info, TxSupport.TERMINATOR_LINK, TxSupport.TERMINATOR_LINK, uid, "terminate");
+                TxSupport.addLinkHeader(builder, info, TxSupport.PARTICIPANT_LINK, TxSupport.PARTICIPANT_LINK, uid);
+
+                return builder.build();
+            }
+
+            throw new TransactionStatusException("Transaction failed to start: " + status);
+        } catch (Exception e) {
+            log.debug("begin", e);
+            throw new TransactionStatusException("Transaction failed to start: " + e);
+        }
+        finally
+        {
+            AtomicAction.suspend();
+        }
+    }
+
+    /**
+     * Obtain the transaction terminator and participant enlistment URIS for the
+     * specified transaction id. These are returned in link headers in the same
+     * way they were returned when the transaction was started @see Coordinator.beginTransaction
+     *
+     * @param info request context
+     * @param id URL template parameter for the transaction id
+     * @return http response
+     */
+    @HEAD
+    @Path(TxSupport.TX_SEGMENT + "/{id}")
+    public Response getTransactionURIs(@Context UriInfo info, @PathParam("id") String id)
+    {
+        log.trace("coordinator txn head request for txn " + id);
+        Transaction tx = getTransaction(id);
+
+        if (tx == null)
+            return Response.status(HttpResponseCodes.SC_GONE).build();
+
+        Response.ResponseBuilder builder = Response.ok();
+
+        TxSupport.addLinkHeader(builder, info, TxSupport.TERMINATOR_LINK, TxSupport.TERMINATOR_LINK, "terminate");
+        TxSupport.addLinkHeader(builder, info, TxSupport.PARTICIPANT_LINK, TxSupport.PARTICIPANT_LINK);
+
+        return builder.build();
+    }
+
+    /**
+     * The client can control the outcome of the transaction by by PUTing to the terminator
+     * URL returned as a response to the original transaction create request.
+     * Upon termination, the resource and all associated resources are implicitly deleted.
+     * For any subsequent invocation then an implementation MAY return 410 if the implementation
+     * records information about transactions that have completed, otherwise it should return 404
+     * (not necessary for presumed rollback semantics) but at a minimum MUST return 401.
+     * The invoker can assume this was a rollback. In order for an interested party to know for
+     * sure the outcome of a transaction then it MUST be registered as a participant with the
+     * transaction coordinator.
+     *
+     * @param txId URL template component containing the transaction identifier
+     * @param fault mechanism for injecting faults TODO use byteman instead
+     * @param content body of the request indicating a commit or abort request
+     *  @see TxSupport.COMMITTED etc
+     * @return http response code
+    */
+    @PUT
+    @Path(TxSupport.TX_SEGMENT + "/{TxId}/terminate")
+    public Response terminateTransaction(@PathParam("TxId")String txId, @QueryParam("fault") @DefaultValue("")String fault, String content)
+    {
+        log.trace("coordinator: commit: transaction-manager/" + txId + "/terminate");
+
+        Transaction tx = getTransaction(txId);
+        Collection<String> enlistmentIds = new ArrayList<String>();
+        String how = TxSupport.getStringValue(content, TxSupport.STATUS_PROPERTY);
+        String status;
+        int aaRes;
+        int scRes;
+        boolean commit;
+
+        tx.getParticipants(enlistmentIds);
+
+        if (TxSupport.COMMITTED.equals(how))
+            commit  = true;
+        else if (TxSupport.ABORTED.equals(how))
+            commit = false;
+        else
+            return Response.status(HttpResponseCodes.SC_BAD_REQUEST).build();
+
+        tx.setFault(fault);
+        AtomicAction.resume(tx);
+        aaRes = commit ? tx.commit(true) : tx.abort();
+        status = tx.getStatus(aaRes);
+        AtomicAction.suspend();
+
+        log.trace("terminate result: " + status);
+
+        if (tx.isRunning())
+            throw new TransactionStatusException("Transaction failed to terminate");
+
+        if (!tx.isAlive()) {
+            transactions.remove(txId);
+
+            for (Iterator<String> i = participants.keySet().iterator(); i.hasNext(); )
+                if (enlistmentIds.contains(i.next()))
+                    i.remove();
+        } else if (tx.isFinishing()) {
+            // TODO who cleans up in this case
+            log.debug("transaction is still terminating: " + aaRes);
+        }
+
+        if (status.length() == 0)
+            scRes = HttpResponseCodes.SC_INTERNAL_SERVER_ERROR;
+        else
+            scRes = HttpResponseCodes.SC_OK;
+
+        return Response.status(scRes).entity(TxSupport.toStatusContent(status)).build();
+    }
+
+    /**
+     * Register a participant in a tx
+     * @param txId id of transaction
+     * @param content body of request containing URI for driving the participant through completion
+     *  (the URI should be unique within the scope of txId)
+     * @return unique resource ref for the participant
+     */
+    @POST
+    @Path(TxSupport.TX_SEGMENT + "/{TxId}")
+    public Response enlistParticipant(@Context UriInfo info, @PathParam("TxId")String txId, String content)
+    {
+        Transaction tx = getTransaction(txId);
+        if (tx == null)
+            return Response.status(HttpResponseCodes.SC_GONE).build();
+
+        LinkHolder links = new LinkHolder(content);
+        String txURI = TxSupport.buildURI(info.getBaseUriBuilder(), info.getPathSegments().get(0).getPath(), info.getPathSegments().get(1).getPath());
+
+
+
+        UriBuilder builder = info.getBaseUriBuilder();
+        builder.path(info.getPathSegments().get(0).getPath());
+        builder.path(TxSupport.RC_SEGMENT);
+        String recoveryUrlBase = builder.build().toString() + '/';       
+        String terminatorUrl = links.get(TxSupport.TERMINATOR_LINK);
+
+        if (tx.isEnlisted(terminatorUrl))
+            return Response.status(HttpURLConnection.HTTP_BAD_REQUEST).build();
+
+        String coordinatorId = tx.enlistParticipant(txURI, links.get(TxSupport.PARTICIPANT_LINK), terminatorUrl, recoveryUrlBase);
+
+        if (coordinatorId == null) // the request was rejected (2PC processing must have started or already registerd)
+            return Response.status(HttpResponseCodes.SC_FORBIDDEN).build();
+
+        links.put("txid", txId);
+        participants.put(coordinatorId, links);
+
+        log.debug("enlisted participant: content=" + content + " in tx " + txId + " Coordinator url base: " + recoveryUrlBase);
+
+        return Response.created(URI.create(tx.getRecoveryUrl())).build();
+    }
+    
+    /**
+     * 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 semantics.
+     * @param enlistmentId the resource reference
+     * @return the participant url
+     */
+    @GET
+    @Path(TxSupport.RC_SEGMENT + "/{RecCoordId}")
+    public Response lookupParticipant(@PathParam("RecCoordId")String enlistmentId)
+    {
+        log.trace("coordinator: lookup: transaction-coordinator/" + enlistmentId);
+
+        LinkHolder p = participants.get(enlistmentId);
+
+        if (p == null)
+            return Response.status(HttpResponseCodes.SC_NOT_FOUND).build();
+
+        String pContent = TxSupport.getParticipantUrls(p.get(TxSupport.TERMINATOR_LINK), p.get(TxSupport.PARTICIPANT_LINK));
+
+        return Response.ok(p).entity(pContent).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 enlistmentId id by the participant is known
+     * @param content http body
+     * @return http status code
+     */
+    @PUT
+    @Path(TxSupport.RC_SEGMENT + "/{RecCoordId}")
+    public Response replaceParticipant(@PathParam("RecCoordId")String enlistmentId, String content)
+    {
+        LinkHolder links = new LinkHolder(content);
+        String terminator = links.get(TxSupport.TERMINATOR_LINK);
+
+        log.trace("coordinator: replace: recovery-coordinator/" + enlistmentId + "?URL=" + terminator);
+        if (terminator == null)
+            return Response.status(HttpResponseCodes.SC_BAD_REQUEST).build();
+
+        participants.put(enlistmentId, links);
+
+        return Response.status(HttpResponseCodes.SC_OK).build();
+    }
+
+    @POST
+    @Path(TxSupport.RC_SEGMENT + "/{RecCoordId}")
+    public Response postParticipant(@PathParam("RecCoordId")String enlistmentId)
+    {
+        log.trace("coordinator: replace via Post: recovery-coordinator/" + enlistmentId);
+        return Response.status(HttpResponseCodes.SC_UNAUTHORIZED).build();
+    }
+
+    @DELETE
+    @Path(TxSupport.RC_SEGMENT + "/{RecCoordId}")
+    public Response deleteParticipant(@PathParam("RecCoordId")String enlistmentId)
+    {
+        log.trace("coordinator: participant leaving via Delete: recovery-coordinator/" + enlistmentId);
+        LinkHolder p = participants.get(enlistmentId);
+        Transaction txn;
+
+        if (p == null || (txn = transactions.get(p.get("txid"))) == null)
+            return Response.status(HttpResponseCodes.SC_NOT_FOUND).build();
+
+        if (txn.forgetParticipant(p.get(TxSupport.TERMINATOR_LINK)))
+            return Response.status(HttpResponseCodes.SC_OK).build();
+        
+        return Response.status(HttpResponseCodes.SC_CONFLICT).build();
+    }
+
+    private Transaction getTransaction(String txId)
+    {
+        Transaction tx = transactions.get(txId);
+
+        if (tx == null)
+            throw new ResourceNotFoundException("Transaction id not found");
+
+        return tx;
+    }
+}
+

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/TMApplication.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/TMApplication.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/service/TMApplication.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,102 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.service;
+
+import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
+import com.arjuna.ats.arjuna.coordinator.RecordType;
+import com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeManager;
+import com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeMap;
+import com.arjuna.ats.arjuna.recovery.RecoveryManager;
+
+import javax.ws.rs.core.Application;
+import java.util.Set;
+import java.util.HashSet;
+
+import org.apache.log4j.Logger;
+import org.jboss.jbossts.star.provider.NotFoundMapper;
+import org.jboss.jbossts.star.resource.RESTRecord;
+import org.jboss.jbossts.star.provider.TMUnavailableMapper;
+import org.jboss.jbossts.star.provider.TransactionStatusMapper;
+import org.jboss.jbossts.star.provider.HttpResponseMapper;
+
+public class TMApplication extends Application
+{
+    private final static Logger log = Logger.getLogger(TMApplication.class);
+
+    HashSet<Object> singletons = new HashSet<Object>();
+    Set<Class<?>> classes = new HashSet<Class<?>> ();
+
+    public TMApplication()
+    {
+        for (Class cl : resourceClasses)
+            classes.add(cl);
+
+        for (Class cl : mappers)
+            classes.add(cl);
+
+//        singletons.addAll(Arrays.asList(resources));
+        try
+        {
+            // TODO move com/arjuna/ats/jbossatx/jt[as]/TransactionManagerService.isRecoveryManagerRunning
+            // to RecoveryManager and change logging
+            // by default do not colocate the coordinator and recovery manager
+            if ("true".equals(System.getProperty("recovery", "false")))
+                RecoveryManager.manager();
+
+           // register RESTRecord record type so that it is persisted in the object store correctly
+           RecordTypeManager.manager().add(new RecordTypeMap() {
+               public Class<? extends AbstractRecord> getRecordClass () { return RESTRecord.class;}
+               public int getType () {return RecordType.USER_DEF_FIRST0;}
+           });
+        }
+        catch (Throwable e)
+        {
+            log.warn("TM JAX-RS application failed to start: " + e.getMessage());
+        }
+    }
+
+    @Override
+    public Set<Class<?>> getClasses()
+    {
+        return classes;
+    }
+
+    @Override
+    public Set<Object> getSingletons()
+    {
+        return singletons;
+    }
+
+    private static Class<?>[] mappers = {
+        TMUnavailableMapper.class,
+        TransactionStatusMapper.class,
+        HttpResponseMapper.class,
+        NotFoundMapper.class
+    };
+    
+    private static Class[] resourceClasses = {
+            Coordinator.class,
+    };
+
+    private static Object[] resources = {
+            new Coordinator(),
+    };
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/LinkHolder.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/LinkHolder.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/LinkHolder.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.util;
+
+import java.util.HashMap;
+
+public class LinkHolder extends HashMap<String, String> {
+    public LinkHolder() {
+        super();
+    }
+
+    public LinkHolder(String content) {
+        this();
+        TxSupport.matchNames(this, content, ";");
+    }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/TxSupport.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/TxSupport.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/main/java/org/jboss/jbossts/star/util/TxSupport.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,460 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.util;
+
+import org.apache.log4j.Logger;
+import org.jboss.jbossts.star.provider.HttpResponseException;
+import org.jboss.resteasy.spi.Link;
+
+import javax.ws.rs.core.PathSegment;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Various utilities for sending HTTP messages
+ */
+public class TxSupport
+{
+    protected static final Logger log = Logger.getLogger(TxSupport.class);
+
+    private static Pattern NVP_PATTERN = Pattern.compile("\\b\\w+\\s*=\\s*.*"); // matches name=value pairs
+    private static String LINK_REGEX = "<(.*?)>.*rel=\"(.*?)\"";
+    private static Pattern LINK_PATTERN = Pattern.compile(LINK_REGEX);
+
+    public static int PORT = 8080;
+    public static String BIND_ADDRESS = System.getProperty("jboss.bind.address", "127.0.0.1");
+    public static String BASE_URL = "http://" + BIND_ADDRESS + ':';
+    public static final String TX_CONTEXT = System.getProperty("rest.tx.context.path", "/rest-tx");
+    /**
+     * The REST path prefix for the transaction and recovery coordinator URLS
+     */
+    public static final String TX_PATH = "/tx/";
+    /**
+     * The REST path for the transaction coordinator. Thus the full path for the coordinator
+     * is <web server context> + TX_PATH + TX_SEGMENT
+     */
+    public static final String TX_SEGMENT = "transaction-manager";
+
+    public static final String RC_SEGMENT = "recovery-coordinator";
+
+    public static final String DEF_TX_URL = BASE_URL + PORT + TX_CONTEXT + TX_PATH + TX_SEGMENT;
+    public static String TXN_MGR_URL = DEF_TX_URL;
+
+
+    public static final String URI_SEPARATOR = ";";
+
+    public static final String ABORT_ONLY = "TransactionRollbackOnly";
+    public static final String ABORTING = "TransactionRollingBack";
+    public static final String ABORTED = "TransactionRolledBack";
+    public static final String COMMITTING = "TransactionCommitting";
+    public static final String COMMITTED = "TransactionCommitted";
+    public static final String H_ROLLBACK = "TransactionHeuristicRollback";
+    public static final String H_COMMIT = "TransactionHeuristicCommit";
+    public static final String H_HAZARD = "TransactionHeuristicHazard";
+    public static final String H_MIXED = "TransactionHeuristicMixed";
+    public static final String PREPARING = "TransactionPreparing";
+    public static final String PREPARED = "TransactionPrepared";
+    public static final String RUNNING = "TransactionActive";
+
+    public static final String READONLY = "TransactionReadOnly";
+
+    public static final String TX_ACTIVE = toStatusContent(RUNNING);
+    public static final String TX_PREPARED = toStatusContent(PREPARED);
+    public static final String TX_COMMITTED = toStatusContent(COMMITTED);
+    public static final String TX_ABORTED = toStatusContent(ABORTED);
+    public static final String TX_H_MIXED = toStatusContent(H_MIXED);
+    public static final String TX_H_ROLLBACK = toStatusContent(H_ROLLBACK);
+
+    public static final String DO_COMMIT = toStatusContent(COMMITTED);
+    public static final String DO_ABORT = toStatusContent(ABORTED);
+
+    public static final String STATUS_MEDIA_TYPE = "application/txstatus";
+    public static final String POST_MEDIA_TYPE = "application/x-www-form-urlencoded";
+    public static final String PLAIN_MEDIA_TYPE = "text/plain";
+
+    public static final String LOCATION_LINK = "location";
+    public static final String TERMINATOR_LINK = "terminator";
+    public static final String PARTICIPANT_LINK = "durableparticipant";
+
+    public static final String TIMEOUT_PROPERTY = "timeout";
+    public static final String STATUS_PROPERTY = "txStatus";
+
+    private Map<String, String> links = new HashMap<String, String>();
+    private int status = -1;
+    private String body = null;
+    private String txnMgr;
+
+    public static void setTxnMgrUrl(String txnMgrUrl) {
+        TXN_MGR_URL = txnMgrUrl;
+    }
+
+    public TxSupport(String txnMgr) {
+        this.txnMgr = txnMgr;
+    }
+
+    public TxSupport() {
+        this(TXN_MGR_URL);
+    }
+
+    public static void addLinkHeader(Response.ResponseBuilder response, UriInfo info, String title, String name, String ... pathComponents)
+    {
+        String basePath = info.getMatchedURIs().get(0);
+        UriBuilder builder = info.getBaseUriBuilder();
+        builder.path(basePath);
+
+        for (String component : pathComponents)
+            builder.path(component);
+
+        String uri = builder.build().toString();
+
+        setLinkHeader(response, title, name, uri, null);
+    }
+
+    public static void setLinkHeader(Response.ResponseBuilder builder, String title, String rel, String href, String type)
+    {
+        Link link = new Link(title, rel, href, type, null);
+        setLinkHeader(builder, link);
+    }
+
+    public static void setLinkHeader(Response.ResponseBuilder builder, Link link)
+    {
+        builder.header("Link", link);
+    }
+
+    public Collection<String> getTransactions() throws HttpResponseException {
+        String content = httpRequest(new int[] {HttpURLConnection.HTTP_OK}, txnMgr, "GET", STATUS_MEDIA_TYPE, null, null);
+        Collection<String> txns = new ArrayList<String> ();
+
+        // the returned document contains transaction URLs delimited by the TXN_LIST_SEP character
+        for (String txn : content.split(URI_SEPARATOR))
+            txns.add(txn.trim());
+
+        return txns;
+    }
+    public int txCount() throws HttpResponseException {
+        String content = httpRequest(new int[] {HttpURLConnection.HTTP_OK}, txnMgr, "GET", STATUS_MEDIA_TYPE, null, null);
+
+        return content.length() == 0 ? 0 : content.split(URI_SEPARATOR).length;
+    }
+
+    // Transaction control methods
+    public TxSupport startTx() throws HttpResponseException {
+        httpRequest(new int[] {HttpURLConnection.HTTP_CREATED}, txnMgr, "POST", POST_MEDIA_TYPE, "", links);
+        return this;
+    }
+    public TxSupport startTx(long milliseconds) throws HttpResponseException {
+        httpRequest(new int[] {HttpURLConnection.HTTP_CREATED}, txnMgr, "POST", POST_MEDIA_TYPE, TIMEOUT_PROPERTY + "=" + milliseconds, links);
+        return this;
+    }
+    public String commitTx() throws HttpResponseException {
+        return httpRequest(new int[] {HttpURLConnection.HTTP_OK}, links.get(TERMINATOR_LINK), "PUT", STATUS_MEDIA_TYPE, DO_COMMIT, null);
+    }
+    public String rollbackTx() throws HttpResponseException {
+        return httpRequest(new int[] {HttpURLConnection.HTTP_OK}, links.get(TERMINATOR_LINK), "PUT", STATUS_MEDIA_TYPE, DO_ABORT, null);
+    }
+    public String txStatus() throws HttpResponseException {
+        return httpRequest(new int[] {HttpURLConnection.HTTP_OK}, links.get(LOCATION_LINK), "GET", null, null, null);
+    }
+
+    public String txUrl() {
+        return links.get(LOCATION_LINK);
+    }
+    public String txTerminatorUrl() {
+        return links.get(TERMINATOR_LINK);
+    }
+    public String enlistUrl() {
+        return links.get(PARTICIPANT_LINK);
+    }
+
+
+    public String getBody() {
+        return body;
+    }
+    public int getStatus() {
+        return status;
+    }
+
+    public void refreshLinkHeaders(Map<String, String> linkHeaders) throws HttpResponseException {
+        httpRequest(new int[] {HttpURLConnection.HTTP_OK}, links.get(LOCATION_LINK), "HEAD", STATUS_MEDIA_TYPE, null, linkHeaders);
+    }
+
+    public String enlist(String pUrl) throws HttpResponseException {
+        return httpRequest(new int[] {HttpURLConnection.HTTP_OK}, pUrl, "POST", POST_MEDIA_TYPE, enlistUrl(), null);
+    }
+
+    public String httpRequest(int[] expect, String url, String method, String mediaType, String content, Map<String, String> linkHeaders) throws HttpResponseException {
+        HttpURLConnection connection = null;
+
+        try {
+            connection = openConnection(null, url, method, mediaType, content);
+            connection.setReadTimeout(5000);
+            status = connection.getResponseCode();
+
+            try {
+                body = (status != -1 ? getContent(connection) : "");
+            } catch (IOException e) {
+                body = "";
+            }
+
+            if (linkHeaders != null) {
+                extractLinkHeaders(connection, linkHeaders);
+                addLocationHeader(connection, linkHeaders);
+            }
+
+            if (expect != null && expect.length != 0) {
+                for (int sc : expect)
+                    if (sc == status)
+                        return body;
+
+                throw new HttpResponseException(null, "", expect, status);
+            } else {
+                return body;
+            }
+        } catch (IOException e) {
+            throw new HttpResponseException(e, "", expect, status);
+        } finally {
+            if (connection != null)
+                connection.disconnect();
+        }
+    }
+
+    private void extractLinkHeaders(HttpURLConnection connection, Map<String, String> links) {
+        Collection<String> linkHeaders = connection.getHeaderFields().get("Link");
+
+        if (linkHeaders == null)
+            linkHeaders = connection.getHeaderFields().get("link");
+
+        if (linkHeaders != null) {
+            for (String link : linkHeaders) {
+                String[] lhs = link.split(","); // links are separated by a comma
+
+                for (String lnk : lhs) {
+                    Matcher m = LINK_PATTERN.matcher(lnk);
+                    if (m.find() && m.groupCount() > 1)
+                        links.put(m.group(2), m.group(1));
+                }
+            }
+        }
+    }
+
+    private void addLocationHeader(HttpURLConnection connection, Map<String, String> links) {
+        try {
+            if (connection.getResponseCode() == HttpURLConnection.HTTP_CREATED)
+                links.put("location", connection.getHeaderField("location"));
+        } catch (IOException e) {
+        }
+    }
+
+    private HttpURLConnection openConnection(HttpURLConnection connection, String url, String method, String contentType,
+                                                   String content) throws IOException {
+        if (connection != null)
+            connection.disconnect();
+
+        connection = (HttpURLConnection) new URL(url).openConnection();
+
+        connection.setRequestMethod(method);
+
+        if (contentType != null)
+            connection.setRequestProperty("Content-Type", contentType);
+
+        if (content != null) {
+            connection.setDoOutput(true);
+
+            OutputStream os = connection.getOutputStream();
+            os.write(content.getBytes());
+            os.flush();
+        }
+
+        return connection;
+    }
+
+    private String getContent(HttpURLConnection connection) throws IOException {
+        return getContent(connection, new StringBuilder()).toString();
+    }
+
+    private StringBuilder getContent(HttpURLConnection connection, StringBuilder builder) throws IOException {
+        char[] buffer = new char[1024];
+        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+        int wasRead;
+
+        do
+        {
+            wasRead = reader.read(buffer, 0, 1024);
+            if (wasRead > 0)
+                builder.append(buffer, 0, wasRead);
+        }
+        while (wasRead > -1);
+
+        return builder;
+    }
+
+    public static String getParticipantUrls(String terminatorUrl, String participantUrl) {
+        return new StringBuilder().append(TERMINATOR_LINK).append('=').append(terminatorUrl).
+                append(URI_SEPARATOR).append(PARTICIPANT_LINK).append('=').append(participantUrl).toString();
+    }
+
+    public static String getStringValue(String content, String name)
+    {
+        Map<String, String> matches = new HashMap<String, String>();
+
+        TxSupport.matchNames(matches, content, null);
+
+        return matches.get(name);
+    }
+
+    public static int getIntValue(String content, String name, int defValue)
+    {
+        String v = getStringValue(content, name);
+
+        if (v != null)
+            try {
+                return Integer.valueOf(v);
+            } catch (NumberFormatException e) {
+                //
+            }
+
+        return defValue;
+    }
+
+    /**
+     * Parse a string for name=value pairs
+     * TODO java.util.Scanner might be more efficient
+     * @param pairs the name value pairs contained in content
+     * @param content a string containing name=value substrings
+     */
+    public static void matchNames(Map<String, String> pairs, String content, String splitChars) {
+        if (content != null) {
+            String[] lines;
+
+            if (splitChars == null) {
+                lines = new String[] {content};
+            } else {
+                lines = content.split(splitChars);
+            }
+
+            for (String line : lines) {
+                Matcher m = NVP_PATTERN.matcher(line);
+
+                while (m.find()) {
+                    String[] tokens = m.group().trim().split("\\s+");
+                    for (String tok : tokens) {
+                        String[] pair = tok.split("=");
+
+                        if (pair.length > 1)
+                            pairs.put(pair[0], pair[1]);
+                    }
+                }
+            }
+        }
+    }
+    
+    public static UriBuilder getUriBuilder(UriInfo info, int npaths, String ... paths)
+    {
+        UriBuilder builder = info.getBaseUriBuilder();
+
+        if (npaths > 0){
+            List<PathSegment> segments = info.getPathSegments();
+
+            for (int i = 0; i < npaths; i++)
+                builder.path(segments.get(i).getPath());
+        } else {
+            String basePath = info.getMatchedURIs().get(0);
+
+            builder.path(basePath);
+        }
+
+        for (String path : paths)
+            builder.path(path);
+
+        return builder;
+    }
+
+    public static URI getUri(UriInfo info, int npaths, String ... paths)
+    {
+        return getUriBuilder(info, npaths, paths).build();
+    }
+
+    public static String buildURI(UriBuilder builder, String ... pathComponents)
+    {
+        for (String component : pathComponents)
+            builder.path(component);
+
+        return builder.build().toString();
+    }
+
+    public static String getStatus(String statusContent) {
+        return getStringValue(statusContent, STATUS_PROPERTY);
+    }
+
+    public static String toContent(String property, String status) {
+        return new StringBuilder(property).append('=').append(status).toString();
+    }
+
+    public static String toStatusContent(String status) {
+        return toContent(STATUS_PROPERTY, status);
+    }
+
+    public static boolean isPrepare(String status) {
+        return PREPARED.equals(status);
+    }
+
+    public static boolean isCommit(String status) {
+        return COMMITTED.equals(status);
+    }
+
+    public static boolean isAbort(String status) {
+        return ABORTED.equals(status);
+    }
+
+    public static boolean isReadOnly(String status) {
+        return READONLY.equals(status);
+    }
+
+    public static boolean isHeuristic(String status) {
+        return H_COMMIT.equals(status) ||
+                H_HAZARD.equals(status) ||
+                H_MIXED.equals(status) ||
+                H_ROLLBACK.equals(status);
+    }
+
+    public static boolean isComplete(String status) {
+        return COMMITTED.equals(status) ||
+                ABORTED.equals(status);
+    }
+
+    public static boolean isActive(String status) {
+        return ABORT_ONLY.equals(status) ||
+                ABORTING.equals(status) ||
+                COMMITTING.equals(status) ||
+                PREPARING.equals(status) ||
+                PREPARED.equals(status) ||
+                RUNNING.equals(status);
+    }
+
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/BaseTest.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/BaseTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/BaseTest.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,434 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.test;
+
+import com.sun.grizzly.http.SelectorThread;
+import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;
+import org.apache.log4j.Logger;
+import org.jboss.jbossts.star.provider.*;
+import org.jboss.jbossts.star.service.Coordinator;
+import org.jboss.jbossts.star.util.LinkHolder;
+import org.jboss.jbossts.star.util.TxSupport;
+import org.jboss.resteasy.plugins.server.tjws.TJWSEmbeddedJaxrsServer;
+import org.jboss.resteasy.spi.Registry;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.junit.*;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BaseTest {
+    protected final static Logger log = Logger.getLogger(BaseTest.class);
+
+    protected static boolean USE_RESTEASY = false;
+
+    protected static final int PORT = 58081;
+    protected static final String SURL = "http://127.0.0.1:" + PORT + '/';
+    protected static final String PSEGMENT = "txresource";
+    protected static final String PURL = SURL + PSEGMENT;
+    protected static String TXN_MGR_URL = SURL + "tx/transaction-manager";
+    private static TJWSEmbeddedJaxrsServer server = null;
+    private static SelectorThread threadSelector = null;
+
+    protected static void setTxnMgrUrl(String txnMgrUrl) {
+        TXN_MGR_URL = txnMgrUrl;
+    }
+    
+    protected static void startRestEasy(Class<?> ... classes) throws Exception
+    {
+        server = new TJWSEmbeddedJaxrsServer();
+        server.setPort(PORT);
+        server.start();
+        Registry registry = server.getDeployment().getRegistry();
+        ResteasyProviderFactory factory = server.getDeployment().getDispatcher().getProviderFactory();
+
+        if (classes != null)
+            for (Class<?> clazz : classes)
+                registry.addPerRequestResource(clazz);
+
+        factory.addExceptionMapper(TMUnavailableMapper.class);
+        factory.addExceptionMapper(TransactionStatusMapper.class);
+        factory.addExceptionMapper(HttpResponseMapper.class);
+        factory.addExceptionMapper(NotFoundMapper.class);
+    }
+
+    protected static void startJersey(String packages) throws Exception {
+        final URI baseUri= UriBuilder.fromUri(SURL).build();
+        final Map<String, String> initParams = new HashMap<String, String>();
+
+        initParams.put("com.sun.jersey.config.property.packages", packages);
+
+        try {
+            threadSelector = GrizzlyWebContainerFactory.create(baseUri, initParams);
+        } catch (IOException e) {
+            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+        }
+    }
+
+    public static void startContainer(String txnMgrUrl, String packages, Class<?> ... classes) throws Exception {
+        TxSupport.setTxnMgrUrl(txnMgrUrl);
+        
+        if (USE_RESTEASY)
+            startRestEasy(classes);
+        else
+            startJersey(packages);
+    }
+
+    public static void startContainer(String txnMgrUrl) throws Exception {
+        startContainer(txnMgrUrl,
+                "org.jboss.jbossts.star.service;org.jboss.jbossts.star.provider;org.jboss.jbossts.star.test",
+                TransactionalResource.class, Coordinator.class);
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception {
+        if (server != null) {
+            server.stop();
+            server = null;
+        }
+
+        if (threadSelector != null) {
+            threadSelector.stopEndpoint();
+            threadSelector = null;
+        }
+    }
+
+    @Before
+    public void before() throws Exception {
+        TransactionalResource.faults.clear();
+    }
+
+    @Test
+    public void nullTest() throws Exception
+    {
+        // need at least one test
+    }
+
+    protected String enlistResource(TxSupport txn, String pUrl)
+    {
+        return txn.enlist(pUrl);
+    }
+
+    private StringBuilder getResourceUpdateUrl(String pUrl, String pid, String name, String value)
+    {
+        StringBuilder sb = new StringBuilder(pUrl);
+
+        if (pid != null)
+            sb.append("?pId=").append(pid).append("&name=");
+        else
+            sb.append("?name=");
+
+        sb.append(name);
+
+        if (value != null)
+            sb.append("&value=").append(value);
+
+        return sb;
+    }
+
+    /**
+     * Modify a transactional participant
+     * @param txn the transaction
+     * @param pUrl the  transactional participant
+     * @param pid an id
+     * @param name name of a property to update
+     * @param value the new value of the property
+     * @return the response body
+     */
+    protected String modifyResource(TxSupport txn, String pUrl, String pid, String name, String value)
+    {
+        // tell the resource to modify some data and pass the transaction enlistment url along with the request
+        return txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK},
+                getResourceUpdateUrl(pUrl, pid, name, value).toString(), "GET", TxSupport.POST_MEDIA_TYPE, null, null);
+    }
+
+    protected String getResourceProperty(TxSupport txn, String pUrl, String pid, String name)
+    {
+        return txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_NO_CONTENT},
+                getResourceUpdateUrl(pUrl, pid, name, null).toString(), "GET", TxSupport.POST_MEDIA_TYPE, null, null);
+    }
+
+    private static class Work
+    {
+        String id;
+        String tid;
+        String uri;
+        String pUrls;
+        String enlistUrl;
+        String recoveryUrl;
+        String fault;
+        Map<String, String> oldState;
+        Map<String, String> newState;
+        String status;
+
+        Work(String id, String tid, String uri, String pUrls, String enlistUrl, String recoveryUrl, String fault) {
+            this.id = id;
+            this.tid = tid;
+            this.uri = uri;
+            this.pUrls = pUrls;
+            this.enlistUrl = enlistUrl;
+            this.recoveryUrl = recoveryUrl;
+            this.fault = fault;
+            this.oldState = new HashMap<String, String> ();
+            this.newState = new HashMap<String, String> ();
+        }
+
+        public void start() {
+            newState.clear();
+            newState.putAll(oldState);
+        }
+
+        public void end(boolean commit) {
+            if (commit) {
+                oldState.clear();
+                oldState.putAll(newState);
+            }
+        }
+
+        public boolean inTxn() {
+            return TxSupport.isActive(status);
+        }
+    }
+
+    @Path(PSEGMENT)
+    public static class TransactionalResource
+    {
+        private static int pid = 0;
+        static Map<String, Work> faults = new HashMap<String, Work> ();
+
+        public Work makeWork(String id, String txId, String enlistUrl, String recoveryUrl, String fault) {
+            String pURI = PURL + '/' + id;
+            String terminator = new StringBuilder().append(pURI).append('/').append(txId).append("/terminate").toString();
+            String participant = new StringBuilder().append(pURI).append('/').append(txId).append("/terminator").toString();
+            String pUrls = TxSupport.getParticipantUrls(terminator, participant);
+
+            return new Work(id, txId, pURI, pUrls, enlistUrl, recoveryUrl, fault);
+        }
+
+        private String moveParticipant(Work work, String nid, String register)
+        {
+            faults.remove(work.id);
+            work = makeWork(nid, work.tid, work.enlistUrl, work.recoveryUrl, work.fault);
+            faults.put(nid, work);
+            // now tell the transaction manager about the new location
+            if ("true".equals(register))
+                new TxSupport().httpRequest(new int[] {HttpURLConnection.HTTP_OK},
+                        work.recoveryUrl, "PUT", TxSupport.POST_MEDIA_TYPE, work.pUrls, null);
+
+            return nid;
+        }
+
+        @GET
+        public String getBasic(@QueryParam("pId") @DefaultValue("")String pId,
+                               @QueryParam("context") @DefaultValue("")String ctx,
+                               @QueryParam("name") @DefaultValue("")String name,
+                               @QueryParam("value") @DefaultValue("")String value,
+                               @QueryParam("query") @DefaultValue("pUrl") String query,
+                               @QueryParam("arg") @DefaultValue("") String arg,
+                               @QueryParam("register") @DefaultValue("true") String register)
+        {
+            Work work = faults.get(pId);
+            String res = null;
+
+            if (name.length() != 0) {
+                if (value.length() != 0) {
+                    if (work == null){
+                        work = makeWork(Integer.toString(++pid), null, null, null, null);
+                        work.oldState.put(name, value);
+                        faults.put(work.id, work);
+                        return work.id;
+                    }
+
+                    work.newState.put(name, value);
+                }
+
+                if (work != null)
+                    if (work.inTxn())
+                        res = work.newState.get(name);
+                    else
+                        res = work.oldState.get(name);
+            }
+
+
+            if (work == null)
+                //return Response.status(HttpURLConnection.HTTP_NOT_FOUND).build();
+                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
+
+            if ("move".equals(query))
+                res = moveParticipant(work, arg, register);
+            else if ("recoveryUrl".equals(query))
+                res = work.recoveryUrl;
+            else if ("status".equals(query))
+                res = work.status;
+            else if (res == null && work != null)
+                res = work.pUrls;
+
+            return res; // null will generate a 204 status code (no content)
+        }
+
+        @POST
+        @Produces(TxSupport.PLAIN_MEDIA_TYPE)
+        public String enlist(@QueryParam("pId") @DefaultValue("")String pId, @QueryParam("fault") @DefaultValue("")String fault, String enlistUrl) throws IOException {
+            Map<String, String> links = new HashMap<String, String>();
+            Work work = faults.get(pId);
+
+            if (work == null)
+                work = makeWork(Integer.toString(++pid), enlistUrl.substring(enlistUrl.lastIndexOf('/') + 1), enlistUrl, null, fault);
+
+            // enlist in the transaction as a participant
+            try {
+                new TxSupport().httpRequest(new int[] {HttpURLConnection.HTTP_CREATED}, enlistUrl, "POST", TxSupport.POST_MEDIA_TYPE, work.pUrls, links);
+                work.recoveryUrl = links.get("location");
+            } catch (HttpResponseException e) {
+                throw new WebApplicationException(e.getActualResponse());
+            }
+
+            work.status = TxSupport.RUNNING;
+            work.start();
+
+            faults.put(work.id, work);
+
+            return work.id;
+        }
+
+        @PUT
+        @Path("{pId}/{tId}/terminate")
+        public Response terminate(@PathParam("pId") @DefaultValue("")String pId, @PathParam("tId") @DefaultValue("")String tId, String content) {
+            String status = TxSupport.getStatus(content);
+            Work work = faults.get(pId);
+
+            if (work == null)
+                return Response.status(HttpURLConnection.HTTP_NOT_FOUND).build();
+
+            String fault = work.fault;
+
+            if (TxSupport.isPrepare(status)) {
+                if ("READONLY".equals(fault)) {
+//                    faults.remove(pId);
+                    work.status = TxSupport.READONLY;
+                } else if ("PREPARE_FAIL".equals(fault)) {
+//                    faults.remove(pId);
+                    return Response.status(HttpURLConnection.HTTP_CONFLICT).build();
+                    //throw new WebApplicationException(HttpURLConnection.HTTP_CONFLICT);
+                } else {
+                    if ("PDELAY".equals(fault)) {
+                        try {
+                            Thread.sleep(2000);
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                    work.status = TxSupport.PREPARED;
+                }
+            } else if (TxSupport.isCommit(status)) {
+                if ("H_HAZARD".equals(fault))
+                    work.status = TxSupport.H_HAZARD;
+                else if ("H_ROLLBACK".equals(fault))
+                    work.status = TxSupport.H_ROLLBACK;
+                else if ("H_MIXED".equals(fault))
+                    work.status = TxSupport.H_MIXED;
+                else {
+                    if ("CDELAY".equals(fault)) {
+                        try {
+                            Thread.sleep(2000);
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                    work.status = TxSupport.COMMITTED;
+                    work.end(true);
+                }
+            } else if (TxSupport.isAbort(status)) {
+                if ("H_HAZARD".equals(fault))
+                    work.status = TxSupport.H_HAZARD;
+                else if ("H_COMMIT".equals(fault))
+                    work.status = TxSupport.H_COMMIT;
+                else if ("H_MIXED".equals(fault))
+                    work.status = TxSupport.H_MIXED;
+                else {
+                    if ("ADELAY".equals(fault)) {
+                        try {
+                            Thread.sleep(2000);
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                    work.status = TxSupport.ABORTED;
+                    work.end(false);
+//                    faults.remove(pId);
+                }
+            } else {
+                return Response.status(HttpURLConnection.HTTP_BAD_REQUEST).build();
+                //throw new WebApplicationException(HttpURLConnection.HTTP_BAD_REQUEST);
+            }
+
+            //return TxSupport.toStatusContent(work.status);
+            return Response.ok(TxSupport.toStatusContent(work.status)).build();
+        }
+
+        @HEAD
+        @Path("{pId}/{tId}/terminator")
+        public Response getTerminator(@Context UriInfo info, @PathParam("pId") @DefaultValue("")String pId, @PathParam("tId") @DefaultValue("")String tId) {
+            Work work = faults.get(pId);
+
+            if (work == null)
+                return Response.status(HttpURLConnection.HTTP_BAD_REQUEST).build();
+
+            Response.ResponseBuilder builder = Response.ok();
+
+            LinkHolder pUrls = new LinkHolder(work.pUrls);
+            String pTerminator = pUrls.get(TxSupport.TERMINATOR_LINK);
+
+            TxSupport.setLinkHeader(builder, TxSupport.TERMINATOR_LINK, TxSupport.TERMINATOR_LINK, pTerminator, null);
+
+            return builder.build();
+        }
+
+        @GET
+        @Path("{pId}/{tId}/terminator")
+        public String getStatus(@PathParam("pId") @DefaultValue("")String pId, @PathParam("tId") @DefaultValue("")String tId) {
+            Work work = faults.get(pId);
+
+            if (work == null)
+                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
+
+            return TxSupport.toStatusContent(work.status);
+
+        }
+
+        @DELETE
+        @Path("{pId}/{tId}/terminator")
+        public void forgetWork(@PathParam("pId") String pId, @PathParam("tId") String tId) {
+            Work work = faults.get(pId);
+
+            if (work == null)
+                throw new WebApplicationException(HttpURLConnection.HTTP_NOT_FOUND);
+
+            faults.remove(pId);
+
+        }
+    }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/CoordinatorTest.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/CoordinatorTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/CoordinatorTest.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,167 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.test;
+
+import org.jboss.jbossts.star.util.TxSupport;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+
+public class CoordinatorTest extends BaseTest {
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        startContainer(TXN_MGR_URL);
+    }
+
+    /*
+      * Note: TxSupport methods throw exceptions if unexpected status codes are returned.
+      * use the TxSupport.httpRequest(...) if you want to specify other status codes
+      */
+
+    // list transactions
+    @Test
+    public void testListTransactions() throws IOException
+    {
+        TxSupport[] txns = {new TxSupport(), new TxSupport()};
+        int txnCount = new TxSupport().txCount();
+
+        for (TxSupport txn : txns)
+            txn.startTx();
+
+        // there should be txns.length more transactions
+        Assert.assertEquals(txnCount + txns.length, txns[0].txCount());
+
+        for (TxSupport txn : txns)
+            txn.commitTx();
+
+        // the number of transactions should be back to the original number
+        Assert.assertEquals(txnCount, txns[0].txCount());
+
+    }
+
+    // 1PC commit abort
+    @Test
+    public void test1PCAbort() throws Exception
+    {
+        TxSupport txn = new TxSupport();
+        String pUrl = PURL;
+        String pid = null;
+        String pVal;
+
+        pid = modifyResource(txn, pUrl, pid, "p1", "v1");
+        pVal = getResourceProperty(txn, pUrl, pid, "p1");
+        Assert.assertEquals(pVal, "v1");
+
+        txn.startTx();
+        pid = enlistResource(txn, pUrl + "?pId=" + pid);
+
+        modifyResource(txn, pUrl, pid, "p1", "v2");
+        pVal = getResourceProperty(txn, pUrl, pid, "p1");
+        Assert.assertEquals(pVal, "v2");
+
+        txn.rollbackTx();
+
+        pVal = getResourceProperty(txn, pUrl, pid, "p1");
+        Assert.assertEquals(pVal, "v1");
+    }
+
+    // 1PC commit
+    @Test
+    public void test1PCCommit() throws Exception
+    {
+        TxSupport txn = new TxSupport();
+        String pUrl = PURL;
+        String pid = null;
+        String pVal;
+
+        pid = modifyResource(txn, pUrl, pid, "p1", "v1");
+        pVal = getResourceProperty(txn, pUrl, pid, "p1");
+        Assert.assertEquals(pVal, "v1");
+
+        txn.startTx();
+        pid = enlistResource(txn, pUrl + "?pId=" + pid);
+
+        modifyResource(txn, pUrl, pid, "p1", "v2");
+        pVal = getResourceProperty(txn, pUrl, pid, "p1");
+        Assert.assertEquals(pVal, "v2");
+
+        txn.commitTx();
+
+        pVal = getResourceProperty(txn, pUrl, pid, "p1");
+        Assert.assertEquals(pVal, "v2");
+    }
+
+    // 2PC commit
+    @Test
+    public void test2PC() throws Exception
+    {
+        TxSupport txn = new TxSupport();
+        String pUrl = PURL;
+        String[] pid = new String[2];
+        String[] pVal = new String[2];
+
+        for (int i = 0; i < pid.length; i++) {
+            pid[i] = modifyResource(txn, pUrl, null, "p1", "v1");
+            pVal[i] = getResourceProperty(txn, pUrl, pid[i], "p1");
+
+            Assert.assertEquals(pVal[i], "v1");
+        }
+
+        txn.startTx();
+
+        for (int i = 0; i < pid.length; i++) {
+            enlistResource(txn, pUrl + "?pId=" + pid[i]);
+
+            modifyResource(txn, pUrl, pid[i], "p1", "v2");
+            pVal[i] = getResourceProperty(txn, pUrl, pid[i], "p1");
+
+            Assert.assertEquals(pVal[i], "v2");
+        }
+
+        txn.rollbackTx();
+
+        for (int i = 0; i < pid.length; i++) {
+            pVal[i] = getResourceProperty(txn, pUrl, pid[i], "p1");
+            Assert.assertEquals(pVal[i], "v1");
+        }
+    }
+
+    // commit an invalid transaction
+    @Test
+    public void testCommitInvalidTx() throws IOException
+    {
+        TxSupport txn = new TxSupport();
+
+        txn.startTx();
+
+        String terminator = txn.txTerminatorUrl();
+        // we know that the terminator URL ends in terminate (by reading the code for the provider) - modify to produce an invalid one
+        terminator = terminator.replace("/terminate", "_dead/terminate");
+        // an attempt to commit on this URL should fail:
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_NOT_FOUND}, terminator, "PUT", TxSupport.STATUS_MEDIA_TYPE, TxSupport.DO_COMMIT, null);
+        // commit it properly
+        txn.commitTx();
+    }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/SpecTest.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/SpecTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/test/java/org/jboss/jbossts/star/test/SpecTest.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,512 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.test;
+
+import junit.framework.Assert;
+import org.jboss.jbossts.star.provider.*;
+import org.jboss.jbossts.star.util.LinkHolder;
+import org.jboss.jbossts.star.util.TxSupport;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.net.HttpURLConnection;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/*
+ * The comments that are preceded by line numbers refer to text in version 4
+ * of the specification
+ */
+public class SpecTest extends BaseTest {
+    // jax-rs does not support TRACE, CONNECT and PATCH
+    private static enum HTTP_METHOD {HEAD, GET, POST, PUT, DELETE, OPTIONS}
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        startContainer(TXN_MGR_URL);
+    }
+
+    @Test
+    public void testTransactionUrls() throws Exception {
+        TxSupport txn = new TxSupport();
+        Map<String, String> links = new HashMap<String, String>();
+        int txnCount = txn.txCount();
+
+        /*
+        156 Performing a POST on /transaction-manager with content as shown below will start a new
+        157 transaction with a default timeout. A successful invocation will return 201 and the Location header
+        158 MUST contain the URI of the newly created transaction resource, which we will refer to as
+        159 transaction-coordinator in the rest of this specification. Two related URLs MUST also be returned,
+        160 one for the terminator of the transaction to use (typically referred to as the client) and one used
+        161 for registering durable participation in the transaction (typically referred to as the server).
+        162 Although uniform URL structures are used in the examples, these linked URLs can be of arbitrary
+        163 format.
+        */
+        txn.startTx();
+
+        Assert.assertNotNull("Missing location header", txn.txUrl());
+        Assert.assertNotNull("Missing durable participant header", txn.enlistUrl());
+        Assert.assertNotNull("Missing terminator header", txn.txTerminatorUrl());
+
+        /*
+        179 Performing a HEAD on location URL MUST return the same link information.
+        */
+        txn.refreshLinkHeaders(links);
+
+        Assert.assertTrue("Missing terminator link header", links.containsKey(TxSupport.TERMINATOR_LINK));
+        Assert.assertTrue("Missing durable-participant link header", links.containsKey(TxSupport.PARTICIPANT_LINK));
+
+        Assert.assertEquals(links.get(TxSupport.PARTICIPANT_LINK), txn.enlistUrl());
+        Assert.assertEquals(links.get(TxSupport.TERMINATOR_LINK), txn.txTerminatorUrl());
+
+        /*
+        206 Performing a GET on the transaction-manager returns a list of all transaction URIs
+        207 known to the coordinator (active and in recovery).
+        */
+        int tcnt1 = txn.txCount();
+        int tcnt2 = txn.getTransactions().size();
+        Assert.assertEquals(tcnt1, tcnt2);
+        Assert.assertEquals(tcnt1, txnCount + 1);
+
+        /*
+        209 Performing a GET on /transaction-coordinator/1234 returns the current status of the transaction,
+        210 as described later.
+        */
+        Assert.assertEquals(TxSupport.TX_ACTIVE, txn.txStatus());
+
+        /*
+        223 Performing a DELETE on any of the /transaction-coordinator URIs will return a 403.
+        */
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_FORBIDDEN}, txn.txUrl(), "DELETE", null, null, null);
+
+        /*
+        225 The client can PUT on the terminator URL in order to
+        226 control the outcome of the transaction; anything else MUST return a 400.
+        */
+        for (HTTP_METHOD method : HTTP_METHOD.values()) {
+            if (method != HTTP_METHOD.PUT) {
+                txn.httpRequest(new int[] {HttpURLConnection.HTTP_BAD_REQUEST}, txn.txTerminatorUrl(), method.name(), null, null, null);
+            }
+        }
+
+        /*
+        226 ... Performing a PUT as
+        227 shown below will trigger the commit of the transaction. Upon termination, the resource and all
+        228 associated resources are implicitly deleted. For any subsequent invocation then an
+        229 implementation MAY return 410 if the implementation records information about transactions that
+        230 have rolled back, (not necessary for presumed rollback semantics) but at a minimum MUST
+        231 return 401. The invoker can assume this was a rollback. In order for an interested party to know
+        232 for sure the outcome of a transaction then it MUST be registered as a participant with the
+        233 transaction coordinator.
+        */
+        Assert.assertEquals(TxSupport.TX_COMMITTED, txn.commitTx());
+
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_NOT_FOUND}, txn.txUrl(), "GET", null, null, null);
+
+        log.info("Spec test passed");
+    }
+
+    @Test
+    public void testTransactionTimeout() throws Exception {
+        TxSupport txn = new TxSupport();
+
+        /*
+        190 Performing a POST on transaction uri with content "timeout=1000" will start a new transaction with a
+        120000 millisecond timeout
+        */
+        txn.startTx(1000);
+
+        // sleep for longer than the transaction timeout period
+        Thread.sleep(2000);
+
+        /*
+        200 If the transaction is terminated because of a timeout, the resources representing the created
+        201 transaction are deleted. All further invocations on the transaction-coordinator or any of its related
+        202 URIs MAY return 410 if the implementation records information about transactions that have
+        203 rolled back, (not necessary for presumed rollback semantics) but at a minimum MUST return 401.
+        204 The invoker can assume this was a rollback.
+        */
+        /*
+        242 If the transaction no longer exists then an implementation MAY return 410 if the implementation
+        243 records information about transactions that have rolled back, (not necessary for presumed
+        244 rollback semantics) but at a minimum MUST return 401.
+        */
+
+        try {
+            Assert.assertEquals(txn.commitTx(), TxSupport.TX_ABORTED);
+        } catch (HttpResponseException e) {
+            Assert.assertTrue(e.getActualResponse() == HttpURLConnection.HTTP_GONE);
+        }
+    }
+
+    @Test
+    public void testRollback() throws Exception {
+        TxSupport txn = new TxSupport();
+
+        txn.startTx();
+
+        /*
+        253 The transaction may be told to rollback with the following PUT request:
+        */
+        Assert.assertEquals(TxSupport.TX_ABORTED, txn.rollbackTx());
+
+        /*
+        228 associated resources are implicitly deleted. For any subsequent invocation then an
+        229 implementation MAY return 410 if the implementation records information about transactions that
+        230 have rolled back, (not necessary for presumed rollback semantics) but at a minimum MUST
+        231 return 404. The invoker can assume this was a rollback. In order for an interested party to know
+        232 for sure the outcome of a transaction then it MUST be registered as a participant with the
+        233 transaction coordinator.
+        */
+        try {
+            txn.rollbackTx();
+        } catch (HttpResponseException e) {
+            Assert.assertTrue(e.getActualResponse() == HttpURLConnection.HTTP_GONE ||
+                e.getActualResponse() == HttpURLConnection.HTTP_NOT_FOUND);
+        }
+    }
+
+    @Test
+    public void testEnlistResource() throws Exception {
+        TxSupport txn = new TxSupport();
+
+        txn.startTx();
+
+        // enlist two Transactional Participants with the transaction
+        for (int i = 0; i < 2; i++)
+            txn.enlist(PURL);
+
+        /*
+        225 The client can PUT one of the following to /transaction-coordinator/1234/terminator in order to
+        226 control the outcome of the transaction; anything else MUST return a 400. Performing a PUT as
+            ...
+        */
+        try {
+            txn.httpRequest(new int[] {HttpURLConnection.HTTP_BAD_REQUEST},
+                    txn.txTerminatorUrl(), "POST", TxSupport.STATUS_MEDIA_TYPE, TxSupport.DO_COMMIT, null);
+        } catch (HttpResponseException e) {
+            Assert.fail("Should have thrown 400: " + e);
+        }
+        /*
+        246 The state of the transaction MUST be Active for this operation to succeed. If the transaction is in
+        247 an invalid state for the operation then the implementation MUST 403. Otherwise the
+        248 implementation MAY return 200 or 202. In the latter case the Location header SHOULD contain a
+        249 URI upon which a GET may be performed to obtain the transaction outcome. It is implementation
+        250 dependent as to how long this URI will remain valid. Once removed by an implementation then
+        251 410 MUST be returned.
+        */
+        try {
+            txn.httpRequest(new int[] {HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND},
+                    txn.txTerminatorUrl() + "/garbage", "PUT", TxSupport.STATUS_MEDIA_TYPE, TxSupport.DO_COMMIT, null);
+        } catch (HttpResponseException e) {
+            Assert.fail("Should have thrown 400 or 404");
+        }
+        // and finally commit it correctly
+        txn.commitTx();
+    }
+
+    @Test
+    public void testHeuristic() throws Exception {
+        TxSupport txn = new TxSupport();
+        /*
+        cause a heuristic so that we can test:
+        246 The state of the transaction MUST be Active for this operation to succeed. If the transaction is in
+        247 an invalid state for the operation then the implementation MUST 403. Otherwise the
+         */
+        String[] pUrl = {
+                PURL,
+                PURL + "?fault=H_ROLLBACK",
+                PURL,
+        };
+        String[] work = new String[pUrl.length];
+
+        // start a transaction
+        txn.startTx();
+
+        // enlist participants (one of which will rollback when asked to commit)
+        for (int i = 0; i < pUrl.length; i++)
+            work[i] = txn.enlist(pUrl[i]);
+
+        // the commit request should produce a heuristic
+        String content = txn.commitTx();
+        Assert.assertEquals(TxSupport.TX_H_MIXED, content);
+
+        // ask the dummy TransactionalResource for the terminator and participant urls:
+        content = txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK},
+                PURL + "?pId=" + work[1], "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+        LinkHolder pUrls = new LinkHolder(content);
+        String pTerminator = pUrls.get(TxSupport.TERMINATOR_LINK);
+        String pParticipant = pUrls.get(TxSupport.PARTICIPANT_LINK);
+        Map<String, String> links2 = new HashMap<String, String>();
+
+        /*
+        287 Performing a HEAD on a registered participant URI MUST return the terminator reference, as
+        */
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK}, pParticipant, "HEAD", TxSupport.PLAIN_MEDIA_TYPE, null, links2);
+        Assert.assertEquals(links2.get(TxSupport.TERMINATOR_LINK), pTerminator);
+
+        // manually tell the TransactionalResource to forget the heuristic
+        // (see the test testHeuristicWithForget for how to get the Transaction Manager to
+        // tell the participant to forget it)
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_NO_CONTENT},
+                pParticipant, "DELETE", null, null, null);
+
+        // the terminator should have gone
+        pTerminator = txn.httpRequest(new int[] {HttpURLConnection.HTTP_NOT_FOUND},
+                PURL + "?pId=" + work[1], "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+
+        Assert.assertEquals(pTerminator, "");
+    }
+
+    @Test
+    public void testHeuristicWithForget() throws Exception {
+        TxSupport txn = new TxSupport();
+        String[] pUrl = {
+                PURL + "?fault=H_ROLLBACK",
+                PURL,
+        };
+        String[] work = new String[pUrl.length];
+
+        // start a transaction
+        txn.startTx();
+
+        // enlist participants (one of which will rollback when asked to commit)
+        for (int i = 0; i < pUrl.length; i++)
+            work[i] = txn.enlist(pUrl[i]);
+
+        /*
+         * the commit request should produce a heuristic but since the first participant
+         * generates a heuristic rollback the transaction manager (TM) can decide to rollback
+         * the second participant. Hence both participants will be consistent and therefore
+         * the TM will tell the first participant that it is OK to forget the heuristic
+         */
+
+        String content = txn.commitTx();
+        Assert.assertEquals(TxSupport.TX_H_ROLLBACK, content);
+
+        // the participant terminator should have gone even though it got a heuristic
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_NOT_FOUND},
+                PURL + "?pId=" + work[0], "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+    }
+
+    @Test
+    public void testSpec6() throws Exception {
+        TxSupport txn = new TxSupport();
+
+        String[] pUrl = {
+                PURL,
+                PURL + "?fault=CDELAY",
+                PURL,
+        };
+
+        // start another transaction
+        txn.startTx();
+
+        // enlist transactional participants (one of which will delay during commit)
+        for (String url : pUrl)
+            txn.enlist(url);
+
+        AsynchronousCommit async = new AsynchronousCommit(txn);
+
+        new Thread(async).start();
+
+        Thread.sleep(4000);
+
+        Assert.assertEquals(async.status, TxSupport.TX_COMMITTED);
+
+    }
+
+    @Test
+    public void testParticipantStatus() throws Exception {
+        TxSupport txn = new TxSupport();
+        String[] pUrls = {
+                PURL + "?fault=CDELAY",
+                PURL,
+        };
+        String[] work = new String[pUrls.length];
+        AsynchronousCommit[] async = new AsynchronousCommit[pUrls.length];
+
+        // start a transaction
+        txn.startTx();
+
+        for (int i = 0; i < pUrls.length; i++) {
+            work[i] = txn.enlist(pUrls[i]);
+            async[i] = new AsynchronousCommit(txn);
+        }
+
+        for (int i = 0; i < async.length; i++)
+            new Thread(async[i]).start();
+
+        /*
+        347 Performing a GET on the /participant-resource URL MUST return the current status of the
+        348 participant
+        */
+
+        // ask the TransactionalResource for the participant url:
+        String content = txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK},
+                PURL + "?pId=" + work[0], "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+        LinkHolder pLinks = new LinkHolder(content);
+        String pParticipant = pLinks.get(TxSupport.PARTICIPANT_LINK);
+
+        // wait long enough for the prepare
+        Thread.sleep(1000);
+
+        /*
+        347 Performing a GET on the /participant-resource URL MUST return the current status of the
+        348 participant
+        */
+
+        // the commit on the first participant is delayed so asking for its status should return prepared:
+        content = txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK}, pParticipant, "GET", TxSupport.STATUS_MEDIA_TYPE, null, null);
+        Assert.assertEquals(content, TxSupport.TX_PREPARED);
+    }
+
+    @Test
+    public void testCannotEnlistDuring2PC() throws Exception {
+        TxSupport txn = new TxSupport();
+
+        /*
+        297 If the transaction is not Active then the implementation MUST return 403. If the implementation
+        298 has seen this participant URI before then it MUST return 400. Otherwise the operation is
+        */
+        String[] pUrls = {
+                PURL + "?fault=PDELAY",
+                PURL,
+        };
+        String[] work = new String[pUrls.length];
+
+        // start a transaction
+        txn.startTx();
+
+        // enlist participants
+        for (int i = 0; i < pUrls.length; i++)
+            work[i] = txn.enlist(pUrls[i]);
+
+        AsynchronousCommit async = new AsynchronousCommit(txn);
+
+        // ask the TransactionalResource for the participant urls:
+        String enlistUrls = txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK},
+                PURL + "?pId=" + work[0], "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+        // and test that enlisting a participant a second time generates a 400 code:
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_BAD_REQUEST},
+                txn.enlistUrl(), "POST", TxSupport.POST_MEDIA_TYPE, enlistUrls, null);
+
+        // commit the transaction
+        new Thread(async).start();
+
+        // allow time for the prepare to be called
+        Thread.sleep(1000);
+        // the transaction should now be prepared so it should be too late to enlist in the transaction:
+        try {
+            String er = txn.enlist(PURL);
+            Assert.fail("Should not be able to enlist a resource after 2PC has started");
+        } catch (HttpResponseException e) {
+            Assert.assertEquals(e.getActualResponse(), HttpURLConnection.HTTP_FORBIDDEN);
+        }
+    }
+
+    // recovery
+    public void recovery(boolean notifyRecovery) throws Exception {
+        TxSupport txn = new TxSupport();
+        String pUrl = PURL;
+
+        // start a transaction
+        txn.startTx();
+
+        // enlist two participants
+        String pId = pUrl + "?pId=" + txn.enlist(pUrl);
+        String pId2 = pUrl + "?pId=" + txn.enlist(pUrl);
+
+        // ask the TransactionalResource to move the participant to a new url:
+        String notifyQuery = notifyRecovery ? "true" : "false";
+        String newPid = txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK},
+                pId + "&query=move&arg=101&register=" + notifyQuery, "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+
+        newPid = pUrl + "?pId=" + newPid;
+        // make sure the TransactionalResource has forgotten about the original pId
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_NOT_FOUND}, pId, "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+        // and is listening on the new one
+        txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK}, newPid, "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+
+        // commit the transaction
+        String txStatus = txn.commitTx();
+
+        if (notifyRecovery) {
+            // the participant that moved should have had commit called
+            String status = txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK},
+                    pId2 + "&query=status", "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+
+            Assert.assertEquals(TxSupport.COMMITTED, status);
+        } else {
+            // the participant that did not move should have aborted
+            String status = txn.httpRequest(new int[] {HttpURLConnection.HTTP_OK},
+                    pId2 + "&query=status", "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+
+            // the participant that moved should not have had prepare or commit called on it so expect 204
+            txn.httpRequest(new int[] {HttpURLConnection.HTTP_NO_CONTENT},
+                    newPid + "&query=status", "GET", TxSupport.PLAIN_MEDIA_TYPE, null, null);
+
+            Assert.assertEquals(TxSupport.ABORTED, status);
+        }
+    }
+
+    @Test // recovery
+    public void testRecoveryURL() throws Exception {
+       recovery(true);
+       recovery(false);
+    }
+
+    static class AsynchronousCommit implements Runnable {
+        String status;
+        TxSupport txn;
+        public AsynchronousCommit(TxSupport txn) { this.txn = txn; }
+        public void run() {
+            try {
+                status = txn.commitTx();
+            } catch (HttpResponseException e) {
+                status = "";
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        TxSupport[] transactions = {new TxSupport(), new TxSupport()};
+        int txnCnt = new TxSupport().txCount();
+
+        // start 2 transactions
+        for (TxSupport txn : transactions)
+            txn.startTx();
+
+        // make sure that there are 2 more transactions
+        if (transactions[0].txCount() != txnCnt + 2)
+            System.out.println("Some transactions failed to start");
+
+        // terminate the transactions
+        for (TxSupport txn : transactions)
+            txn.commitTx();
+
+        // make sure that there are 2 less transactions
+        if (transactions[0].txCount() != txnCnt)
+            System.out.println("Some transactions failed to terminate");
+    }
+}

Added: labs/jbosstm/trunk/rest-tx/tx/src/test/resources/log4j.xml
===================================================================
--- labs/jbosstm/trunk/rest-tx/tx/src/test/resources/log4j.xml	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/tx/src/test/resources/log4j.xml	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,42 @@
+<?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="INFO"/>
+
+        <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="TRACE"/>
+
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%d [%t] %p - %m%n"/>
+        </layout>
+    </appender>
+
+    <category name="org.jboss.resteasy">
+        <level value="TRACE"/>
+        <appender-ref ref="console"/>
+        <appender-ref ref="file"/>
+    </category>
+
+    <category name="org.jboss.jbossts.rts">
+        <level value="TRACE"/>
+        <appender-ref ref="console"/>
+        <appender-ref ref="file"/>
+    </category>
+
+    <category name="com.arjuna">
+        <level value="INFO"/>
+        <appender-ref ref="console"/>
+        <appender-ref ref="file"/>
+    </category>
+</log4j:configuration>

Added: labs/jbosstm/trunk/rest-tx/webservice/pom.xml
===================================================================
--- labs/jbosstm/trunk/rest-tx/webservice/pom.xml	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/webservice/pom.xml	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,384 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	JBoss, Home of Professional Open Source Copyright 2009, Red Hat
+	Middleware LLC, and others contributors as indicated by the @authors
+	tag. All rights reserved. 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.
+-->
+
+<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">
+    <parent>
+        <groupId>org.jboss.jbossts.rts</groupId>
+        <artifactId>rest-tx</artifactId>
+        <version>1.0-M1-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>${artifactId.name}-web</artifactId>
+    <version>${version.artifactId}</version>
+    <packaging>war</packaging>
+    <name>RESTful Atomic Transactions War Packaging</name>
+    <description>Provides Transactions for web applications</description>
+
+    <properties>
+        <version.org.jboss.resteasy>2.0.0.GA</version.org.jboss.resteasy>
+        <version.artifactId>1.0-M1-SNAPSHOT</version.artifactId>
+        <artifactId.name>rest-tx</artifactId.name>
+        <version.org.jboss.jbossas>6.0.0.20100721-M4</version.org.jboss.jbossas>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.jboss.jbossas</groupId>
+                <artifactId>jboss-as-component-matrix</artifactId>
+                <version>${version.org.jboss.jbossas}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+
+<!--
+		<dependency>
+			<groupId>org.jboss.jbossas</groupId>
+			<artifactId>jboss-as-depchain</artifactId>
+			<version>${version.org.jboss.jbossas}</version>
+			<type>pom</type>
+		</dependency>
+		-->
+
+        <dependency>
+            <groupId>org.jboss.jbossts.rts</groupId>
+            <artifactId>rest-tx-api</artifactId>
+            <version>${version.artifactId}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.jbossts.rts</groupId>
+            <artifactId>rest-tx-api</artifactId>
+            <version>${version.artifactId}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.jbossts</groupId>
+            <artifactId>jbossjts</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+		<dependency>
+			<groupId>asm</groupId>
+			<artifactId>asm-all</artifactId>
+			<version>3.1</version>
+			<scope>test</scope>
+		</dependency>
+
+        <dependency>
+            <groupId>com.sun.jersey.jersey-test-framework</groupId>
+            <artifactId>jersey-test-framework-grizzly</artifactId>
+            <version>1.5-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <version>${version.org.jboss.resteasy}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jaxrs-api</artifactId>
+            <version>${version.org.jboss.resteasy}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>tjws</artifactId>
+            <version>${version.org.jboss.resteasy}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>create_sources_jar</id>
+                        <goals> <goal>jar</goal> </goals>
+                        <phase>prepare-package</phase>
+                    </execution>
+                </executions>
+                <configuration>
+                    <forceCreation>true</forceCreation>
+                    <includePom>true</includePom>
+                </configuration>
+            </plugin>
+			<plugin>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<configuration>
+					<redirectTestOutputToFile>true</redirectTestOutputToFile>
+					<trimStackTrace>false</trimStackTrace>
+					<printSummary>true</printSummary>
+					<includes>
+						<include>**/*UnitTest.java</include>
+					</includes>
+					<forkMode>always</forkMode>
+				</configuration>
+			</plugin>
+            
+        </plugins>
+    </build>
+
+    <profiles>
+		<profile>
+			<id>bust</id>
+			<!-- eventually starts but fails because it can't get a JMX connection
+			     so the stop goal isn't executed leaving the server running
+			     -->
+			<activation>
+				<activeByDefault>false</activeByDefault>
+			</activation>
+			<build>
+			<plugins>
+				<plugin>
+					<groupId>org.codehaus.mojo</groupId>
+					<artifactId>jboss-maven-plugin</artifactId>
+					<version>1.4.1</version>
+					<executions>
+						<execution>
+							<id>start-container</id>
+							<phase>pre-integration-test</phase>
+							<goals>
+								<goal>configure</goal>
+								<goal>startAndWait</goal>
+								<goal>deploy</goal>
+							</goals>
+						</execution>
+						<execution>
+							<id>stop-container</id>
+							<phase>post-integration-test</phase>
+							<goals>
+								<goal>undeploy</goal>
+								<goal>stop</goal>
+							</goals>
+						</execution>
+					</executions>
+					<configuration>
+						<!--
+						<jbossHome>${project.build.directory}/jboss-${version.org.jboss.jbossas}</jbossHome>
+						-->
+						<jbossHome>${env.JBOSS_HOME}</jbossHome>
+						<fileName>${basedir}/target/${artifactId.name}-web-${version.artifactId}.war</fileName>
+						<serverName>default</serverName>
+						<port>8080</port>
+					</configuration>
+				</plugin>
+			</plugins>
+			</build>
+		</profile>
+        <profile>
+            <id>remote</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>jboss-maven-plugin</artifactId>
+                        <version>1.3.1</version>
+                        <configuration>
+                            <jbossHome>${env.JBOSS_HOME}</jbossHome>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>jboss-deploy</id>
+                                <phase>pre-integration-test</phase>
+                                <goals>
+                                    <goal>deploy</goal>
+                                </goals>
+                                <configuration>
+                                    <fileName>${basedir}/target/${artifactId.name}-web-${version.artifactId}.war</fileName>
+                                    <serverName>default</serverName>
+                                    <skip>false</skip>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>jboss-undeploy</id>
+                                <phase>post-integration-test</phase>
+                                <goals>
+                                    <goal>undeploy</goal>
+                                </goals>
+                                <configuration>
+                                    <fileName>${basedir}/target/${artifactId.name}-web-${version.artifactId}.war</fileName>
+                                    <serverName>default</serverName>
+                                    <skip>false</skip>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>surefire-it</id>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                                    <trimStackTrace>false</trimStackTrace>
+                                    <printSummary>true</printSummary>
+                                    <includes>
+                                        <include>**/*IntegrationTest.java</include>
+                                    </includes>
+                                    <forkMode>always</forkMode>
+                                    <skip>false</skip>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+ 
+        <profile>
+            <id>embedded</id>
+            <activation>
+            <!-- TODO cant get this profile to work either - needs more investigation -->
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>integration-test</id>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+									<systemProperties>
+										<property>
+											<name>resttx.war</name>
+											<value>${basedir}/target/${artifactId.name}-web-${version.artifactId}.war</value>
+										</property>
+									</systemProperties>
+                                    <environmentVariables>
+                                        <resttx.war>${basedir}/target/${artifactId.name}-web-${version.artifactId}.war</resttx.war>    
+                                    </environmentVariables>
+                                    <additionalClasspathElements>
+                                        <additionalClasspathElement>${JBOSS_HOME}/client/jbossws-native-client.jar</additionalClasspathElement>
+                                        <!--
+                                        Because jbossweb.sar contains shared web.xml,
+                                        which must be visible from same CL as
+                                        TomcatDeployer.class.getClassLoader
+                                        -->
+                                        <additionalClasspathElement>${JBOSS_HOME}/server/default/deploy/jbossweb.sar</additionalClasspathElement>
+                                    </additionalClasspathElements>
+                                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                                    <trimStackTrace>false</trimStackTrace>
+                                    <printSummary>true</printSummary>
+                                    <includes>
+                                        <include>**/*IntegrationTest.java</include>
+                                    </includes>
+                                    <forkMode>always</forkMode>
+                                    <!--
+                                     MaxPermSize Required to bump the space for relective
+                                     data like classes, methods, etc. EMB-41. Endorsed
+                                     required for things like WS support (EMB-61)
+                                     -->
+                                    <argLine>-Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Djava.endorsed.dirs=${JBOSS_HOME}/lib/endorsed -Djboss.home=${JBOSS_HOME} -Djboss.boot.server.log.dir=${JBOSS_HOME}</argLine>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <!-- Get AS and put into "target" -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-dependency-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>unpack</id>
+                                <phase>pre-integration-test</phase> <!-- So run before testing -->
+                                <goals>
+                                    <goal>unpack</goal>
+                                </goals>
+                                <configuration>
+                                    <artifactItems>
+                                        <artifactItem>
+                                            <groupId>org.jboss.jbossas</groupId>
+                                            <artifactId>jboss-as-distribution</artifactId>
+                                            <version>${version.org.jboss.jbossas}</version>
+                                            <type>zip</type>
+                                            <overWrite>false</overWrite>
+                                            <outputDirectory>${project.build.directory}</outputDirectory>
+                                        </artifactItem>
+                                    </artifactItems>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>

Added: labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/jboss-web.xml
===================================================================
--- labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/jboss-web.xml	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/jboss-web.xml	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,3 @@
+<jboss-web>
+	<context-root>rest-tx</context-root>
+</jboss-web>

Added: labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/web.xml
===================================================================
--- labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/web.xml	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/webservice/src/main/webapp/WEB-INF/web.xml	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,56 @@
+<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
+<display-name>REST-tx Web Application</display-name>
+
+	<listener>
+		<listener-class>org.jboss.jbossts.star.service.ContextListener</listener-class>
+	</listener>
+
+    <servlet>
+        <servlet-name>Resteasy</servlet-name>
+        <servlet-class>
+            org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
+        </servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+      		<param-value>org.jboss.jbossts.star.service.TMApplication</param-value>
+        </init-param>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Resteasy</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+<!--
+   <context-param>
+      <param-name>javax.ws.rs.Application</param-name>
+      <param-value>org.jboss.jbossts.star.service.TMApplication</param-value>
+   </context-param>
+
+   <context-param>
+      <param-name>resteasy.servlet.mapping.prefix</param-name>
+      <param-value>/resteasy</param-value>
+   </context-param>
+
+   <listener>
+      <listener-class>
+         org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
+      </listener-class>
+   </listener>
+
+   <servlet>
+      <servlet-name>Resteasy</servlet-name>
+      <servlet-class>
+         org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
+      </servlet-class>
+   </servlet>
+
+   <servlet-mapping>
+      <servlet-name>Resteasy</servlet-name>
+      <url-pattern>/*</url-pattern>
+   </servlet-mapping>
+   -->
+
+</web-app>

Added: labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/jboss/jbossts/star/test/ClientIntegrationTest.java
===================================================================
--- labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/jboss/jbossts/star/test/ClientIntegrationTest.java	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/webservice/src/test/java/org/jboss/jbossts/star/test/ClientIntegrationTest.java	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,76 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, 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) 2010
+ * @author JBoss Inc.
+ */
+package org.jboss.jbossts.star.test;
+
+import org.jboss.jbossts.star.util.TxSupport;
+import org.jboss.jbossts.star.test.BaseTest;
+import org.jboss.jbossts.star.test.SpecTest;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class ClientIntegrationTest extends BaseTest {
+    SpecTest specTest = new SpecTest();
+
+    @BeforeClass
+    public static void startServer() throws Exception {
+        startContainer(TxSupport.DEF_TX_URL, "org.jboss.jbossts.star.test", BaseTest.TransactionalResource.class);
+    }
+    
+    @Test
+    public void testTransactionUrls() throws Exception {
+        specTest.testTransactionUrls();
+    }
+    @Test
+    public void testTransactionTimeout() throws Exception {
+        specTest.testTransactionTimeout();
+    }
+     @Test
+    public void testRollback() throws Exception {
+         specTest.testRollback();
+     }
+
+    @Test
+    public void testEnlistResource() throws Exception {
+        specTest.testEnlistResource();
+    }
+
+    @Test
+    public void testHeuristic() throws Exception {
+        specTest.testHeuristic();
+    }
+    @Test
+    public void testSpec6() throws Exception {
+        specTest.testSpec6();
+    }
+    @Test
+    public void testParticipantStatus() throws Exception {
+        specTest.testParticipantStatus();
+    }
+    @Test
+    public void testCannotEnlistDuring2PC() throws Exception {
+        specTest.testCannotEnlistDuring2PC();
+    }
+    @Test // recovery
+    public void testRecoveryURL() throws Exception {
+        specTest.testRecoveryURL();
+    }
+
+}

Added: labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/jndi.properties
===================================================================
--- labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/jndi.properties	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/jndi.properties	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,3 @@
+java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
+java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
+java.naming.provider.url=jnp://localhost:1099

Added: labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/log4j.xml
===================================================================
--- labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/log4j.xml	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/webservice/src/test/resources/log4j.xml	2010-10-08 14:54:32 UTC (rev 35465)
@@ -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="INFO"/>
+
+        <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="INFO"/>
+
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%d [%t] %p - %m%n"/>
+        </layout>
+    </appender>
+
+    <category name="org.jboss.resteasy">
+        <level value="WARN"/>
+        <appender-ref ref="console"/>
+        <appender-ref ref="file"/>
+    </category>
+
+    <category name="org.jboss.jbossts.rts">
+        <level value="INFO"/>
+        <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>

Added: labs/jbosstm/trunk/rest-tx/x
===================================================================
--- labs/jbosstm/trunk/rest-tx/x	                        (rev 0)
+++ labs/jbosstm/trunk/rest-tx/x	2010-10-08 14:54:32 UTC (rev 35465)
@@ -0,0 +1,191 @@
+.:
+total 20
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 15:11 docs
+-rw-rw-r--. 1 mmusgrov mmusgrov 1723 Oct  8 15:02 pom.xml
+-rw-rw-r--. 1 mmusgrov mmusgrov 2144 Oct  8 15:42 README.txt
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  8 15:50 tx
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  8 15:51 webservice
+-rw-rw-r--. 1 mmusgrov mmusgrov    0 Oct  8 15:51 x
+
+./docs:
+total 284
+-rw-rw-r--. 1 mmusgrov mmusgrov  58059 Sep 23 12:56 draft-nottingham-http-link-header-10.txt
+-rw-rw-r--. 1 mmusgrov mmusgrov 203817 Sep  9 17:30 REST-Atomic+v2+draft+4.pdf
+-rw-rw-r--. 1 mmusgrov mmusgrov  24329 Oct  8 15:11 REST-Atomic+v2+draft+4.txt
+
+./tx:
+total 12
+-rw-rw-r--. 1 mmusgrov mmusgrov 7123 Oct  8 15:12 pom.xml
+drwxrwxr-x. 4 mmusgrov mmusgrov 4096 Sep  1 17:47 src
+
+./tx/src:
+total 8
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  9 17:46 main
+drwxrwxr-x. 4 mmusgrov mmusgrov 4096 Sep  1 17:47 test
+
+./tx/src/main:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  1 17:47 java
+
+./tx/src/main/java:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  1 17:47 org
+
+./tx/src/main/java/org:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  1 17:47 jboss
+
+./tx/src/main/java/org/jboss:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  8 15:40 jbossts
+
+./tx/src/main/java/org/jboss/jbossts:
+total 4
+drwxrwxr-x. 6 mmusgrov mmusgrov 4096 Oct  8 15:40 star
+
+./tx/src/main/java/org/jboss/jbossts/star:
+total 16
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 15:09 provider
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 15:09 resource
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 15:09 service
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 15:10 util
+
+./tx/src/main/java/org/jboss/jbossts/star/provider:
+total 32
+-rw-rw-r--. 1 mmusgrov mmusgrov 2483 Oct  8 15:42 HttpResponseException.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 1445 Oct  8 15:42 HttpResponseMapper.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 1440 Oct  8 15:42 NotFoundMapper.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 1199 Oct  8 15:42 ResourceNotFoundException.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 1235 Oct  8 15:42 TMUnavailableException.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 1472 Oct  8 15:42 TMUnavailableMapper.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 1215 Oct  8 15:42 TransactionStatusException.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 1473 Oct  8 15:42 TransactionStatusMapper.java
+
+./tx/src/main/java/org/jboss/jbossts/star/resource:
+total 24
+-rw-rw-r--. 1 mmusgrov mmusgrov 15320 Oct  8 15:42 RESTRecord.java
+-rw-rw-r--. 1 mmusgrov mmusgrov  7844 Oct  8 15:42 Transaction.java
+
+./tx/src/main/java/org/jboss/jbossts/star/service:
+total 28
+-rw-rw-r--. 1 mmusgrov mmusgrov  1496 Oct  8 15:42 ContextListener.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 17543 Oct  8 15:42 Coordinator.java
+-rw-rw-r--. 1 mmusgrov mmusgrov  3613 Oct  8 15:42 TMApplication.java
+
+./tx/src/main/java/org/jboss/jbossts/star/util:
+total 24
+-rw-rw-r--. 1 mmusgrov mmusgrov  1260 Oct  8 15:42 LinkHolder.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 16748 Oct  8 15:42 TxSupport.java
+
+./tx/src/test:
+total 8
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  1 17:47 java
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 14:58 resources
+
+./tx/src/test/java:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  1 17:47 org
+
+./tx/src/test/java/org:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  1 17:47 jboss
+
+./tx/src/test/java/org/jboss:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  8 15:38 jbossts
+
+./tx/src/test/java/org/jboss/jbossts:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  8 15:38 star
+
+./tx/src/test/java/org/jboss/jbossts/star:
+total 4
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 15:38 test
+
+./tx/src/test/java/org/jboss/jbossts/star/test:
+total 52
+-rw-rw-r--. 1 mmusgrov mmusgrov 16624 Oct  8 15:47 BaseTest.java
+-rw-rw-r--. 1 mmusgrov mmusgrov  5313 Oct  8 15:42 CoordinatorTest.java
+-rw-rw-r--. 1 mmusgrov mmusgrov 21298 Oct  8 15:42 SpecTest.java
+
+./tx/src/test/resources:
+total 4
+-rw-rw-r--. 1 mmusgrov mmusgrov 1438 Sep  1 17:47 log4j.xml
+
+./webservice:
+total 24
+-rw-rw-r--. 1 mmusgrov mmusgrov 16488 Oct  8 15:04 pom.xml
+drwxrwxr-x. 4 mmusgrov mmusgrov  4096 Sep  9 11:47 src
+
+./webservice/src:
+total 8
+drwxrwxr-x. 4 mmusgrov mmusgrov 4096 Sep  9 11:47 main
+drwxrwxr-x. 4 mmusgrov mmusgrov 4096 Sep  9 11:47 test
+
+./webservice/src/main:
+total 8
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  9 11:47 java
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  7 11:46 webapp
+
+./webservice/src/main/java:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  9 11:47 org
+
+./webservice/src/main/java/org:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  9 11:47 jboss
+
+./webservice/src/main/java/org/jboss:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  8 15:41 jbossts
+
+./webservice/src/main/java/org/jboss/jbossts:
+total 4
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 15:41 star
+
+./webservice/src/main/java/org/jboss/jbossts/star:
+total 0
+
+./webservice/src/main/webapp:
+total 8
+-rw-rw-r--. 1 mmusgrov mmusgrov  172 Sep 22 15:33 hello.jsp
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 14:09 WEB-INF
+
+./webservice/src/main/webapp/WEB-INF:
+total 8
+-rw-rw-r--. 1 mmusgrov mmusgrov   63 Oct  7 12:33 jboss-web.xml
+-rw-rw-r--. 1 mmusgrov mmusgrov 1773 Oct  8 15:42 web.xml
+
+./webservice/src/test:
+total 8
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  9 11:47 java
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 14:52 resources
+
+./webservice/src/test/java:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  9 11:47 org
+
+./webservice/src/test/java/org:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Sep  9 11:47 jboss
+
+./webservice/src/test/java/org/jboss:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  8 15:42 jbossts
+
+./webservice/src/test/java/org/jboss/jbossts:
+total 4
+drwxrwxr-x. 3 mmusgrov mmusgrov 4096 Oct  8 15:42 star
+
+./webservice/src/test/java/org/jboss/jbossts/star:
+total 4
+drwxrwxr-x. 2 mmusgrov mmusgrov 4096 Oct  8 15:42 test
+
+./webservice/src/test/java/org/jboss/jbossts/star/test:
+total 4
+-rw-rw-r--. 1 mmusgrov mmusgrov 2498 Oct  8 15:48 ClientIntegrationTest.java
+
+./webservice/src/test/resources:
+total 8
+-rw-rw-r--. 1 mmusgrov mmusgrov  179 Sep  9 16:12 jndi.properties
+-rw-rw-r--. 1 mmusgrov mmusgrov 1615 Sep  9 11:47 log4j.xml



More information about the jboss-svn-commits mailing list