[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®ister=" + 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