diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java
index 87d285b4..e83c05e6 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java
@@ -35,14 +35,29 @@
* will be chosen). The spec makes no guarantees about when it will be invoked,
* just that is will eventually be called.
*
- * The id of the currently running LRA can be obtained by inspecting the incoming
- * JAX-RS headers. If this LRA is nested then the parent LRA MUST be present
- * in the header with the name
+ * If the annotated method is a JAX-RS resource method the id of the currently
+ * running LRA can be obtained by inspecting the incoming JAX-RS headers. If
+ * this LRA is nested then the parent LRA MUST be present in the header with the name
* {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA#LRA_HTTP_PARENT_CONTEXT_HEADER}.
*
+ * If the annotated method is not a JAX-RS resource method the id of the currently
+ * running LRA can be obtained by adhering to a predefined method signature as
+ * defined in the LRA specification document. Similarly the method may determine
+ * whether or not it runs with a nested LRA by providing a parameter to hold the parent id.
+ * For example,
+ *
+ *
+ * @Compensate
+ * public void compensate(URI lraId, URI parentId) { ...}
+ *
+ *
+ * would be a valid compensation method declaration. If an invalid signature is detected
+ * the {@link org.eclipse.microprofile.lra.participant.InvalidLRAParticipantDefinitionException}
+ * will be thrown during the application startup.
+ *
* Note that, according to the state model {@link LRAStatus} once an LRA has been
* asked to cancel it is no longer possible to join with it as a participant.
- * Therefore combining this annotation with an `@LRA` annotation that does not
+ * Therefore in JAX-RS, combining this annotation with an `@LRA` annotation that does not
* start a new LRA will result in a `412 PreCondition Failed` status code and is
* not advised. On the other hand, combining it with an `@LRA` annotation that
* begins a new LRA can in certain use case make sense, but in this case the LRA
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java
index 9be2901a..46537093 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java
@@ -37,14 +37,29 @@
* If the annotation is present on more than one method then an arbitrary one
* will be chosen.
*
- * The id of the currently running LRA can be obtained by inspecting the incoming
- * JAX-RS headers. If this LRA is nested then the parent LRA MUST be present
- * in the header with the name
+ * If the annotated method is a JAX-RS resource method the id of the currently
+ * running LRA can be obtained by inspecting the incoming JAX-RS headers. If
+ * this LRA is nested then the parent LRA MUST be present in the header with the name
* {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA#LRA_HTTP_PARENT_CONTEXT_HEADER}.
*
+ * If the annotated method is not a JAX-RS resource method the id of the currently
+ * running LRA can be obtained by adhering to a predefined method signature as
+ * defined in the LRA specification document. Similarly the method may determine
+ * whether or not it runs with a nested LRA by providing a parameter to hold the parent id.
+ * For example,
+ *
+ *
+ * @Complete
+ * public void complete(URI lraId, URI parentId) { ...}
+ *
+ *
+ * would be a valid completion method declaration. If an invalid signature is detected
+ * the {@link org.eclipse.microprofile.lra.participant.InvalidLRAParticipantDefinitionException}
+ * will be thrown during the application startup.
+ *
* Note that, according to the state model {@link LRAStatus} once an LRA has been
* asked to close it is no longer possible to join with it as a participant.
- * Therefore combining this annotation with an `@LRA` annotation that does not
+ * Therefore in JAX-RS, combining this annotation with an `@LRA` annotation that does not
* start a new LRA will result in a `412 PreCondition Failed` status code and is
* not advised. On the other hand, combining it with an `@LRA` annotation that
* begins a new LRA can in certain use case make sense, but in this case the LRA
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Forget.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Forget.java
index 1b26e16e..843d71ef 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Forget.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Forget.java
@@ -33,9 +33,7 @@
* in progress) or because of a failure (ie will never be able to finish)
* then it must remember the fact (by reporting it when asked for its'
* {@link Status})) until explicitly told that it can clean
- * up using this @Forget annotation. The annotated method
- * must be a standard JAX-RS endpoint annotated with the JAX-RS
- * @DELETE annotation.
+ * up using this @Forget annotation.
*
* A similar remark applies if the participant was enlisted in a
* nested LRA {@link LRA#value()}. Actions performed in the context
@@ -43,11 +41,27 @@
* is explicitly told it can clean up using this @Forget
* annotation.
*
- * The id of the currently running LRA can be obtained by inspecting the
- * incoming JAX-RS headers. If this LRA is nested then the parent LRA
- * MUST be present in the header with the name
+ * If the annotated method is a JAX-RS resource method the id of the currently
+ * running LRA can be obtained by inspecting the incoming JAX-RS headers. If
+ * this LRA is nested then the parent LRA MUST be present in the header with the name
* {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA#LRA_HTTP_PARENT_CONTEXT_HEADER}.
*
+ * If the annotated method is not a JAX-RS resource method the id of the currently
+ * running LRA can be obtained by adhering to a predefined method signature as
+ * defined in the LRA specification document. Similarly the method may determine
+ * whether or not it runs with a nested LRA by providing a parameter to hold the parent id.
+ * For example,
+ *
+ *
+ * @Forget
+ * public void forget(URI lraId, URI parentId) { ...}
+ *
+ *
+ * would be a valid forget method declaration. If an invalid signature is detected
+ * the {@link org.eclipse.microprofile.lra.participant.InvalidLRAParticipantDefinitionException}
+ * will be thrown during the application startup.
+ *
+ *
* Since the participant generally needs to know the id of the LRA in order
* to clean up there is generally no benefit to combining this annotation
* with the `@LRA` annotation (though it is not prohibited).
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java
index a6160f3a..df18eb78 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java
@@ -36,18 +36,35 @@
* or compensation notification but if the participant (the class that
* contains the Compensate and Complete annotations) does not
* support idempotency then it must be able to report its' status by
- * by annotating one of the methods with this @Status annotation
- * together with the @GET JAX-RS annotation.
+ * by annotating one of the methods with this @Status.
* The annotated method should report the status according to one of the
* {@link ParticipantStatus} enum values.
*
+ * If the annotated method is a JAX-RS resource method the id of the currently
+ * running LRA can be obtained by inspecting the incoming JAX-RS headers. If
+ * this LRA is nested then the parent LRA MUST be present in the header with the name
+ * {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA#LRA_HTTP_PARENT_CONTEXT_HEADER}.
+ *
+ * If the annotated method is not a JAX-RS resource method the id of the currently
+ * running LRA can be obtained by adhering to a predefined method signature as
+ * defined in the LRA specification document. Similarly the method may determine
+ * whether or not it runs with a nested LRA by providing a parameter to hold the parent id.
+ * For example,
+ *
+ *
+ * @Status
+ * public void status(URI lraId, URI parentId) { ...}
+ *
+ *
+ * would be a valid status method declaration. If an invalid signature is detected
+ * the {@link org.eclipse.microprofile.lra.participant.InvalidLRAParticipantDefinitionException}
+ * will be thrown during the application startup.
+ *
* If the participant has already responded successfully to an invocation
* of the @Compensate or @Complete method then it may
- * report 404 Not Found HTTP status code. This enables the
- * participant to free up resources.
- *
- * The id of the currently running LRA can be obtained by inspecting the
- * incoming JAX-RS headers.
+ * report 404 Not Found HTTP status code or in case of
+ * non-JAX-RS method returning {@link ParticipantStatus} to return null.
+ * This enables the participant to free up resources.
*
* Since the participant generally needs to know the id of the LRA in order
* to report its status there is generally no benefit to combining this
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/participant/InvalidLRAParticipantDefinitionException.java b/api/src/main/java/org/eclipse/microprofile/lra/participant/InvalidLRAParticipantDefinitionException.java
new file mode 100644
index 00000000..ca6f4d68
--- /dev/null
+++ b/api/src/main/java/org/eclipse/microprofile/lra/participant/InvalidLRAParticipantDefinitionException.java
@@ -0,0 +1,54 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.eclipse.microprofile.lra.participant;
+
+/**
+ * Runtime exception thrown when invalid non-JAX-RS LRA signature definition is detected.
+ *
+ * The prohibited valid signatures are:
+ * Return type:
+ * - void: successfull execution is mapped to `Compensated` or `Completed` participant statuses,
+ * error execution is handled by exceptions thrown in the participant method
+ * - not applicable for `@Status` participant methods
+ * - {@link org.eclipse.microprofile.lra.annotation.ParticipantStatus}
+ * - {@link javax.ws.rs.core.Response}: handled similarly as for JAX-RS participant methods
+ * - java.util.concurrent.CompletionStage: with the parameter of any of the previously
+ * defined types
+ *
+ * Arguments: up to 2 arguments of types in this order:
+ * - {@link java.net.URI}: representing current LRA context identification
+ * - {@link java.net.URI}: representing potentional parent LRA context identification
+ *
+ * Any other signature will result in deployment / start time of this type.
+ */
+public class InvalidLRAParticipantDefinitionException extends RuntimeException {
+
+ public InvalidLRAParticipantDefinitionException(String message) {
+ super(message);
+ }
+
+ public InvalidLRAParticipantDefinitionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InvalidLRAParticipantDefinitionException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/participant/JoinLRAException.java b/api/src/main/java/org/eclipse/microprofile/lra/participant/JoinLRAException.java
deleted file mode 100644
index a59161d4..00000000
--- a/api/src/main/java/org/eclipse/microprofile/lra/participant/JoinLRAException.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *******************************************************************************
- * Copyright (c) 2018 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-
-package org.eclipse.microprofile.lra.participant;
-
-import java.net.URI;
-
-/**
- * An exception used to report failures during enlistment of a participant in an LRA
- */
-public class JoinLRAException extends Exception {
- private URI lraId;
- private int statusCode;
-
- /**
- * @return the specific reason for why the enlistment failed
- */
- public int getStatusCode() {
- return statusCode;
- }
-
- /**
- * @return the LRA that join request related to
- */
- public URI getLraId() {
- return lraId;
- }
-
- public JoinLRAException(URI lraId, int statusCode, String message, Throwable cause) {
- super(String.format("%s: %s", lraId, message), cause);
-
- this.lraId = lraId;
- this.statusCode = statusCode;
- }
-}
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAManagement.java b/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAManagement.java
deleted file mode 100644
index f7b03b86..00000000
--- a/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAManagement.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- *******************************************************************************
- * Copyright (c) 2018 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-
-package org.eclipse.microprofile.lra.participant;
-
-import javax.enterprise.context.ApplicationScoped;
-import java.net.URI;
-import java.time.temporal.ChronoUnit;
-
-@ApplicationScoped
-public interface LRAManagement {
- /**
- * Join an existing LRA.
- *
- * @param participant an instance of a {@link LRAParticipant} that will be
- * notified when the target LRA ends
- * @param lraId the LRA that the join request pertains to
- * @param timeLimit the time for which the participant should remain valid. When
- * this time limit is exceeded the participant may longer be able
- * to fulfil the protocol guarantees.
- * @param unit the unit that the timeLimit parameter is expressed in
- *
- * @return a recovery URI for this enlistment
- *
- * @throws JoinLRAException if the request to the coordinator failed.
- * {@link JoinLRAException#getCause()} and/or
- * {@link JoinLRAException#getStatusCode()} may provide a more specific reason
- */
- URI joinLRA(LRAParticipant participant, URI lraId, Long timeLimit,
- ChronoUnit unit)
- throws JoinLRAException;
-
- /**
- * Join an existing LRA. In contrast to the other form of registration this
- * method does not indicate a time limit for the participant meaning that the
- * participant registration will remain valid until it terminates successfully
- * or unsuccessfully (ie it will never be timed out externally).
- *
- * @param participant an instance of a {@link LRAParticipant} that will be
- * notified when the target LRA ends
- * @param lraId the LRA that the join request pertains to
- *
- * @return a recovery URI for this enlistment
- *
- * @throws JoinLRAException if the request to the coordinator failed.
- * {@link JoinLRAException#getCause()} and/or
- * {@link JoinLRAException#getStatusCode()} may provide a more specific reason
- */
- URI joinLRA(LRAParticipant participant, URI lraId) throws JoinLRAException;
-
-}
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAParticipant.java b/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAParticipant.java
deleted file mode 100644
index 7de2c8ee..00000000
--- a/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAParticipant.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *******************************************************************************
- * Copyright (c) 2018 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-
-package org.eclipse.microprofile.lra.participant;
-
-import javax.ws.rs.NotFoundException;
-import java.io.Serializable;
-import java.net.URI;
-import java.util.concurrent.Future;
-
-/**
- * The API for notifying participants that a LRA is completing or cancelling.
- * A participant joins with an LRA via a call to
- * {@link LRAManagement#joinLRA(LRAParticipant, URI)}
- */
-public interface LRAParticipant extends Serializable {
- /**
- * Notifies the participant that the LRA is closing
- * @param lraId the LRA that is closing
- * @return null if the participant completed successfully. If the participant
- * cannot complete immediately it should return a future that the caller
- * can used to monitor progress. If the JVM crashes before the participant
- * can finish it should expect this method to be called again. If the
- * participant fails to complete it must cancel the future or throw a
- * TerminationException.
- * @throws NotFoundException the participant does not know about this LRA
- * @throws TerminationException the participant was unable to complete and will
- * never be able to do so
- */
- Future completeWork(URI lraId)
- throws NotFoundException, TerminationException;
-
- /**
- * Notifies the participant that the LRA is cancelling
- * @param lraId the LRA that is closing
- * @return null if the participant completed successfully. If the participant
- * cannot complete immediately it should return a future that the
- * caller can use to monitor progress. If the JVM crashes before
- * the participant can finish it should expect this method to be
- * called again. If the participant fails to complete it must cancel
- * the future or throw a TerminationException.
- * @throws NotFoundException the participant does not know about this LRA
- * @throws TerminationException the participant was unable to complete and
- * will never be able to do so
- */
- Future compensateWork(URI lraId)
- throws NotFoundException, TerminationException;
-}
-
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/participant/TerminationException.java b/api/src/main/java/org/eclipse/microprofile/lra/participant/TerminationException.java
deleted file mode 100644
index 98654e75..00000000
--- a/api/src/main/java/org/eclipse/microprofile/lra/participant/TerminationException.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- *******************************************************************************
- * Copyright (c) 2018 Contributors to the Eclipse Foundation
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information regarding copyright ownership.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-
-package org.eclipse.microprofile.lra.participant;
-
-public class TerminationException extends Exception {
- private static final long serialVersionUID = 1L;
-}
diff --git a/spec/src/main/asciidoc/microprofile-lra-spec.adoc b/spec/src/main/asciidoc/microprofile-lra-spec.adoc
index 742af9e5..491a001f 100644
--- a/spec/src/main/asciidoc/microprofile-lra-spec.adoc
+++ b/spec/src/main/asciidoc/microprofile-lra-spec.adoc
@@ -236,14 +236,8 @@ when the right subset of work (LRAs) is arrived at by the application
will that subset be confirmed; all other LRAs will be told to cancel
(complete in a failure state).
-In the rest of this proposal we specify two different APIs for
-controlling the life cycle of and participation in LRAs and a third API
-for writing participants:
-
-1. <> for controlling
-the life cycle and membership of LRAs
-2. <>
-to support services that do not use JAX-RS
+In the rest of this proposal we specify an API for
+controlling the life cycle of and participation in LRAs:
[[java-annotations-for-lras]]
=== Java Annotations for LRAs
@@ -505,10 +499,10 @@ the method finishes and the `end = true` element on the confirmTrip method force
[[compensating-activities]]
==== Compensating Activities
-The application developer indicates which JAX-RS method to use for a compensating
-activity by annotating it with both the `@Compensate` and the JAX-RS `@PUT` annotations.
+The application developer indicates which method to use for a compensating
+activity by annotating it with the `@Compensate` annotation.
Whenever the associated resource is invoked in the context of
-an LRA the endpoint corresponding to the `@Compensate` method MUST be enlisted with
+an LRA the method corresponding to the `@Compensate` method MUST be enlisted with
the LRA. If the LRA is subsequently cancelled then the compensation method MUST be invoked.
The specification does not mandate when this method is invoked
@@ -530,17 +524,17 @@ An example of how to use this annoation is:
}
----
-The resource class MAY also contain a method marked with the `@Complete` and the
-JAX-RS `@PUT` annotations. If such a method is present then the method MUST
-be invoked when the associated LRA is closed. Again, the specification does
-not MANDATE when the method is called, just that it will eventually be invoked.
-Typically the resource would use this call to perform any clean up actions. The
-method is optional since such clean up actions may not be necessary,
-for example consider a system that just tracks hotel reservations and has operations
-for booking a room or cancelling the reservation (`@Compensate`). Since this system
-is passive, once a room is booked, it does not make any difference if the LRA is
-completed or not: the room will be unavailable for others. If it receives a call
-to `@Compensate` then it will free the room. But it won't do anything on `@complete`.
+The resource class MAY also contain a method marked with the `@Complete` annotation.
+If such a method is present then the method MUST be invoked when the associated LRA
+is closed. Again, the specification does not MANDATE when the method is called,
+just that it will eventually be invoked. Typically the resource would use this call
+to perform any clean up actions. The method is optional since such clean up actions
+may not be necessary, for example consider a system that just tracks hotel reservations
+and has operations for booking a room or cancelling the reservation (`@Compensate`).
+Since this system is passive, once a room is booked, it does not make any difference
+if the LRA is completed or not: the room will be unavailable for others. If it receives
+a call to `@Compensate` then it will free the room. But it won't do anything on
+`@Complete`.
If the participant successfully compensates or completes then it may forget about
the LRA. Otherwise it should remember that it is still associated with the LRA and
@@ -555,11 +549,144 @@ of these methods. If it knows it will never be able to compensate or complete th
it MUST remember the fact until explicitly told that it can clean up by providing
a method annotated with `@Forget` (the requirement is marked MUST because message
delivery is not guaranteed in a distributed system).
-The forget method MUST be annotated with the JAX-RS `@DELETE` annotation.
If there is no `@Status` then the `@Compensate` or `@Complete` methods will continue
to be invoked until the implementation knows it has the final status.
+If the `@Compensate` or `@Complete` annotation is present on multiple methods
+then an arbitrary one is chosen.
+
+The javadoc for the <> provides
+more details about this annotation.
+
+Similarly, the javadoc for the <>
+provides details about the `@Complete` annotation.
+
+==== Participant marker annotations method signatures
+
+The participant marker annotations are annotations that allow users to mark a method
+for the execution by the LRA implementation according to the <>. These annotations are:
+
+* `@Compensate` -- a method to be executed when the LRA is cancelled
+* `@Complete` -- a method to be executed when the LRA is closed
+* `@Status` -- a method that allow user to state status of the participant with regards
+to a paricular LRA
+* `@Forget` -- a method to be executed when the LRA allows participant to
+clear all associated information
+
+This specification differentiates two types of participant method definitions --
+methods associated with the JAX-RS resource method or the methods which are not
+bound to JAX-RS.
+
+[[jaxrs-participant-methods]]
+===== JAX-RS participant methods
+
+The following table presents expectations that are placed on individual participant
+annotations when associated with JAX-RS resource methods:
+
+[[jaxrs-response-table]]
+|===
+| Annotation | HTTP method | Expected status codes | Response
+
+| `@Compensate`
+| PUT
+| 200, 202, 404, 412
+| <>
+
+| `@Complete`
+| PUT
+| 200, 202, 404, 412
+| <>
+
+| `@Status`
+| GET
+| 200, 202, 404, 412
+| <>
+
+| `@Forget`
+| DELETE
+| 200, 404, 412
+| no expectations
+
+|===
+
+If the participant annotation is not accompanied by an associated JAX-RS HTTP method
+annotation according to the <>, the error SHOULD be reported using a JAX-RS
+exception mapper that maps to a `412 Precondition Failed` HTTP status code.
+
+[[non-jaxrs-participant-methods]]
+===== Non-JAX-RS participant methods
+
+When the participant annotations are applied to the non-JAX-RS resource methods they
+MUST adhere to these predefined signatures:
+
+* *Return type*:
+** `void`: successfull execution is mapped to `Compensated` or `Completed` participant statuses,
+ error execution is handled by <> thrown in the participant method
+*** not applicable for `@Status` participant methods
+** <>
+** `javax.ws.rs.core.Response`: handled similarly as for
+<>
+** `java.util.concurrent.CompletionStage`: with the parameter of any of the previously
+defined types
+* *Arguments*: up to 2 arguments of types in this order:
+** `java.net.URI`: representing current LRA context identification
+** `java.net.URI`: representing potentional parent LRA context identification
+
+Declaring more than two arguments, different types of arguments or different return type
+for any non-JAX-RS method annotatated with the participant marker annotation MUST result
+in the deployment time `InvalidLRAParticipantDefinitionException`.
+Please note that both arguments are optional but the order is required. This means that
+if only one argument is provided this argument will contain the value of the current
+active LRA context (not the parent LRA context in case of nested LRA).
+
+Examples of valid signatures:
+
+[source,java]
+----
+@Compensate
+public void compensate(URI lraId, URI parentId)
+
+@Complete
+public Response complete(URI lraId)
+
+@Status
+public CompletionStage status(URI lraId)
+----
+
+Examples of invalid signatures:
+
+[source,java]
+----
+@Compensate
+public void compensate(String lraId, String parentId) // invalid types of arguments
+
+@Compensate
+public String compensate(URI lraId) // invalid return type
+
+@Forget
+public void forget(URI lraId, URI parentId, String additional) // too many arguments
+----
+
+[[non-jax-rs-exceptions]]
+If any of the described methods throws an exception, we distinguish two cases depending
+on the exception type:
+
+* `WebApplicationException` -- the exception is mapped to the HTTP response it carries
+and then handled as defined in the section
+<>
+* any other exception
+** @Compensate and @Complete - results into `FailedToCompensate` or `FailedToComplete`
+participant states
+** @Status and @Forget - as the participant may have already compensated (or completed)
+or may in the process of compensation (completion) the exception in these methods should
+result into failure condition (in JAX-RS this condition is represented by
+500 return HTTP status code) which individual interpretation is left further unspecified.
+
+[[eventual-compensations]]
+==== Eventual compensations
+
[[async-compensators]]
If the resource cannot perform a compensation activity
immediately the `@Compensate` method SHOULD do one or more of the following:
@@ -573,21 +700,6 @@ The `@Status` method, if present, MUST report the progress of the compensation.
Similarly if it cannot perform a completion activity immediately.
-If the `@Compensate` or `@Complete` annotation is present on multiple methods
-then an arbitrary one is chosen. If the annotation is not accompanied by
-a JAX-RS `@PUT` annotation the error SHOULD be reported using a JAX-RS
-exception mapper that maps to a `412 Precondition Failed` HTTP status code.
-
-footnote:[issue #15 (Provide an annotation to supply opaque data during
-participant registration) allows participants to register opaque data when
-joining with an LRA. This data is made available to the completion and
-compensation callbacks]
-
-The javadoc for the <> provides
-more details about this annotation.
-
-Similarly, the javadoc for the <>
-provides details about the `@Complete` annotation.
[[nesting-lras]]
==== Nesting LRAs
@@ -713,7 +825,9 @@ report the status according to the
If the participant has already responded successfully to an `@Compensate`
or `@Complete` method invocation then it MAY report `404 Not Found`
-HTTP status code. This enables the participant to free up resources.
+HTTP status code or in the case of non-JAX-RS method returning
+<> `null`.
+This enables the participant to free up resources.
[[forgetting-an-lra]]
==== Forgetting an LRA
@@ -723,8 +837,7 @@ If a participant is unable to complete or compensate immediately
in progress) or because of a failure (i.e. will never be able to finish)
then it must remember the fact (by reporting its' status via the
`@Status` method) until explicitly told that it can clean up
-using this `@Forget` annotation. The forget annotation must be a
-standard JAX-RS endpoint annotated with the JAX-RS `@DELETE` annotation.
+using this `@Forget` annotation.
This requirement ensures that the implementation will be able to guarantee
the expectations of the LRA protocol under various failure conditions.
@@ -847,87 +960,6 @@ survive system failures. The specification is not prescriptive about how
an implementation achieves resiliency provided that it obeys the requirements
of the specification as laid out in this document.
-[[java-based-lra-participant-registration-api]]
-=== Java based LRA participant registration API
-
-If an application does not directly expose JAX-RS endpoints for
-compensation activities then participants can join an LRA directly using an instance of
-<>
-(https://github.com/eclipse/microprofile-lra/tree/master/api/src/main/java/org/eclipse/microprofile/lra/participant[github link]).
-This interface requires that a participant be a Java class conforming to the
-<>
-(https://github.com/eclipse/microprofile-lra/tree/master/api/src/main/java/org/eclipse/microprofile/lra/participant[github link]).
-
-When an LRA is closed or cancelled the implementation [of this specification] is responsible
-for invoking LRAParticipant completion or compensation
-callbacks on any participants that were registered via the `LRAManagement#joinLRA` method.
-
-How the application obtains an LRAManagement instance is unspecified
-(but CDI injection could be an obvious choice). For example, the following code listing
-shows how to inject instances of the API and how to perform work in the context of an LRA:
-
-[source,java]
-----
-@Path("/")
-public class UnannotatedParticpant implements LRAParticipant {
- @Inject
- private LRAManagement lraManagement;
-
- public void doInTransaction() throws JoinLRAException
- {
- // start a new LRA with an unlimited timeout
- // by invoking a suitable JAX-RS resource method annotated
- // with @LRA(value = LRA.Type.REQUIRES_NEW, end = false)
- ...
-
- // Join the LRA as a participant.
- lraManagement.joinLRA(this, lraId, 0L, ChronoUnit.SECONDS);
-
- // do something interesting
-
- // close the LRA by invoking a suitable JAX-RS resource method
- // annotated with, for example,
- // @LRA(value = LRA.Type.SUPPORTS, end = true)
- // This will cause the completion call back to be invoked
- ...
-
- // the completeWork method will have been invoked at this point
- }
-
- // the callback that will be invoked if the LRA is closed
- @Override
- public Future completeWork(URI lraId)
- throws NotFoundException, TerminationException
- {
- return null;
- }
-
- // the callback that will be invoked if the LRA is cancelled
- @Override
- public Future compensateWork(URI lraId)
- throws NotFoundException, TerminationException
- {
- return null;
- }
-}
-----
-
-The framework must guarantee that participants will still be triggered (the LRA protocol still provides the "all or
-nothing" guarantees that traditional transactions give).
-This means that an implementation for all JAX-RS resources which implement the _LRAParticipant_ interface directly
-or indirectly, MUST create some proxy which can be called by the spec implementation. +
-This is to ensure that after this class has joined an LRA (through the _lraManagement.joinLRA()_ ) the _complete_ or
-_compensate_ methods can be called by the spec implementation in case the microservice crashed before this notification could
-be delivered.
-
-Serializable participants need to know how to contact the original
-business application in order to trigger compensation activities whereas
-a JAX-RS based solution need only persist resource paths which are likely
-to correspond to existing microservice endpoints. In other words, from
-an administrative and manageability point of view, it may be simpler to
-use one of the other APIs such as the <>.
-
[[appendix-1]]
== Appendix 1: Selected Javadoc API Descriptions
@@ -965,17 +997,3 @@ include::{sourcedir}/org/eclipse/microprofile/lra/annotation/Complete.java[Leave
include::{sourcedir}/org/eclipse/microprofile/lra/annotation/ParticipantStatus.java/[ParticipantStatus]
----
<<<
-
-=== LRAManagement
-[[source-LRAManagement]]
-----
-include::{sourcedir}/org/eclipse/microprofile/lra/participant/LRAManagement.java[LRAManagement]
-----
-<<<
-
-=== LRAParticipant
-[[source-LRAParticipant]]
-----
-include::{sourcedir}/org/eclipse/microprofile/lra/participant/LRAParticipant.java[LRAParticipant]
-----
-<<<
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckInvalidParticipantSignaturesTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckInvalidParticipantSignaturesTests.java
new file mode 100644
index 00000000..cf0ec019
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckInvalidParticipantSignaturesTests.java
@@ -0,0 +1,132 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.eclipse.microprofile.lra.tck;
+
+import org.eclipse.microprofile.lra.participant.InvalidLRAParticipantDefinitionException;
+import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.InvalidArgumentTypesParticipant;
+import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.InvalidReturnTypeParticipant;
+import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.TooManyArgsParticipant;
+import org.jboss.arquillian.container.spi.client.container.DeploymentException;
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+/**
+ *
+ * TCK that verifies that invalid non-JAX-RS participant method signatures are reported during deployment
+ *
+ *
+ *
+ * Each test deploys an archive containing single invalid participant containing an error in its participant
+ * method signature and expects that such deployment is aborted with {@link InvalidLRAParticipantDefinitionException}
+ * according to the specification.
+ *
+ */
+@RunWith(Arquillian.class)
+public class TckInvalidParticipantSignaturesTests {
+
+ private static final String INVALID_RETURN_TYPE_DEPLOYMENT = "nonjaxrs-return-type-deploy";
+ private static final String TOO_MANY_ARGS_DEPLOYMENT = "too-many-args-deploy";
+ private static final String INVALID_ARGUMENT_TYPE_DEPLOYMENT = "nonjaxrs-argument-type-deploy";
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public DeploymentNameRule deploymentNameRule = new DeploymentNameRule();
+
+ @ArquillianResource
+ private Deployer deployer;
+
+ @Deployment(name = INVALID_RETURN_TYPE_DEPLOYMENT, managed = false)
+ public static WebArchive deployInvalidReturnTypeParticipant() {
+ return createArchive(InvalidReturnTypeParticipant.class);
+ }
+
+ @Deployment(name = TOO_MANY_ARGS_DEPLOYMENT, managed = false)
+ public static WebArchive deployTooManyArgsParticipant() {
+ return createArchive(TooManyArgsParticipant.class);
+ }
+
+ @Deployment(name = INVALID_ARGUMENT_TYPE_DEPLOYMENT, managed = false)
+ public static WebArchive deployInvalidArgumentTypeParticipant() {
+ return createArchive(InvalidArgumentTypesParticipant.class);
+ }
+
+ private static WebArchive createArchive(Class> participantClass) {
+ String archiveName = participantClass.getSimpleName();
+ return ShrinkWrap
+ .create(WebArchive.class, archiveName + ".war")
+ .addClasses(participantClass, JaxRsActivator.class)
+ .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
+ }
+
+ @After
+ public void after() {
+ deployer.undeploy(deploymentNameRule.deploymentName);
+ }
+
+ /**
+ * Verify that invalid return type (String) in participant method is detected
+ */
+ @Test
+ public void invalidReturnTypeInParticipantMethodTest() {
+ testInvalidDeployment(INVALID_RETURN_TYPE_DEPLOYMENT);
+ }
+
+ /**
+ * Verify that too many arguments (more than 2) in partcipant method are detected
+ */
+ @Test
+ public void tooManyArgsInParticipantMethodTest() {
+ testInvalidDeployment(TOO_MANY_ARGS_DEPLOYMENT);
+ }
+
+ /**
+ * Verigy that invalid type of argument (int) in participant method is detected
+ */
+ @Test
+ public void invalidArgumentTypeInParticipantMethodTest() {
+ testInvalidDeployment(INVALID_ARGUMENT_TYPE_DEPLOYMENT);
+ }
+
+ private void testInvalidDeployment(String deploymentName) {
+ deploymentNameRule.deploymentName = deploymentName;
+ expectedException.expect(DeploymentException.class);
+ expectedException.expectMessage(InvalidLRAParticipantDefinitionException.class.getSimpleName());
+
+ deployer.deploy(deploymentName);
+ }
+
+ private static final class DeploymentNameRule extends TestName {
+
+ String deploymentName;
+ }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java
index 87f42537..efa6f238 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java
@@ -20,29 +20,16 @@
package org.eclipse.microprofile.lra.tck;
import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
-import org.eclipse.microprofile.lra.tck.participant.api.Util;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
-import org.jboss.shrinkwrap.api.ShrinkWrap;
-import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestName;
import org.junit.runner.RunWith;
-import javax.inject.Inject;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
-import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
-import java.net.MalformedURLException;
import java.net.URI;
-import java.net.URL;
import java.time.temporal.ChronoUnit;
import java.util.logging.Logger;
@@ -89,47 +76,12 @@
*
*/
@RunWith(Arquillian.class)
-public class TckLRATypeTests {
+public class TckLRATypeTests extends TckTestBase {
private static final Logger LOGGER = Logger.getLogger(TckLRATypeTests.class.getName());
-
- @Rule public TestName testName = new TestName();
-
- @Inject
- private LraTckConfigBean config;
-
- private LRAClientOps lraClient;
-
- private static Client tckSuiteClient;
-
- private WebTarget tckSuiteTarget;
-
+
@Deployment(name = "lra-type-tck-tests")
public static WebArchive deploy() {
- String archiveName = TckLRATypeTests.class.getSimpleName().toLowerCase();
- return ShrinkWrap
- .create(WebArchive.class, archiveName + ".war")
- .addPackages(true, "org.eclipse.microprofile.lra.tck")
- .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
- }
-
- @AfterClass
- public static void afterClass() {
- if(tckSuiteClient != null) {
- tckSuiteClient.close();
- }
- }
-
- @Before
- public void before() {
- LOGGER.info("Running test: " + testName.getMethodName());
- setUpTestCase();
-
- try {
- tckSuiteTarget = tckSuiteClient.target(URI.create(new URL(config.tckSuiteBaseUrl()).toExternalForm()));
- lraClient = new LRAClientOps(tckSuiteTarget);
- } catch (MalformedURLException mfe) {
- throw new IllegalStateException("Cannot create URL for the LRA TCK suite base url " + config.tckSuiteBaseUrl(), mfe);
- }
+ return deploy(TckLRATypeTests.class.getSimpleName().toLowerCase());
}
@After
@@ -137,10 +89,6 @@ public void after() {
lraClient.cleanUp(LOGGER, testName.getMethodName());
}
- private void setUpTestCase() {
- tckSuiteClient = ClientBuilder.newClient();
- }
-
// enum to indicate which checks to perform on the expected and actual active LRA after running a resource method
private enum MethodLRACheck {
NONE, NOT_PRESENT, EQUALS, NOT_EQUALS
@@ -339,19 +287,4 @@ private void resourceRequest(String path, boolean startLRA, int expectedStatus,
response.close();
}
}
-
- /**
- * The started LRA will be named based on the class name and the running test name.
- */
- private String lraClientId() {
- return this.getClass().getSimpleName() + "#" + testName.getMethodName();
- }
-
- /**
- * Adjusting the default timeout by the specified timeout factor
- * which can be defined by user.
- */
- private long lraTimeout() {
- return Util.adjust(LraTckConfigBean.LRA_TIMEOUT_MILLIS, config.timeoutFactor());
- }
}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckParticipantTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckParticipantTests.java
new file mode 100644
index 00000000..cb509c4c
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckParticipantTests.java
@@ -0,0 +1,205 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.eclipse.microprofile.lra.tck;
+
+import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.valid.ValidLRACSParticipant;
+import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.valid.ValidLRAParticipant;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * TCK to verify that valid non-JAX-RS participant method signatures are respected
+ */
+@RunWith(Arquillian.class)
+public class TckParticipantTests extends TckTestBase {
+
+ private static final String VALID_DEPLOYMENT = "valid-deploy";
+
+ private int beforeCompletedCount;
+ private int beforeCompensatedCount;
+ private int beforeStatusCount;
+ private int beforeForgetCount;
+
+ @Deployment(name = VALID_DEPLOYMENT)
+ public static WebArchive deployValidParticipant() {
+ return TckTestBase.deploy(VALID_DEPLOYMENT)
+ .addPackage(ValidLRAParticipant.class.getPackage());
+ }
+
+ @Before
+ public void before() {
+ super.before();
+
+ beforeCompensatedCount = getCompensateCount();
+ beforeCompletedCount = getCompleteCount();
+ beforeStatusCount = getStatusCount();
+ beforeForgetCount = getForgetCount();
+ }
+
+ /**
+ * Test verifies that non-JAX-RS @Complete method is invoked according to the
+ * LRA protocol and that if {@link javax.ws.rs.WebApplicationException} is
+ * thrown inside of a non-JAX-RS participant method than {@link Response} it
+ * is carrying is extracted and acted upon accoding to LRA response handling
+ */
+ @Test
+ public void validWebApplicationExceptionReturnedTest() {
+ Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH)
+ .path(ValidLRAParticipant.ENLIST_WITH_COMPLETE)
+ .request()
+ .get();
+
+ assertEquals("The 200 status response is expected",
+ Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals("Non JAX-RS @Complete method throwing WebApplicationException shoud have been called",
+ beforeCompletedCount + 1, getCompleteCount());
+ assertEquals("@Compensate method should not have been called as LRA completed succesfully",
+ beforeCompensatedCount, getCompensateCount());
+
+ }
+
+ /**
+ * This test verifies chained call of non-JAX-RS participant methods. First
+ * it starts and cancels a new LRA. @Compensate non-JAX-RS method then returns
+ * {@link org.eclipse.microprofile.lra.annotation.ParticipantStatus#Compensating}
+ * (see {@link ValidLRAParticipant}) indicating that non-JAX-RS @Status method should
+ * be invoked. The test then waits for recovery and then verifies that @Status method is
+ * called. This method finishes compensation with return of the {@link Response} object
+ * indicating failure and so the test then verifies that non-JAX-RS @Forget method has
+ * also been called.
+ */
+ @Test
+ public void validSignaturesChainTest() throws InterruptedException {
+ Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH)
+ .path(ValidLRAParticipant.ENLIST_WITH_COMPENSATE)
+ .request()
+ .get();
+
+ assertEquals("The 500 status response is expected",
+ Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+ assertEquals("Non JAX-RS @Compensate method should have been called",
+ beforeCompensatedCount + 1, getCompensateCount());
+ assertEquals("@Complete method should not have been called as LRA compensated",
+ beforeCompletedCount, getCompleteCount());
+
+ replayEndPhase(ValidLRAParticipant.RESOURCE_PATH);
+
+ assertEquals("Non JAX-RS @Status method should have been called",
+ beforeStatusCount + 1, getStatusCount());
+ assertEquals("Non JAX-RS @Forget method should have been called",
+ beforeForgetCount + 1, getForgetCount());
+
+ }
+
+ /**
+ * Test verifies {@link java.util.concurrent.CompletionStage} parametrized with
+ * {@link Void} as valid non-JAX-RS participant method return type
+ */
+ @Test
+ public void testNonJaxRsCompletionStageVoid() {
+ int beforeCompletedCount = getCompleteCount(ValidLRACSParticipant.ROOT_PATH);
+ int beforeCompensateCount = getCompensateCount(ValidLRACSParticipant.ROOT_PATH);
+
+ Response response = tckSuiteTarget.path(ValidLRACSParticipant.ROOT_PATH)
+ .path(ValidLRACSParticipant.ENLIST_WITH_COMPENSATE)
+ .request()
+ .get();
+
+ assertEquals("The 500 status response is expected",
+ Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+ assertEquals("Non JAX-RS @Compensate method with CompletionStage should have been called",
+ beforeCompensateCount + 1, getCompensateCount(ValidLRACSParticipant.ROOT_PATH));
+ assertEquals("Non JAX-RS @Complete method should have not been called",
+ beforeCompletedCount, getCompleteCount(ValidLRACSParticipant.ROOT_PATH));
+ }
+
+ /**
+ * Test verifyies {@link java.util.concurrent.CompletionStage} parametrized by
+ * {@link Response} and {@link org.eclipse.microprofile.lra.annotation.ParticipantStatus} as valid
+ * non-JAX-RS participant methods return types
+ */
+ @Test
+ public void testNonJaxRsCompletionStageResponseAndParticipantStatus() throws InterruptedException {
+ int beforeCompletedCount = getCompleteCount(ValidLRACSParticipant.ROOT_PATH);
+ int beforeCompensateCount = getCompensateCount(ValidLRACSParticipant.ROOT_PATH);
+ int beforeStatusCount = getStatusCount(ValidLRACSParticipant.ROOT_PATH);
+
+ Response response = tckSuiteTarget.path(ValidLRACSParticipant.ROOT_PATH)
+ .path(ValidLRACSParticipant.ENLIST_WITH_COMPLETE)
+ .request()
+ .get();
+
+ assertEquals("The 200 status response is expected",
+ Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals("Non JAX-RS @Complete method with CompletionStage should have been called",
+ beforeCompletedCount + 1, getCompleteCount(ValidLRACSParticipant.ROOT_PATH));
+ assertEquals("Non JAX-RS @Compensate method should have not been called",
+ beforeCompensateCount, getCompensateCount(ValidLRACSParticipant.ROOT_PATH));
+
+ replayEndPhase(ValidLRACSParticipant.ROOT_PATH);
+
+ assertEquals("Non JAX-RS @Status method with CompletionStage should have been called",
+ beforeStatusCount + 1, getStatusCount(ValidLRACSParticipant.ROOT_PATH));
+ }
+
+ private int getCompleteCount() {
+ return getCompleteCount(ValidLRAParticipant.RESOURCE_PATH);
+ }
+
+ private int getCompensateCount() {
+ return getCompensateCount(ValidLRAParticipant.RESOURCE_PATH);
+ }
+
+ private int getStatusCount() {
+ return getStatusCount(ValidLRAParticipant.RESOURCE_PATH);
+ }
+
+ private int getForgetCount() {
+ Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH)
+ .path(ValidLRAParticipant.FORGET_COUNT_PATH).request().get();
+ return response.readEntity(Integer.class);
+ }
+
+ private int getCompleteCount(String path) {
+ Response response = tckSuiteTarget.path(path)
+ .path(ValidLRAParticipant.COMPLETED_COUNT_PATH).request().get();
+ return response.readEntity(Integer.class);
+ }
+
+ private int getCompensateCount(String path) {
+ Response response = tckSuiteTarget.path(path)
+ .path(ValidLRAParticipant.COMPENSATED_COUNT_PATH).request().get();
+ return response.readEntity(Integer.class);
+ }
+
+ private int getStatusCount(String path) {
+ Response response = tckSuiteTarget.path(path)
+ .path(ValidLRAParticipant.STATUS_COUNT_PATH).request().get();
+ return response.readEntity(Integer.class);
+ }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
index 8195a290..00c061b1 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
@@ -20,6 +20,8 @@
package org.eclipse.microprofile.lra.tck;
import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+import org.eclipse.microprofile.lra.tck.participant.activity.Activity;
+import org.eclipse.microprofile.lra.tck.participant.api.LraController;
import org.eclipse.microprofile.lra.tck.participant.api.Util;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
@@ -64,7 +66,9 @@ public class TckTestBase {
static WebArchive deploy(String archiveName) {
return ShrinkWrap
.create(WebArchive.class, archiveName + ".war")
- .addPackages(true, "org.eclipse.microprofile.lra.tck")
+ .addPackages(false, TckTestBase.class.getPackage(),
+ Activity.class.getPackage(),
+ LraController.class.getPackage())
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidArgumentTypesParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidArgumentTypesParticipant.java
new file mode 100644
index 00000000..af01f485
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidArgumentTypesParticipant.java
@@ -0,0 +1,56 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.eclipse.microprofile.lra.tck.participant.nonjaxrs;
+
+import org.eclipse.microprofile.lra.annotation.Compensate;
+import org.eclipse.microprofile.lra.annotation.Forget;
+import org.eclipse.microprofile.lra.annotation.ParticipantStatus;
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * TCK invalid LRA participant containing invalid type of arguments in the participant method signature
+ * used for verification of deployment time invalid signature detection and error report in
+ * {@link org.eclipse.microprofile.lra.tck.TckInvalidParticipantSignaturesTests}
+ */
+@Path("nonjaxrs-argument-type-nonjaxrs")
+public class InvalidArgumentTypesParticipant {
+
+ @GET
+ @Path("enlist")
+ @LRA(LRA.Type.REQUIRED)
+ public Response doInLRA() {
+ return Response.ok().build();
+ }
+
+ @Compensate
+ public ParticipantStatus compensate(URI lraId) {
+ return ParticipantStatus.Compensated;
+ }
+
+ @Forget
+ public void forget(int lraId, URI parentId) {
+ // intentionally empty
+ }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidReturnTypeParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidReturnTypeParticipant.java
new file mode 100644
index 00000000..dc1608d7
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidReturnTypeParticipant.java
@@ -0,0 +1,48 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.eclipse.microprofile.lra.tck.participant.nonjaxrs;
+
+import org.eclipse.microprofile.lra.annotation.Compensate;
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+/**
+ * TCK invalid LRA participant containing invalid return type in the participant method signature
+ * used for verification of deployment time invalid signature detection and error report in
+ * {@link org.eclipse.microprofile.lra.tck.TckInvalidParticipantSignaturesTests}
+ */
+@Path("nonjaxrs-return-type")
+public class InvalidReturnTypeParticipant {
+
+ @GET
+ @Path("enlist")
+ @LRA(LRA.Type.REQUIRED)
+ public Response doInLRA() {
+ return Response.ok().build();
+ }
+
+ @Compensate
+ public String compensate() {
+ return "compensated";
+ }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/TooManyArgsParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/TooManyArgsParticipant.java
new file mode 100644
index 00000000..9759d743
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/TooManyArgsParticipant.java
@@ -0,0 +1,56 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.eclipse.microprofile.lra.tck.participant.nonjaxrs;
+
+import org.eclipse.microprofile.lra.annotation.Compensate;
+import org.eclipse.microprofile.lra.annotation.Complete;
+import org.eclipse.microprofile.lra.annotation.ParticipantStatus;
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * TCK invalid LRA participant containing too many arguments in the participant method signature
+ * used for verification of deployment time invalid signature detection and error report in
+ * {@link org.eclipse.microprofile.lra.tck.TckInvalidParticipantSignaturesTests}
+ */
+@Path("too-many-args-nonjaxrs")
+public class TooManyArgsParticipant {
+
+ @GET
+ @Path("enlist")
+ @LRA(LRA.Type.REQUIRED)
+ public Response doInLRA() {
+ return Response.ok().build();
+ }
+
+ @Compensate
+ public ParticipantStatus compensate(URI lraId) {
+ return ParticipantStatus.Compensated;
+ }
+
+ @Complete
+ public ParticipantStatus complete(URI lraId, URI parentId, Object additional) {
+ return ParticipantStatus.Completed;
+ }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/valid/ValidLRACSParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/valid/ValidLRACSParticipant.java
new file mode 100644
index 00000000..3b5209d5
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/valid/ValidLRACSParticipant.java
@@ -0,0 +1,160 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.eclipse.microprofile.lra.tck.participant.nonjaxrs.valid;
+
+import org.eclipse.microprofile.lra.annotation.Compensate;
+import org.eclipse.microprofile.lra.annotation.Complete;
+import org.eclipse.microprofile.lra.annotation.ParticipantStatus;
+import org.eclipse.microprofile.lra.annotation.Status;
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.ACCEPT_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.RECOVERY_PARAM;
+import static org.eclipse.microprofile.lra.tck.participant.nonjaxrs.valid.ValidLRAParticipant.COMPENSATED_COUNT_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.nonjaxrs.valid.ValidLRAParticipant.COMPLETED_COUNT_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.nonjaxrs.valid.ValidLRAParticipant.STATUS_COUNT_PATH;
+
+/**
+ * Valid participant resource containing async non-JAX-RS participant methods with
+ * {@link CompletionStage} return types
+ */
+@ApplicationScoped
+@Path(ValidLRACSParticipant.ROOT_PATH)
+public class ValidLRACSParticipant {
+
+ public static final String ROOT_PATH = "valid-cs-participant1";
+ public static final String ENLIST_WITH_COMPLETE = "enlist-complete";
+ public static final String ENLIST_WITH_COMPENSATE = "enlist-compensate";
+
+ private final AtomicInteger completedCount = new AtomicInteger(0);
+ private final AtomicInteger compensatedCount = new AtomicInteger(0);
+ private final AtomicInteger statusCount = new AtomicInteger(0);
+
+ private int recoveryPasses;
+
+ @GET
+ @Path(ENLIST_WITH_COMPLETE)
+ @LRA(value = LRA.Type.REQUIRED)
+ public Response enlistWithComplete(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) String lraId) {
+ return Response.ok().build();
+ }
+
+ @GET
+ @Path(ENLIST_WITH_COMPENSATE)
+ @LRA(value = LRA.Type.REQUIRED, cancelOn = Response.Status.INTERNAL_SERVER_ERROR)
+ public Response enlistWithCompensate(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) String lraId) {
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ @Compensate
+ public CompletionStage compensate(URI lraId) {
+ assert lraId != null;
+
+ return CompletableFuture.runAsync(() -> {
+ compensatedCount.incrementAndGet();
+
+ simulateLongRunningCompensation();
+ });
+ }
+
+ @Complete
+ public CompletionStage complete(URI lraId) {
+ assert lraId != null;
+
+ return CompletableFuture.supplyAsync(() -> {
+ completedCount.incrementAndGet();
+
+ simulateLongRunningCompensation();
+ return Response.accepted().build(); // Completing
+ });
+ }
+
+ @Status
+ public CompletionStage status(URI lraId) {
+ assert lraId != null;
+
+ return CompletableFuture.supplyAsync(() -> {
+ statusCount.incrementAndGet();
+
+ simulateLongRunningCompensation();
+ return ParticipantStatus.Completed;
+ });
+ }
+
+ private void simulateLongRunningCompensation() {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @GET
+ @Path(COMPLETED_COUNT_PATH)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response completed() {
+ return Response.ok(completedCount.toString()).build();
+ }
+
+ @GET
+ @Path(COMPENSATED_COUNT_PATH)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response compensated() {
+ return Response.ok(compensatedCount.toString()).build();
+ }
+
+ @GET
+ @Path(STATUS_COUNT_PATH)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response statusCount() {
+ return Response.ok(statusCount.toString()).build();
+ }
+
+ @PUT
+ @Path(ACCEPT_PATH)
+ @LRA(value = LRA.Type.REQUIRES_NEW)
+ public Response acceptLRA(@QueryParam(RECOVERY_PARAM) @DefaultValue("0") Integer recoveryPasses) {
+ this.recoveryPasses = recoveryPasses;
+
+ return Response.ok().build();
+ }
+
+ @GET
+ @Path(ACCEPT_PATH)
+ public Response getAcceptLRA() {
+ return Response.ok(this.recoveryPasses).build();
+ }
+
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/valid/ValidLRAParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/valid/ValidLRAParticipant.java
new file mode 100644
index 00000000..fd3a1f8c
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/valid/ValidLRAParticipant.java
@@ -0,0 +1,173 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2019 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.eclipse.microprofile.lra.tck.participant.nonjaxrs.valid;
+
+import org.eclipse.microprofile.lra.annotation.Compensate;
+import org.eclipse.microprofile.lra.annotation.Complete;
+import org.eclipse.microprofile.lra.annotation.Forget;
+import org.eclipse.microprofile.lra.annotation.ParticipantStatus;
+import org.eclipse.microprofile.lra.annotation.Status;
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA.Type;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.ACCEPT_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.RECOVERY_PARAM;
+
+/**
+ * TCK valid LRA participant containing a combination of valid participant method signatures
+ */
+@ApplicationScoped
+@Path(ValidLRAParticipant.RESOURCE_PATH)
+public class ValidLRAParticipant {
+ private static final Logger LOGGER = Logger.getLogger(ValidLRAParticipant.class.getName());
+
+ public static final String RESOURCE_PATH = "valid-nonjaxrs";
+ public static final String COMPLETED_COUNT_PATH = "completed";
+ public static final String COMPENSATED_COUNT_PATH = "compensated";
+ public static final String STATUS_COUNT_PATH = "status";
+ public static final String FORGET_COUNT_PATH = "forget";
+
+ private final AtomicInteger completedCount = new AtomicInteger(0);
+ private final AtomicInteger compensatedCount = new AtomicInteger(0);
+ private final AtomicInteger statusCount = new AtomicInteger(0);
+ private final AtomicInteger forgetCount = new AtomicInteger(0);
+
+ public static final String ENLIST_WITH_COMPLETE = "nonjaxrs-enlist-complete";
+ private int recoveryPasses;
+
+ @GET
+ @Path(ENLIST_WITH_COMPLETE)
+ @LRA(value = Type.REQUIRED)
+ public Response enlistWithComplete() {
+ return Response.ok().build();
+ }
+
+ public static final String ENLIST_WITH_COMPENSATE = "nonjaxrs-enlist-compensate";
+
+ @GET
+ @Path(ENLIST_WITH_COMPENSATE)
+ @LRA(value = Type.REQUIRED, cancelOn = Response.Status.INTERNAL_SERVER_ERROR)
+ public Response enlistWithCompensate() {
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+
+ @Complete
+ public void completeWithException(URI lraId, URI parentId) {
+ verifyLRAId(lraId);
+
+ completedCount.incrementAndGet();
+
+ LOGGER.fine(String.format("LRA id '%s' was completed", lraId));
+ throw new WebApplicationException(Response.ok().build());
+ }
+
+ @Compensate
+ public ParticipantStatus compensate(URI lraId) {
+ verifyLRAId(lraId);
+
+ compensatedCount.incrementAndGet();
+
+ LOGGER.fine(String.format("LRA id '%s' was compensated", lraId));
+ return ParticipantStatus.Compensating;
+ }
+
+ @Status
+ public Response status(URI lraId) {
+ verifyLRAId(lraId);
+
+ statusCount.incrementAndGet();
+
+ LOGGER.fine(String.format("LRA id '%s' status called, return FailedToCompensate to get @Forget called", lraId));
+ return Response.ok(ParticipantStatus.FailedToCompensate).build();
+ }
+
+ @Forget
+ public void forget(URI lraId) {
+ verifyLRAId(lraId);
+
+ forgetCount.incrementAndGet();
+
+ LOGGER.fine(String.format("LRA id '%s' forget called", lraId));
+ }
+
+ private void verifyLRAId(URI lraId) {
+ if (lraId == null) {
+ throw new NullPointerException("lraId cannot be null");
+ }
+ }
+
+ @GET
+ @Path(COMPLETED_COUNT_PATH)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response completed() {
+ return Response.ok(completedCount.toString()).build();
+ }
+
+ @GET
+ @Path(COMPENSATED_COUNT_PATH)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response compensated() {
+ return Response.ok(compensatedCount.toString()).build();
+ }
+
+ @GET
+ @Path(STATUS_COUNT_PATH)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response statusCount() {
+ return Response.ok(statusCount.toString()).build();
+ }
+
+ @GET
+ @Path(FORGET_COUNT_PATH)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response forgetCount() {
+ return Response.ok(forgetCount.toString()).build();
+ }
+
+ @PUT
+ @Path(ACCEPT_PATH)
+ @LRA(value = LRA.Type.REQUIRES_NEW)
+ public Response acceptLRA(@QueryParam(RECOVERY_PARAM) @DefaultValue("0") Integer recoveryPasses) {
+ this.recoveryPasses = recoveryPasses;
+
+ return Response.ok().build();
+ }
+
+ @GET
+ @Path(ACCEPT_PATH)
+ public Response getAcceptLRA() {
+ return Response.ok(this.recoveryPasses).build();
+ }
+}