Repository: eclipse/microprofile-lra
Pull request #115 (2019-03-17 13:17:30)

issue95 remove dependencies on LRAClient
#95 and #107 
- the SHA before these commits is fc15945
- spec updates which remove:
  * references to LRAClient and issue #15 (provision of opaque data during participant enlistment)
- deprecated the LRAClient API
- removed LRAClient API references from the asciidoc and javadocs
- removed ManagementSPI from the TCK:
  * this interface was for listing LRA's and querying the status of an LRA
  * I updated the TCK to use an alternative (see the next bullet for clarification)
- introduced two TCK resources to replace what was in LRAClient, they are used by a new class called LRAClientOps (I made them separate from the existing TCK resource so that is clear how we replaced the TCK dependence on LRAClient):
  * NonParticipatingTckResource
  * ParticipatingTckResource
- removed LRAInfo
- moved the HTTP header name for the LRA context into the @LRA annotation (since that is what determines the /creation/presence/destruction of a context)
  * ie LRA_HTTP_HEADER = "Long-Running-Action";
  * I did the same for the header that is created as a result of enlisting with a LRA 
    (ie LRA_HTTP_RECOVERY_HEADER = "Long-Running-Action-Recovery";)
- the test that allows participants to return 202 Accepted rely on a "recovery scan" and I moved
  the property names that identify a recovery endpoint into LRAClientOps (LRA_RECOVERY_HOST, ..._PORT and ..._PATH). This will still need to be addressed before release.
- added @Ignore to two TCK tests:
  * TckTests#mixedMultiLevelNestedActivity (needs an issue raising)
  * TckCancelOnTests#cancelFromRemoteCall (assert needs to be 1)
- removed some tests from TckTests:
  * TckTest#getActiveLRAs - tested LRAClient API but there are equivalents that use annotations
  * TckTest#getAllLRAs - tested LRAClient API but there are equivalents that use annotations
  * TckTest#getRecoveringLRAs - tested LRAClient API but there are equivalents that use annotations
  * TckTest#isActiveLRA - tested LRAClient API but there are equivalents that use annotations
  * TckTest#isCompensatedLRA - tested LRAClient API but there are equivalents that use annotations
  * TckTest#isCompletedLRA - tested LRAClient API but there are equivalents that use annotations
  * TckTest#joinLRAViaBody - an earlier version of the spec allowed participants to join by putting there endpoints in the body of a REST request and is no longer supported


rdebusscher: SimpleLRAParticipant source code in spec still contains the @PUT and @Path on the @Compensate and @Complete annotated methods. They should be removed.
mmusgrov: The following review comments were mentioned in today's LRA hangout and I will push a commit to resolve them in the next hour:

- Grammatical and spelling  errors
- Remove unused methods from LRAClientOps

@rdebusscher the @put and @path annotations should be removed when the PR for #35 is raised

Patch:
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java
index c814c3b..588b7bb 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java
@@ -24,7 +24,6 @@ import javax.ws.rs.HeaderParam;
 import javax.ws.rs.core.Response;
 
 import org.eclipse.microprofile.lra.annotation.Compensate;
-import org.eclipse.microprofile.lra.client.LRAClient;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Inherited;
@@ -54,7 +53,7 @@ import java.time.temporal.ChronoUnit;
  * <p>
  * Newly created LRAs are uniquely identified and the id is referred to as the
  * LRA context. The context is passed around using a JAX-RS request/response header
- * called {@link org.eclipse.microprofile.lra.client.LRAClient#LRA_HTTP_HEADER}.
+ * called {@value #LRA_HTTP_HEADER}.
  * The implementation (of the LRA specification) is expected to manage this context
  * and the application developer is expected to declaratively control the creation,
  * propagation and destruction of LRAs using the {@link LRA} annotation. When a JAX-RS bean
@@ -71,7 +70,7 @@ import java.time.temporal.ChronoUnit;
  * and then performs a JAX-RS request to resource <code>B</code> which does not contain any LRA
  * annotations. If resource <code>B</code> then performs a JAX-RS request to a third service, <code>C</code> say,
  * which does contain LRA annotations then the LRA context started at <code>A</code> must be propagated
- * to <code>C</code> (for example if <code>C</code> uses {@link LRAClient} or annotations to join the LRA,
+ * to <code>C</code> (for example if <code>C</code> uses an annotation to join the LRA,
  * then <code>C</code> must be enlisted in the LRA that was started at <code>A</code>).
  * </p>
  *
@@ -85,6 +84,18 @@ import java.time.temporal.ChronoUnit;
 @Retention(value = RetentionPolicy.RUNTIME)
 @Target({ElementType.TYPE, ElementType.METHOD})
 public @interface LRA {
+    /**
+     * When a JAX-RS invocation is made with an active LRA it is made available
+     * via an HTTP header field with the following name. The value contains
+     * the LRA id associated with the HTTP request/response
+     */
+    String LRA_HTTP_HEADER = "Long-Running-Action";
+
+    /**
+     * the name of the HTTP header field that contains a recovery URI corresponding
+     * to a participant enlistment in an LRA
+     */
+    String LRA_HTTP_RECOVERY_HEADER = "Long-Running-Action-Recovery";
 
     /**
      * <p>
@@ -116,7 +127,7 @@ public @interface LRA {
      * <p>
      * When an LRA is present its identifer <b>MUST</b> be made available to
      * the business logic in the JAX-RS request and response header
-     * {@link org.eclipse.microprofile.lra.client.LRAClient#LRA_HTTP_HEADER}
+     * {@value #LRA_HTTP_HEADER}.
      *
      * @return whether a bean method is to be executed within a transaction context.
      */
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/client/LRAClient.java b/api/src/main/java/org/eclipse/microprofile/lra/client/LRAClient.java
deleted file mode 100644
index 2152ea1..0000000
--- a/api/src/main/java/org/eclipse/microprofile/lra/client/LRAClient.java
+++ /dev/null
@@ -1,268 +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.client;
-
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
-import org.eclipse.microprofile.lra.annotation.Compensate;
-
-import javax.ws.rs.NotFoundException;
-import java.net.URI;
-import java.time.temporal.ChronoUnit;
-
-public interface LRAClient {
-    /**
-     * the name of the HTTP header field that contains the LRA id associated with
-     * a request/response
-     */
-    String LRA_HTTP_HEADER = "Long-Running-Action";
-
-    /**
-     * the name of the HTTP header field that contains a recovery URI corresponding
-     * to a participant
-     * enlistment in an LRA {@link LRAClient#joinLRA(URI, Class, URI, String)}
-     */
-    String LRA_HTTP_RECOVERY_HEADER = "Long-Running-Action-Recovery";
-
-    /**
-     * Key for looking up the config property that specifies which host a
-     * coordinator is running on
-     */
-    String LRA_COORDINATOR_HOST_KEY = "lra.http.host";
-
-    /**
-     * Key for looking up the config property that specifies which port a
-     * coordinator is running on
-     */
-    String LRA_COORDINATOR_PORT_KEY = "lra.http.port";
-
-    /**
-     * Key for looking up the config property that specifies which JAX-RS path a
-     * coordinator is running on
-     */
-    String LRA_COORDINATOR_PATH_KEY = "lra.coordinator.path";
-
-    /**
-     * Key for looking up the config property that specifies which JAX-RS path a
-     * recovery coordinator is running on
-     */
-    String LRA_RECOVERY_HOST_KEY = "lra.http.recovery.host";
-
-    /**
-     * Key for looking up the config property that specifies which JAX-RS path a
-     * recovery coordinator is running on
-     */
-    String LRA_RECOVERY_PORT_KEY = "lra.http.recovery.port";
-
-    /**
-     * Key for looking up the config property that specifies which JAX-RS path a
-     * recovery coordinator is running on
-     */
-    String LRA_RECOVERY_PATH_KEY = "lra.coordinator.recovery.path";
-
-    /**
-     * Explicitly dispose of all resources. After this call the instance may no
-     * longer be useable
-     */
-    void close();
-
-    /**
-     * Start a new LRA
-     *
-     * If the LRA was started during a JAX-RS resource method invocation then the
-     * id of the new LRA will be made available as the JAX-RS response header
-     * {@link LRAClient#LRA_HTTP_HEADER}
-     *
-     * @param parentLRA The parent of the LRA that is about to start. If null then
-     *                 the new LRA will be top level
-     * @param clientID The client may provide a (preferably) unique identity which
-     *                will be reported back when the LRA is queried.
-     * @param timeout Specifies the maximum time that the LRA will exist for. If the
-     *               LRA is terminated because of a timeout it will be cancelled.
-     * @param unit Specifies the unit that the timeout is measured in
-     *
-     * @throws NotFoundException if the parent LRA is known to no longer exist
-     *
-     * @throws GenericLRAException a new LRA could not be started. The specific
-     * reason is available in {@link GenericLRAException#getStatusCode()}
-     *
-     * @return the identifier of the new LRA
-     */
-    URI startLRA(URI parentLRA, String clientID, Long timeout, ChronoUnit unit)
-            throws GenericLRAException;
-
-    /**
-     * Start a top level LRA (ie similar to
-     * {@link LRAClient#startLRA(URI, String, Long, ChronoUnit)}
-     *
-     * @param clientID The client may provide a (preferably) unique identity which
-     *                will be reported back when the LRA is queried.
-     * @param timeout Specifies the maximum time that the LRA will exist for. If the
-     *               LRA is terminated because of a timeout it will be cancelled.
-     * @param unit Specifies the unit that the timeout is measured in
-     *
-     * @throws NotFoundException if the parent LRA is known to no longer exist
-     *
-     * @throws GenericLRAException a new LRA could not be started. The specific
-     * reason is available in {@link GenericLRAException#getStatusCode()}
-     *
-     * @return the identifier of the new LRA
-     */
-    URI startLRA(String clientID, Long timeout, ChronoUnit unit)
-            throws GenericLRAException;
-
-    /**
-     * Attempt to cancel an LRA
-     *
-     * Trigger compensation of all participants enlisted with the LRA (ie the
-     * compensate message will be sent to each participant).
-     *
-     * @param lraId The unique identifier of the LRA (required)
-     *
-     * @throws NotFoundException if the LRA no longer exists
-     *
-     * @throws GenericLRAException Communication error (the reason is availalbe via
-     * the {@link GenericLRAException#getStatusCode()} method
-     *
-     * @return the response MAY contain the final status of the LRA as reported by
-     *         {@link LRAStatus#name()}.
-     *         If the final status is not returned the
-     *         client can only discover the final state using proprietary APIs
-     */
-    String cancelLRA(URI lraId) throws GenericLRAException;
-
-    /**
-     * Attempt to close an LRA
-     *
-     * Tells the LRA to close normally. All participants will be triggered by the
-     * coordinator (ie the complete message will be sent to each participant).
-     *
-     * @param lraId The unique identifier of the LRA (required)
-     *
-     * @throws NotFoundException if the LRA no longer exists
-     *
-     * @throws GenericLRAException Communication error (the reason is availalbe via
-     * the {@link GenericLRAException#getStatusCode()} method
-     *
-     * @return the response MAY contain the final status of the LRA as reported by
-     *         {@link LRAStatus#name()}
-     *         If the final status is not returned the
-     *         client can only discover the final state using proprietary APIs
-     */
-    String closeLRA(URI lraId) throws GenericLRAException;
-
-    /**
-     * Join an LRA passing in a class that will act as the participant.
-     *
-     * @param lraId The unique identifier of the LRA (required)
-     * @param resourceClass An annotated class for the participant methods:
-     * {@link Compensate}, etc.
-     * @param baseUri Base uri for the participant endpoints
-     * @param compensatorData Compensator specific data that the coordinator will
-     *                       pass to the participant when the LRA is closed or
-     *                       cancelled
-     *
-     * @return a recovery URI for this enlistment
-     *
-     * @throws NotFoundException if the LRA no longer exists
-     *
-     * @throws GenericLRAException if the request to the coordinator failed.
-     * {@link GenericLRAException#getCause()} and/or
-     * {@link GenericLRAException#getStatusCode()} may provide a more specific reason.
-     */
-    URI joinLRA(URI lraId, Class<?> resourceClass, URI baseUri, String compensatorData)
-            throws GenericLRAException;
-
-    /**
-     * Change the endpoints that a participant can be contacted on.
-     *
-     * @param recoveryUri the recovery URI returned from a participant join request
-     * @param resourceClass An annotated class for the participant methods:
-     *      * {@link Compensate}, etc.
-     * @param baseUri Base uri for the participant endpoints
-     * @param compensatorData opaque data that returned to the participant when the
-     *                       LRA is closed or cancelled
-     *
-     * @return an updated recovery URI for this participant
-     *
-     * @throws NotFoundException if the LRA no longer exists
-     *
-     * @throws GenericLRAException if the request to the coordinator failed.
-     * {@link GenericLRAException#getCause()} and/or
-     * {@link GenericLRAException#getStatusCode()} may provide a more specific reason.
-     */
-    URI updateCompensator(URI recoveryUri, Class<?> resourceClass, URI baseUri, String compensatorData)
-            throws GenericLRAException;
-
-    /**
-     * A Compensator can resign from the LRA at any time prior to the completion
-     * of an activity
-     *
-     * @param recoveryUri the recovery URI returned from a participant join request
-     *
-     * @throws NotFoundException if the LRA no longer exists
-     *
-     * @throws GenericLRAException if the request to the coordinator failed.
-     * {@link GenericLRAException#getCause()} and/or
-     * {@link GenericLRAException#getStatusCode()} may provide a more specific reason.
-     */
-    void leaveLRA(URI recoveryUri) throws GenericLRAException;
-
-    /**
-     * LRAs can be created with timeouts after which they are cancelled. Use this
-     * method to update the timeout.
-     *
-     * @throws NotFoundException if the LRA no longer exists
-     *
-     * @param lraId the id of the lra to update
-     * @param limit the new timeout period
-     * @param unit the time unit for limit
-     */
-    void renewTimeLimit(URI lraId, long limit, ChronoUnit unit);
-
-    /**
-     * checks whether there is an LRA associated with the calling thread
-     * (this method provides an alternative to relying on the presence
-     * of the {@link LRAClient#LRA_HTTP_HEADER} HTTP header fields since
-     * not all users of the API are being called in the context of JAX-RS
-     * resource requests).
-     *
-     * @return the current LRA (can be null)
-     */
-    URI getCurrent();
-
-    /**
-     * Lookup the status of an LRA
-     *
-     * @param lraId the LRA whose status is being requested
-     *
-     * @return the status of the the LRA.
-     *
-     * @throws NotFoundException if the LRA no longer exists
-     *
-     * @throws UnknowableException if the status is unknowable
-     *
-     * @throws GenericLRAException if the request to the coordinator failed.
-     * {@link GenericLRAException#getCause()} and/or
-     * {@link GenericLRAException#getStatusCode()}
-     * may provide a more specific reason.
-     */
-    LRAStatus getStatus(URI lraId) throws GenericLRAException;
-}
diff --git a/spec/src/main/asciidoc/microprofile-lra-spec.adoc b/spec/src/main/asciidoc/microprofile-lra-spec.adoc
index a57bd23..a73ee0e 100644
--- a/spec/src/main/asciidoc/microprofile-lra-spec.adoc
+++ b/spec/src/main/asciidoc/microprofile-lra-spec.adoc
@@ -210,9 +210,6 @@ enter the state model until it receives a complete or compensate message:
 .Participant State Model
 image::participant-state-model.png[align="center" link="participant-state-model"]
 
-The participant can be enlisted only once per LRA instance. When participant
-is about to be enlisted multiple times (e.g. client calls the same `@LRA` method
-several times) the follow-up enlistments of such participant MUST be ignored.
 
 Different usage patterns for LRAs are possible, for example LRAs may be
 used sequentially and/or concurrently, where the termination of one LRA
@@ -237,13 +234,9 @@ for writing participants:
 
 1.  <<java-annotations-for-lras,Java Annotations for LRAs>> for controlling
 the life cycle and membership of LRAs
-2.  <<lra-client-api,Client API>> for those occasions where the application
-wishes to make <<use-cases, runtime dynamic decisions>> about how to control LRAs
-3.  <<java-based-lra-participant-registration-api,Java based LRA participant registration API>>
+2.  <<java-based-lra-participant-registration-api,Java based LRA participant registration API>>
 to support services that do not use JAX-RS
 
-The annotatons API and the client API are meant to be semantically equivalent.
-
 [[java-annotations-for-lras]]
 === Java Annotations for LRAs
 
@@ -299,14 +292,14 @@ It can report the status directly from the `@Compensate` or `@Complete` methods
 are idempotent, otherwise it MUST provide a method annotated with `@Status`.
 
 If the participant is no longer associated with the LRA (because it has successfully
-compensated or compeleted or because it has left the LRA) it MAY return the
+compensated or completed or because it has left the LRA) it MAY return the
 `404 Not Found` HTTP status code from any of these methods.
 
 If the participant knows it will never be able to compensate or complete then it MUST remember
 that it could not 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).
 
-If multiple methods are annotated with the same annnotation an arbitrary one will be chosen.
+If multiple methods are annotated with the same annotation an arbitrary one will be chosen.
 
 [[java-annotations]]
 ==== Starting and Ending LRAs
@@ -330,18 +323,18 @@ also contains methods annotated with `@Compensate` and `@Complete` then the bean
 (aka participant or compensator) will be enlisted with the LRA. Enlisting with
 an LRA means that the bean will be notified when the current LRA is
 subsequently cancelled or closed. In addition the implementation should generate a
-URI for this participant that can be used with the <<lra-client-api,Client API>>
-for recovery purposes and should make it available to the participant via a JAX-RS
+URI for this participant that can be used
+for recovery purposes and it should make it available to the participant via a JAX-RS
 header param with name defined by the Java constant `LRA_HTTP_RECOVERY_HEADER` as defined
-<<source-LRAClient,in the Client API>>
-(https://github.com/eclipse/microprofile-lra/blob/master/api/src/main/java/org/eclipse/microprofile/lra/client/LRAClient.java[github link]).
+<<source-LRA, in the LRA annotation>>
+(https://github.com/eclipse/microprofile-lra/tree/master/api/src/main/java/org/eclipse/microprofile/lra/annotation/LRA.java[github link]).
 Enlisting in an LRA is explained in more detail
 <<compensating-activities,in a later section>> of this document.
 
 A method which executes with an LRA context
 can obtain the context by injecting a JAX-RS header param with the name
 specified by the Java constant `LRA_HTTP_HEADER` as defined
-<<source-LRAClient, in the Client API>>.
+<<source-LRA, in the LRA annotation>>
 
 It is the `LRA.Type` element that provides fine grained control over the
 LRA for the duration of the execution of the annotated method:
@@ -419,8 +412,10 @@ which when returned as the
 https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/core/Response.html#status-int-[Response status code]
 cause the LRA to be cancelled.
 
-Bear in mind that the LRA is expected to be used at JAX-RS
+Bear in mind that the LRA is expected to be used with JAX-RS
 which expects the `Response` being the method return type.
+If a `200 OK` response is to be returned then the method can
+return any data type that it desires.
 The specification does not discuss what happens when
 an uncaught exception is thrown from the `@LRA` annotated method.
 It's up to the framework to transform the thrown exception
@@ -541,8 +536,7 @@ the method finishes and the `end = true` attribute on the confirmTrip method for
 ==== 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`
-footnote:[issue #35 will remove the JAX-RS dependency for this annotation] annotations.
+activity by annotating it with both the `@Compensate` and the JAX-RS `@PUT` annotations.
 Whenever the associated resource is invoked in the context of
 an LRA the endpoint corresponding to the `@Compensate` method MUST be enlisted with
 the LRA. If the LRA is subsequently cancelled then the compensation method MUST be invoked.
@@ -553,7 +547,7 @@ For example:
   @PUT
   @Path("/compensate")
   @Compensate
-  public Response compensateWork(@HeaderParam(LRAClient.LRA_HTTP_HEADER) String lraId)
+  public Response compensateWork(@HeaderParam(LRA.LRA_HTTP_HEADER) String lraId)
   {
     // compensate for whatever activity the business logic has associated with lraId
     return Response.ok(ParticipantStatus.Compensated.name()).build();
@@ -576,7 +570,7 @@ participant state model>>. If the compensation and completion methods are not
 idempotent then there MUST be a method annotated with `@Status` which reports the
 status. Otherwise the compensation and completion methods should return the status.
 If the participant is no longer associated with the LRA (because it has successfully
-compensated/compeleted) it MAY return the `404 Not Found` HTTP status code from any
+compensated/completed) it MAY return the `404 Not Found` HTTP status code from any
 of these methods. If it knows it will never be able to compensate or complete then
 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
@@ -594,15 +588,13 @@ immediately the `@Compensate` method SHOULD do one or more of the following:
 - mark the method as a JAX-RS asynchronous method (using the
   `javax.ws.rs.container.Suspended` annotation).
 
-In each case it MUST provide an `@Status` method that reports the progress of
-the compensation. The only case where the `@Status` method is not mandatory is
-if the compensation activity cannot fail.
+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
+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
@@ -644,9 +636,7 @@ section for when this feature could be useful.
     }
 ----
 
-Note that there is no annotation to directly cancel a nested LRA
-and the <<lra-client-api,Java LRAClient api>> must be
-used for this purpose if required.
+Note that the mechanics of cancelling nested and top level LRAs is the same.
 
 [[timing-out-lras-and-compensators]]
 ==== Timing out LRAs and Participants
@@ -661,15 +651,15 @@ example, to indicate that an LRA should automatically cancel after 100 milliseco
   @GET
   @Path("/doitASAP")
   @Produces(MediaType.APPLICATION_JSON)
-  @LRA(value = LRA.Type.REQUIRED, timeLimit = 100, timeUnit = ChronoUnit.MILLI)
+  @LRA(value = LRA.Type.REQUIRED, timeLimit = 100, timeUnit = ChronoUnit.MILLIS)
   public Response theClockIsTicking(
-      @HeaderParam(LRAClient.LRA_HTTP_HEADER) String lraId) {...}
+      @HeaderParam(LRA.LRA_HTTP_HEADER) String lraId) {...}
 ----
 
 Furthermore, the ability to compensate or complete may be transient capabilities of a
 service so participants can also be timed out. When the time limit is reached
 the LRA is cancelled and participants will be notified via their compensation
-(ie the method annotated with `@Compensate`). To set such a time limit add the
+(i.e. the method annotated with `@Compensate`). To set such a time limit add the
 `timeLimit` and `timeUnit` attributes to the `@Compensate` and `@Complete`
 annotations. If different time limits are set on these two methods then the
 earliest of the two will take effect. An example of how a participant could
@@ -684,8 +674,8 @@ indicate that its' ability to compensate is limited to 100 milliseconds could be
                                  String userData) { ... }
 ----
 
-In this example afer 100 milliseconds has passed the implementation SHOULD
-automatcially cancel the LRA which will result in this method being invoked.
+In this example after 100 milliseconds has passed the implementation SHOULD
+automatically cancel the LRA which will result in this method being invoked.
 
 [[leaving-an-lra]]
 ==== Leaving an LRA
@@ -694,11 +684,10 @@ If a resource method annotated with `@Leave` is invoked in the context of
 a LRA and if the bean class has registered a participant with the active LRA
 it will be removed from the LRA just before the bean method is entered
 (and will not be asked to complete or compensate when the LRA is subsequently
-ended). Even though the method runs without an LRA context the implemention
-MUST make the context available either via an JAX-RS header param or via the
-<<lra-client-api,Java LRAClient api>>.
+ended). Even though the method runs without an LRA context the implementation
+MUST still make the context available via a JAX-RS header.
 
-An example of this annotion is shown next:
+An example of this annotation is shown next:
 
 [source,java]
 ----
@@ -716,8 +705,8 @@ An example of this annotion is shown next:
 This specification supports distributed communications amongst services and
 due to the unreliable nature of networks messages/requests can be lost, delayed,
 duplicated etc and the implementation component responsible for invoking
-partipant completion/compensation logic may loose track of the status of
-a particpant. In this case, ideally it would just resend the
+participant completion/compensation logic may loose track of the status of
+a participant. In this case, ideally it would just resend the
 completion or compensation notification but if the participant does not
 support idempotency then it MUST be able to report its' status by
 annotating one of the methods with the `@Status` annotation which should
@@ -741,8 +730,8 @@ maps to a `412 Precondition Failed` HTTP status code (such as
 ==== Forgetting an LRA
 
 If a participant is unable to complete or compensate immediately
-(ie it has indicated that the request has been accepted and is
-in progress) or because of a failure (ie will never be able to finish)
+(i.e. it has indicated that the request has been accepted and is
+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
@@ -758,7 +747,7 @@ with the LRA.
 ==== Recovery Requirements
 
 This LRA specification provides guarantees of Atomicity, Consistency and Durability
-of work which places responsibilities on both spec implementors and application
+of work which places responsibilities on both spec implementers and application
 writers. Failure points include loss of contact with components managing the
 life cycle of LRAs and of participants. Application writers need to know
 how to associate work with an LRA context so that the correct work
@@ -768,19 +757,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.
 
-[[lra-client-api]]
-=== LRA Client API
-
-For completeness the proposal supports clients that wish to directly
-control LRAs and participants or wish to dynamically control LRAs and
-LRA enlistment. To support this class of user an instance of
-<<source-LRAClient,LRA Client API>>
-(https://github.com/eclipse/microprofile-lra/blob/master/api/src/main/java/org/eclipse/microprofile/lra/client/LRAClient.java[github link])
-can be instantiated directly or injected if the client is using CDI. This
-interface provides developers with complete programmatic control of the LRA
-capabilities.  The later section <<use-cases, on use cases>> provides some
-examples of how to inject and use the client API.
-
 [[java-based-lra-participant-registration-api]]
 === Java based LRA participant registration API
 
@@ -804,25 +780,26 @@ shows how to inject instances of the API and how to perform work in the context
 ----
 @Path("/")
 public class UnannotatedParticpant implements LRAParticipant {
-    @Inject
-    private LRAClient lraClient;
-
     @Inject
     private LRAManagement lraManagement;
 
     public void doInTransaction() throws JoinLRAException
     {
         // start a new LRA with an unlimited timeout
-        URI lraId = lraClient.startLRA(
-                        "Unannotated client", 0L, ChronoUnit.MILLIS);
+        // 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 which should cause the completion call back to be invoked
-        lraClient.closeLRA(lraId);
+        // 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
     }
@@ -846,11 +823,11 @@ public class UnannotatedParticpant implements LRAParticipant {
 ----
 
 The framework must guarantee that participants will still be triggered (the LRA protocol still provides the "all or
-nothing" guarantees that traditional transactions give). +
+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 coordinator. +
 This to ensure that after this class has joined an LRA (through the _lraManagement.joinLRA()_ ) the _complete_ or
-_compensate_ methods can be called by the coordinator in case the micro-service crashed before this notification could
+_compensate_ methods can be called by the coordinator in case the microservice crashed before this notification could
 be delivered.
 
 Serializable participants need to know how to contact the original
@@ -861,101 +838,13 @@ an administrative and manageability point of view, it may be simpler to
 use one of the other APIs such as the <<java-annotations-for-lras,Java
 Annotations for LRAs>>.
 
-[[use-cases]]
-=== Use Cases
-
-==== Programmatic API versus CDI annotations
-
-There are scenarios where a business method may wish to make runtime decisions
-about how to interact with an LRA (such as whether or not to start a new one or
-to join in with an existing one, etc).
-Take for example a trip booking service which starts a top level LRA and includes
-the option of adding a flight as part of the overall trip:
-
-[source,java]
-----
-// Trip service code
-@PUT
-@Path("/trip")
-@LRA(LRA.Type.REQUIRED) // ensures an LRA is active when the method executes
-public void bookTrip(@HeaderParam(LRA_HTTP_HEADER) URI lraId) {
-    // include a flight in the trip by sending a JAX-RS PUT
-    // request to the flight booking service
-    ...
-}
-----
-
-The flight service will be on a different endpoint, perhaps in a different microservice:
-
-[source,java]
-----
-// Flight service code
-@PUT
-@Path("/flight")
-@LRA(LRA.Type.REQUIRED)
-void bookFlight(...) {
-    ...
-}
-
-@Compensate
-@PUT
-void cancelFlight(@HeaderParam(LRA_HTTP_HEADER) URI lraId) {...}
-
-----
-
-The bookTrip method starts a new LRA which is propagated to the flight booking service and the
-flight is booked in the context of the same top level LRA. This means that the flight may only
-be cancelled if the whole trip is cancelled.
-
-But now suppose there is a new business requirement to be able to cancel the flight booking
-but to keep the other items in the trip. The requirement could be supported by annotating
-the `bookFlight` method with the `@NestedLRA` annotation since nested LRAs can be cancelled
-independently of the parent LRA.
-
-But what if the trip provider has no control over the flight service provider and may only
-roll out new versions of its' own services. It can still support the feature by
-using CDI to inject an instance of the programmatic API and use that to start a nested
-LRA before sending the flight booking request:
-
-[source,java]
-----
-    @Inject
-    private LRAClient lraClient;
-
-    @PUT
-    @Path("/trip")
-    @LRA(LRA.Type.REQUIRED)
-    public void bookTrip(@HeaderParam(LRA_HTTP_HEADER) URI lraId) {
-        // start a nested LRA with lraId as the parent
-        flightLraId = lraClient.startLRA(lraId, ...)
-
-        // same code as before but now the nested LRA will
-        // be active when the flight service method
-        // runs (as opposed to the top level one
-        ...
-    }
-----
-
-This API method creates a new LRA nested under the one identified by `lraId`.
-When the flight request is sent the flight booking will be made in the context
-of the nested LRA which means that the trip provider can later cancel the booking
-without cancelling the whole trip, thereby allowing the client to choose some
-atlernative transport mode for his trip:
-
-[source,java]
-----
-   lraClient.cancelLRA(flightLraId);
-
-   // now it is possible to offer the customer the choice of alternative transport
-----
-
 [[appendix-1]]
 == Appendix 1: Selected Javadoc API Descriptions
 
 === LRA Annotation
 [[source-LRA]]
 ----
-include::{sourcedir}/org/eclipse/microprofile/lra/annotation/LRA.java[LRA]
+include::{sourcedir}/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java[LRA]
 ----
 <<<
 
@@ -973,13 +862,6 @@ include::{sourcedir}/org/eclipse/microprofile/lra/annotation/ParticipantStatus.j
 ----
 <<<
 
-=== LRAClient
-[[source-LRAClient]]
-----
-include::{sourcedir}/org/eclipse/microprofile/lra/client/LRAClient.java[LRAClient]
-----
-<<<
-
 === LRAManagement
 [[source-LRAManagement]]
 ----
@@ -993,49 +875,3 @@ include::{sourcedir}/org/eclipse/microprofile/lra/participant/LRAManagement.java
 include::{sourcedir}/org/eclipse/microprofile/lra/participant/LRAParticipant.java[LRAParticipant]
 ----
 <<<
-
-[[appendix-2]]
-== Appendix 2
-
-[[typical-recovery-scenarios]]
-=== Typical Recovery Scenarios
-
-Setup:
-
-* Start 2 services and an LRA coordinator
-* Start an LRA and enlist both services
-
-[[scenario-1]]
-==== Scenario 1
-
-* Kill the coordinator before closing the LRA
-* Restart the coordinator which should cause the LRA to be reloaded
-(from a log in persistent storage)
-* Since the LRA is still active recovery should leave it alone
-* Close the LRA
-* Each service should be told to complete (and the log should be
-removed)
-
-[[scenario-2]]
-==== Scenario 2
-
-* Kill one of the services before closing the LRA
-* The LRA close should fail because one of the services is down
-* Periodic recovery should keep retrying to close the LRA (even if you
-restart the coordinator it should still replay the close)
-* Restart the service
-* Periodic recovery should now successfully close the LRA
-
-[[scenario-3]]
-==== Scenario 3
-
-* Crash the second service after the first one has completed (this should
-generate a heuristic)
-* Restart the second service
-* Periodic recovery should replay the complete on the failed participant
-
-[[scenarios-4-5-and-6]]
-==== Scenarios 4, 5 and 6
-
-* And similarly, the same 3 scenarios but cancelling an LRA instead of
-closing it.
diff --git a/tck/running_the_tck.asciidoc b/tck/running_the_tck.asciidoc
index 9cdf42d..3ba3022 100644
--- a/tck/running_the_tck.asciidoc
+++ b/tck/running_the_tck.asciidoc
@@ -39,9 +39,6 @@ The LRA TCK suite can be parametrized by following properties, handled in the su
 * `pom.xml` dependencies are set-up
 * the LRA TCK suite requires MicroProfile Config
 * a default Arquillian container is configured in `arquillian.xml` (tests manually deploy with use of `@ArquillianResource Deployer`)
-* implementation provides one implementation of `org.eclipse.microprofile.lra.client.LRAClient` as it's injected by TCK suite
-* implementation has to provide one implementation of `org.eclipse.microprofile.lra.tck.spi.ManagementSPI`. This is an interface
-  with definition of util methods used by the TCK suite for its run.
 * JAX-RS `ClientBuilder` is used to run REST calls from tests to tested application. Provide the implementation of the
   JAX-RS `ClientBuilder` on the test class path.
 
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java
new file mode 100644
index 0000000..f7105e7
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java
@@ -0,0 +1,169 @@
+/*
+ *******************************************************************************
+ * 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.annotation.ws.rs.LRA;
+import org.eclipse.microprofile.lra.client.GenericLRAException;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.temporal.ChronoUnit;
+
+import static junit.framework.TestCase.fail;
+import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingTckResource.END_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingTckResource.START_BUT_DONT_END_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingTckResource.STATUS_CODE_QUERY_NAME;
+import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingTckResource.TCK_NON_PARTICIPANT_RESOURCE_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.JOIN_WITH_EXISTNG_LRA_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.LEAVE_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.TCK_PARTICIPANT_RESOURCE_PATH;
+
+public class LRAClientOps {
+    /**
+     * TODO recovery needs to be properly speced
+     * It is used in the two tests for participants that return 202:
+     * {@link TckTests#acceptCancelTest()}  and {@link TckTests#acceptCloseTest()}
+     * to connect to an endpoint that will trigger recovery (the alternative would
+     * be to wait)
+     */
+
+    /**
+     * Key for looking up the config property that specifies which host a
+     * recovery coordinator is running on
+     */
+    static final String LRA_RECOVERY_HOST_KEY = "lra.http.recovery.host";
+
+    /**
+     * Key for looking up the config property that specifies which port a
+     * recovery coordinator is listening on
+     */
+    static final String LRA_RECOVERY_PORT_KEY = "lra.http.recovery.port";
+
+    /**
+     * Key for looking up the config property that specifies which JAX-RS path a
+     * recovery coordinator is running on
+     */
+    static final String LRA_RECOVERY_PATH_KEY = "lra.http.recovery.path";
+
+    private final WebTarget target;
+
+    public LRAClientOps(WebTarget target) {
+        this.target = target;
+    }
+
+    // see if it is possible to join with an LRA - if it is possible to do that then the LRA is still active
+    private int tryToEnlistWithAnLRA(String lra) {
+        // call a JAX-RS endpint that should result in the enlistment of a resource into the LRA
+        int status = invokeRestEndpointAndReturnStatus(lra, TCK_PARTICIPANT_RESOURCE_PATH, JOIN_WITH_EXISTNG_LRA_PATH, 200);
+
+        if (status == 200) {
+            // leave the LRA otherwise any tests checking completion/compensation counts would fail
+            try {
+                leaveLRA(new URI(lra), TCK_PARTICIPANT_RESOURCE_PATH, LEAVE_PATH);
+            } catch (URISyntaxException e) {
+                fail("tryToEnlistWithAnLRA: invalid URI " + lra);
+            }
+        }
+
+        return status;
+    }
+
+    boolean isLRAFinished(URI lra) {
+        return isLRAFinished(lra.toASCIIString());
+    }
+
+    boolean isLRAFinished(String lra) {
+        // if the LRA has finished/finishing or does not exist 412 or 404 MUST be be reported
+        int status = tryToEnlistWithAnLRA(lra);
+
+        return status == Response.Status.NOT_FOUND.getStatusCode() || status == Response.Status.PRECONDITION_FAILED.getStatusCode();
+    }
+
+    private Response invokeRestEndpoint(URI lra, String basePath, String path, int coerceResponse) {
+        return invokeRestEndpoint(lra == null ? null : lra.toASCIIString(), basePath, path, coerceResponse);
+    }
+
+    private Response invokeRestEndpoint(String lra, String basePath, String path, int coerceResponse) {
+        WebTarget resourcePath = target.path(basePath).path(path).queryParam(STATUS_CODE_QUERY_NAME, coerceResponse);
+        Invocation.Builder builder = resourcePath.request();
+
+        if (lra != null) {
+            builder.header(LRA.LRA_HTTP_HEADER, lra);
+        }
+
+        return builder.put(Entity.text(""));
+    }
+
+    public String invokeRestEndpointAndReturnLRA(String lra, String basePath, String path, int coerceResponse) {
+        Response response = invokeRestEndpoint(lra, basePath, path, coerceResponse);
+
+        try {
+            return response.readEntity(String.class);
+        } finally {
+            response.close();
+        }
+    }
+
+    private int invokeRestEndpointAndReturnStatus(String lra, String basePath, String path, int coerceResponse) {
+        Response response = invokeRestEndpoint(lra, basePath, path, coerceResponse);
+
+        try {
+            return response.getStatus();
+        } finally {
+            response.close();
+        }
+    }
+
+    private URI toURI(String lra) throws GenericLRAException {
+        try {
+            return new URI(lra);
+        } catch (URISyntaxException e) {
+            throw new GenericLRAException(null, 0, e.getMessage(), e);
+        }
+    }
+
+    public URI startLRA(URI parentLRA, String clientID, Long timeout, ChronoUnit unit)
+            throws GenericLRAException {
+        return toURI(invokeRestEndpoint(parentLRA,
+                TCK_NON_PARTICIPANT_RESOURCE_PATH, START_BUT_DONT_END_PATH, 200)
+                .readEntity(String.class));
+    }
+
+    void cancelLRA(String lra) {
+        invokeRestEndpointAndReturnLRA(lra, TCK_NON_PARTICIPANT_RESOURCE_PATH, END_PATH, 200);
+    }
+
+    void cancelLRA(URI lraId) throws GenericLRAException {
+        invokeRestEndpointAndReturnLRA(lraId.toASCIIString(), TCK_NON_PARTICIPANT_RESOURCE_PATH, END_PATH, 500);
+    }
+
+    void closeLRA(URI lraId) throws GenericLRAException {
+        invokeRestEndpointAndReturnLRA(lraId.toASCIIString(), TCK_NON_PARTICIPANT_RESOURCE_PATH, END_PATH, 200);
+    }
+
+    void leaveLRA(URI lra, String basePath, String resourcePath) throws GenericLRAException {
+        invokeRestEndpoint(lra, basePath, resourcePath, 200);
+    }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAInfo.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAInfo.java
deleted file mode 100644
index c511ef8..0000000
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAInfo.java
+++ /dev/null
@@ -1,63 +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.tck;
-
-/**
- * Data object carrying information about an instance
- * of LRA (specified by LRA id) and it's status.
- */
-public interface LRAInfo {
-
-    /**
-     * @return  LRA id that LRA instance is identified by
-     */
-    String getLraId();
-
-    /**
-     * @return  LRA client id
-     */
-    String getClientId();
-
-    /**
-     * @return  true if LRA was successfully closed, false otherwise
-     */
-    boolean isClosed();
-
-    /**
-     * @return  true if LRA was cancelled, false otherwise
-     */
-    boolean isCancelled();
-
-    /**
-     * @return  true if recovery is in progress on the LRA, false otherwise
-     */
-    boolean isRecovering();
-
-    /**
-     * @return  true if LRA is in active state right now, false otherwise
-     */
-    boolean isActive();
-
-    /**
-     * @return  true if LRA is top level (not nested), false otherwise
-     */
-    boolean isTopLevel();
-}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LraTckConfigBean.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LraTckConfigBean.java
index b0df98c..e1b2d44 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LraTckConfigBean.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LraTckConfigBean.java
@@ -24,7 +24,6 @@ import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 
 import org.eclipse.microprofile.config.inject.ConfigProperty;
-import org.eclipse.microprofile.lra.client.LRAClient;
 
 @ApplicationScoped
 public class LraTckConfigBean {
@@ -53,14 +52,14 @@ public class LraTckConfigBean {
      * Host name where LRA recovery is expected to be launch and TCK suite tries to connect to it at.
      * The port is specifed by {@link #recoveryPort}.
      */
-    @Inject @ConfigProperty(name = LRAClient.LRA_RECOVERY_HOST_KEY, defaultValue = "localhost")
+    @Inject @ConfigProperty(name = LRAClientOps.LRA_RECOVERY_HOST_KEY, defaultValue = "localhost")
     private String recoveryHostName;
 
     /**
      * Port where LRA recovery is expected to be launch and TCK suite tries to connect to it at.
      * The hostname is specifed by {@link #recoveryHostName}.
      */
-    @Inject @ConfigProperty(name = LRAClient.LRA_RECOVERY_PORT_KEY, defaultValue = "8080")
+    @Inject @ConfigProperty(name = LRAClientOps.LRA_RECOVERY_PORT_KEY, defaultValue = "8080")
     private int recoveryPort;
 
     /**
@@ -68,7 +67,7 @@ public class LraTckConfigBean {
      * The hostname of LRA recovery is specifed by {@link #recoveryHostName},
      * the port of LRA recovery is defined by {@link #recoveryPort}.
      */
-    @Inject @ConfigProperty(name = LRAClient.LRA_RECOVERY_PATH_KEY, defaultValue = "lra-recovery-coordinator")
+    @Inject @ConfigProperty(name = LRAClientOps.LRA_RECOVERY_PATH_KEY, defaultValue = "lra-recovery-coordinator")
     private String recoveryPath;
 
     /**
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraCancelOnController.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraCancelOnController.java
index e283335..eb8ffc5 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraCancelOnController.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraCancelOnController.java
@@ -19,7 +19,7 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.participant.api;
 
-import static org.eclipse.microprofile.lra.client.LRAClient.LRA_HTTP_HEADER;
+import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_HEADER;
 
 import java.net.MalformedURLException;
 import java.net.URI;
@@ -127,20 +127,9 @@ public class LraCancelOnController {
 
     public static final String CANCEL_FROM_REMOTE_CALL = "cancelFromRemoteCall";
     /**
-     * <p>
      * Returning <code>200</code> thus the LRA should be closed but
-     * beforehand it makes a remote REST call which returns 5xx which is the
+     * it calls the remote method which ends with 5xx which is the
      * default for the cancelling and so the whole LRA should be cancelled.
-     * </p>
-     * <p>
-     * The remote REST call invokes the same controller class {@link LraCancelOnController}
-     * That assumes the call to the representative of the same LRA participant
-     * as it's already enlisted by the method {@link #cancelFromRemoteCall()} invoked by the test.
-     * Because the specification mandates that the same participant can be enlisted
-     * only once per LRA instance then
-     * the {@link Compensate} method {@link #compensateWork(String, String)}
-     * will be called only once for the test invocation.
-     * </p>
      */
     @GET
     @Path(CANCEL_FROM_REMOTE_CALL)
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraController.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraController.java
index 6f3d128..2e9e93f 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraController.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraController.java
@@ -22,7 +22,7 @@ package org.eclipse.microprofile.lra.tck.participant.api;
 import org.eclipse.microprofile.lra.annotation.Forget;
 import org.eclipse.microprofile.lra.client.GenericLRAException;
 import org.eclipse.microprofile.lra.client.InvalidLRAIdException;
-import org.eclipse.microprofile.lra.client.LRAClient;
+import org.eclipse.microprofile.lra.tck.LRAClientOps;
 import org.eclipse.microprofile.lra.tck.participant.activity.Activity;
 import org.eclipse.microprofile.lra.tck.participant.activity.ActivityStorage;
 import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
@@ -47,8 +47,10 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
 import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -64,8 +66,8 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.IntStream;
 
-import static org.eclipse.microprofile.lra.client.LRAClient.LRA_HTTP_HEADER;
-import static org.eclipse.microprofile.lra.client.LRAClient.LRA_HTTP_RECOVERY_HEADER;
+import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_HEADER;
+import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_RECOVERY_HEADER;
 
 @ApplicationScoped
 @Path(LraController.LRA_CONTROLLER_PATH)
@@ -80,9 +82,6 @@ public class LraController {
 
     private static final String MISSING_LRA_DATA = "Missing LRA data";
 
-    @Inject
-    private LRAClient lraClient;
-
     private static final AtomicInteger COMPLETED_COUNT = new AtomicInteger(0);
     private static final AtomicInteger COMPENSATED_COUNT = new AtomicInteger(0);
 
@@ -123,33 +122,6 @@ public class LraController {
         return Response.ok(activity.getStatus().name()).build();
     }
 
-    /**
-     * Test that participants can leave an LRA using the {@link LRAClient} programmatic API
-     * @param lraUri the id of the LRA
-     * @param recoveryUri header param defines recovery URI
-     * @return the URI of the LRA if it was successfully removed
-     * @throws NotFoundException if the activity was not found
-     */
-    @PUT
-    @Path("/leave/{LraUri}")
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response leaveWorkViaAPI(@PathParam("LraUri") String lraUri,
-                                    @HeaderParam(LRA_HTTP_RECOVERY_HEADER) String recoveryUri)
-        throws NotFoundException {
-
-        if (lraUri != null && recoveryUri != null) {
-            lraClient.leaveLRA(URI.create(recoveryUri));
-
-            activityStore.getActivityAndAssertExistence(lraUri, context);
-
-            activityStore.remove(lraUri);
-
-            return Response.ok(lraUri).build();
-        }
-
-        return Response.ok("non transactional").build();
-    }
-
     @PUT
     @Path("/leave")
     @Produces(MediaType.APPLICATION_JSON)
@@ -283,25 +255,33 @@ public class LraController {
     public Response subActivity(@HeaderParam(LRA_HTTP_HEADER) String lraId) {
         assertNotHeaderPresent(lraId);
 
-        // manually start an LRA via the injection LRAClient api
-        URI lra = lraClient.startLRA(null,"subActivity", 0L, ChronoUnit.SECONDS);
+        Client client = null;
 
-        lraId = lra.toString();
+        try {
+            client = ClientBuilder.newClient();
+            WebTarget target = client.target(context.getBaseUri());
+            URI lra = new LRAClientOps(target).startLRA(null,"subActivity", 0L, ChronoUnit.SECONDS);
 
-        storeActivity(lraId, null);
+            lraId = lra.toString();
 
-        // invoke a method that SUPPORTS LRAs. The filters should detect the LRA we just
-        // started via the injected client
-        // and add it as a header before calling the method at path /supports (ie supportsLRACall()).
-        // The supportsLRACall method will return LRA id in the body if it is present.
-        String id = restPutInvocation(lra,"supports", "");
+            storeActivity(lraId, null);
 
-        // check that the invoked method saw the LRA
-        if (!lraId.equals(id)) {
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Entity.text("Unequal LRA ids")).build();
-        }
+            // invoke a method that SUPPORTS LRAs. The filters should detect the LRA we just started
+            // and add it as a header before calling the method at path /supports (ie supportsLRACall()).
+            // The supportsLRACall method will return LRA id in the body if it is present.
+            String id = restPutInvocation(lra,"supports", "");
+
+            // check that the invoked method saw the LRA
+            if (!lraId.equals(id)) {
+                return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Entity.text("Unequal LRA ids")).build();
+            }
 
-        return Response.ok(id).build();
+            return Response.ok(id).build();
+        } finally {
+            if (client != null) {
+                client.close();
+            }
+        }
     }
 
     @PUT
@@ -327,7 +307,7 @@ public class LraController {
                 .path(LRA_CONTROLLER_PATH)
                 .path(path)
                 .request()
-                .header(LRAClient.LRA_HTTP_HEADER, lraURI)
+                .header(LRA_HTTP_HEADER, lraURI)
                 .put(Entity.text(bodyText));
 
         if (response.hasEntity()) {
@@ -447,30 +427,6 @@ public class LraController {
         return Response.status(Response.Status.OK).entity(Entity.text("Simulate buisiness logic timeoout")).build();
     }
 
-    @GET
-    @Path("/renewTimeLimit")
-    @Produces(MediaType.APPLICATION_JSON)
-    @LRA(value = LRA.Type.REQUIRED, end = false, timeLimit = 100, timeUnit = ChronoUnit.MILLIS)
-    public Response extendTimeLimit(@HeaderParam(LRA_HTTP_HEADER) String lraId) {
-        assertHeaderPresent(lraId);
-
-        activityStore.add(new Activity(lraId));
-
-        try {
-            /*
-             * the incoming LRA was created with a timeLimit of 100 ms via the timeLimit annotation
-             * attribute update the timeLimit to 300 sleep for 200 return from the method so the LRA will
-             * have been running for 200 ms so it should not be cancelled
-             */
-            lraClient.renewTimeLimit(lraToURI(lraId, "Invalid LRA id"), 300, ChronoUnit.MILLIS);
-            // sleep for 200000 micro seconds (should be longer than specified in the timeLimit annotation attribute)
-            Thread.sleep(200);
-        } catch (InterruptedException e) {
-            LOGGER.log(Level.FINE, "Interrupted because the renewed time limit elapsed", e);
-        }
-        return Response.status(Response.Status.OK).entity(Entity.text("Simulate buisiness logic timeoout")).build();
-    }
-
     /**
      * Performing a PUT on "participant URL"/compensate will cause the participant to compensate
      * the work that was done within the scope of the transaction.
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NoLRAController.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NoLRAController.java
index 357b23d..b690f41 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NoLRAController.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NoLRAController.java
@@ -32,7 +32,7 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
-import static org.eclipse.microprofile.lra.client.LRAClient.LRA_HTTP_HEADER;
+import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_HEADER;
 import static org.eclipse.microprofile.lra.tck.participant.api.LraController.LRA_CONTROLLER_PATH;
 
 @ApplicationScoped
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NonParticipatingTckResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NonParticipatingTckResource.java
new file mode 100644
index 0000000..b0f0b49
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NonParticipatingTckResource.java
@@ -0,0 +1,155 @@
+/*
+ *******************************************************************************
+ * 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.api;
+
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+import org.eclipse.microprofile.lra.annotation.ws.rs.NestedLRA;
+import org.eclipse.microprofile.lra.client.InvalidLRAIdException;
+import org.eclipse.microprofile.lra.tck.LRAClientOps;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_HEADER;
+
+@ApplicationScoped
+@Path(NonParticipatingTckResource.TCK_NON_PARTICIPANT_RESOURCE_PATH)
+@LRA(value = LRA.Type.SUPPORTS, end = false)
+public class NonParticipatingTckResource {
+    public static final String TCK_NON_PARTICIPANT_RESOURCE_PATH = "non-participating-tck-resource";
+
+    public static final String START_AND_END_PATH = "/start-and-end";
+    public static final String START_BUT_DONT_END_PATH = "/start-dont-end";
+    public static final String START_AND_END_NESTED_PATH = "/start-nested-and-end";
+
+    public static final String START_BUT_DONT_END_NESTED_PATH = "/start-nested-and-dont-end";
+    public static final String NEVER_PATH = "/never";
+    public static final String END_PATH = "/end";
+
+    public static final String OK_TEXT = "OK";
+
+    private static final String MISSING_LRA_DATA = "Missing LRA data";
+    public static final String TRANSACTIONAL_WORK_PATH = "work";
+    public static final String ACCEPT_WORK = "acceptWork";
+    private static final Logger LOGGER = Logger.getLogger(NonParticipatingTckResource.class.getName());
+
+    static final String MANDATORY_LRA_RESOURCE_PATH = "/mandatory";
+
+    private static final AtomicInteger COMPLETED_COUNT = new AtomicInteger(0);
+    private static final AtomicInteger COMPENSATED_COUNT = new AtomicInteger(0);
+    public static final String STATUS_CODE_QUERY_NAME = "Coerce-Status";
+    public static final String START_LRA_VIA_REMOTE_INVOCATIOM = "/start-via-remote-invocation";
+
+    @Context
+    private UriInfo context;
+
+    @PUT
+    @Path(NonParticipatingTckResource.NEVER_PATH)
+    @LRA(value = LRA.Type.NEVER)
+    public Response neverRunWithLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId) {
+        return Response.ok(lraId).build();
+    }
+
+    @PUT
+    @Path(NonParticipatingTckResource.START_AND_END_PATH)
+    @LRA(value = LRA.Type.REQUIRES_NEW,
+            cancelOnFamily = Response.Status.Family.SERVER_ERROR) // default is to end the LRA
+    public Response startAndEndLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId,
+                                   @DefaultValue("200") @QueryParam(STATUS_CODE_QUERY_NAME) int coerceStatus) {
+        return Response.status(coerceStatus).entity(checkLRANotNull(lraId)).build();
+    }
+
+    @PUT
+    @Path(NonParticipatingTckResource.START_BUT_DONT_END_PATH)
+    @LRA(value = LRA.Type.REQUIRES_NEW, end = false,
+            cancelOnFamily = Response.Status.Family.SERVER_ERROR)
+    public Response startDontEndLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId,
+                                    @DefaultValue("200") @QueryParam(STATUS_CODE_QUERY_NAME) int coerceStatus) {
+        return Response.status(coerceStatus).entity(checkLRANotNull(lraId)).build();
+    }
+
+    @PUT
+    @Path(NonParticipatingTckResource.END_PATH)
+    @LRA(value = LRA.Type.MANDATORY,
+            cancelOnFamily = Response.Status.Family.SERVER_ERROR) // default is to end the LRA
+    public Response endLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId,
+                           @DefaultValue("200") @QueryParam(STATUS_CODE_QUERY_NAME) int coerceStatus) {
+        return Response.status(coerceStatus).entity(checkLRANotNull(lraId)).build();
+    }
+
+    @PUT
+    @Path(NonParticipatingTckResource.START_AND_END_NESTED_PATH)
+    @LRA(value = LRA.Type.MANDATORY,
+            cancelOnFamily = Response.Status.Family.SERVER_ERROR) // default is to end the LRA
+    @NestedLRA
+    public Response startAndEndNestedLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId) {
+        return Response.ok(lraId).build();
+    }
+
+    @PUT
+    @Path(NonParticipatingTckResource.START_BUT_DONT_END_NESTED_PATH)
+    @LRA(value = LRA.Type.MANDATORY, end = false,
+            cancelOnFamily = Response.Status.Family.SERVER_ERROR)
+    @NestedLRA
+    public Response startAndDontEndNestedLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId) {
+        return Response.ok(lraId).build();
+    }
+
+    @PUT
+    @Path(NonParticipatingTckResource.START_LRA_VIA_REMOTE_INVOCATIOM)
+    @LRA(value = LRA.Type.SUPPORTS, end = false,
+            cancelOnFamily = Response.Status.Family.SERVER_ERROR)
+    public Response notSupportedButCallServiceWhichStartsButDoesntEndAnLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId) {
+        return Response.ok(invokeRestEndpoint(lraId, TCK_NON_PARTICIPANT_RESOURCE_PATH, START_BUT_DONT_END_PATH,
+                200)).build();
+    }
+
+    private String invokeRestEndpoint(String lra, String basePath, String path, int coerceResponse) {
+        Client client = ClientBuilder.newClient();
+
+        try {
+            WebTarget target = client.target(context.getBaseUri());
+            return new LRAClientOps(target).invokeRestEndpointAndReturnLRA(lra, basePath, path, coerceResponse);
+        } finally {
+            client.close();
+        }
+    }
+
+    private String checkLRANotNull(String lraId) {
+        if (lraId == null) {
+            throw new InvalidLRAIdException(null,
+                    String.format("%s: missing '%s' header", context.getPath(), LRA_HTTP_HEADER));
+        }
+
+        return lraId;
+    }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ParticipatingTckResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ParticipatingTckResource.java
new file mode 100644
index 0000000..be4dcc0
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ParticipatingTckResource.java
@@ -0,0 +1,126 @@
+/*
+ *******************************************************************************
+ * 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.api;
+
+import org.eclipse.microprofile.lra.annotation.Compensate;
+import org.eclipse.microprofile.lra.annotation.Complete;
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+import org.eclipse.microprofile.lra.annotation.ws.rs.Leave;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_HEADER;
+
+@ApplicationScoped
+@Path(ParticipatingTckResource.TCK_PARTICIPANT_RESOURCE_PATH)
+public class ParticipatingTckResource {
+    private static final Logger LOGGER = Logger.getLogger(ParticipatingTckResource.class.getName());
+
+    public static final String TCK_PARTICIPANT_RESOURCE_PATH = "participating-tck-resource";
+    public static final String JOIN_WITH_EXISTNG_LRA_PATH = "/join-with-existing-lra";
+    public static final String JOIN_WITH_NEW_LRA_PATH = "/join-with-new-LRA";
+    public static final String COMPLETED_CNT_PATH = "/completed-count";
+    public static final String COMPENSATED_CNT_PATH = "/compensated-count";
+    public static final String LEAVE_PATH = "/leave";
+
+    private static final AtomicInteger COMPLETED_COUNT = new AtomicInteger(0);
+    private static final AtomicInteger COMPENSATED_COUNT = new AtomicInteger(0);
+
+    @PUT
+    @Path("/compensate")
+    @Compensate
+    public Response compensateWork(@HeaderParam(LRA_HTTP_HEADER) String lraId, String userData)
+            throws NotFoundException {
+        if(lraId == null) {
+            throw new NullPointerException("lraId can't be null as it should be invoked with the context");
+        }
+
+        COMPENSATED_COUNT.incrementAndGet();
+
+        LOGGER.info(String.format("LRA id '%s' was compensated", lraId));
+
+        return Response.ok().build();
+    }
+
+    @PUT
+    @Path("/complete")
+    @Complete
+    public Response completeWork(@HeaderParam(LRA_HTTP_HEADER) String lraId, String userData)
+            throws NotFoundException {
+        if(lraId == null) {
+            throw new NullPointerException("lraId can't be null as it should be invoked with the context");
+        }
+
+        COMPLETED_COUNT.incrementAndGet();
+
+        LOGGER.info(String.format("LRA id '%s' was completed", lraId));
+
+        return Response.ok().build();
+    }
+
+    @PUT
+    @Path(LEAVE_PATH)
+    @LRA(value = LRA.Type.SUPPORTS, end = false)
+    @Leave
+    public Response leaveLRA() {
+        return Response.ok().build();
+    }
+
+    @PUT
+    @Path(COMPLETED_CNT_PATH)
+    @Produces(MediaType.APPLICATION_JSON)
+    @LRA(LRA.Type.NOT_SUPPORTED)
+    public Response getCompletedCount() {
+        return Response.ok(COMPLETED_COUNT.get()).build();
+    }
+
+    @PUT
+    @Path(COMPENSATED_CNT_PATH)
+    @Produces(MediaType.APPLICATION_JSON)
+    @LRA(LRA.Type.NOT_SUPPORTED)
+    public Response getCompensatedCount() {
+        return Response.ok(COMPENSATED_COUNT.get()).build();
+    }
+
+    // if this resource path is invoked outside of a LRA then, since this JAX-RS resoruce is annotated
+    // as a participant, the implementation MUST report a PRECONDITION_FAILED status
+    @PUT
+    @Path(ParticipatingTckResource.JOIN_WITH_EXISTNG_LRA_PATH)
+    @LRA(value = LRA.Type.MANDATORY, end = false)
+    public Response joinWithExistingLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId) {
+        return Response.ok().build();
+    }
+
+    @PUT
+    @Path(ParticipatingTckResource.JOIN_WITH_NEW_LRA_PATH)
+    @LRA(value = LRA.Type.REQUIRES_NEW, end = false)
+    public Response joinWithNewLRA(@HeaderParam(LRA_HTTP_HEADER) String lraId) {
+        return Response.ok().build();
+    }
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/spi/ManagementSPI.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/spi/ManagementSPI.java
deleted file mode 100644
index 6f60896..0000000
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/spi/ManagementSPI.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *******************************************************************************
- * 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.spi;
-
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
-import org.eclipse.microprofile.lra.tck.LRAInfo;
-
-import javax.ws.rs.NotFoundException;
-import java.net.URI;
-import java.util.List;
-
-public interface ManagementSPI {
-    /**
-     * Lookup information about an LRA
-     *
-     * @param lraId the LRA whose status is being requested
-     *
-     * @return the status of the the LRA
-     *
-     * @throws NotFoundException if the LRA no longer exists (which may or may
-     * not indicate that the LRA has already closed or cancelled depending upon
-     * how long the implementation chooses to maintain such information).
-     */
-    LRAInfo getStatus(URI lraId) throws NotFoundException;
-
-    /**
-     * Lookup LRAs that are in a particular state.
-     *
-     * @param status the state that the returned LRAs should be in. A null value
-     *               indicates that all LRAs should be returned regardless of their
-     *               state.
-     *
-     * @return all LRAs that are in the given state. The order of the elements in
-     * the list are implementation specific (for example an implementation may choose
-     * to return them in the order in which they were created).
-     */
-    List<LRAInfo> getLRAs(LRAStatus status);
-}


Test patch:
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckCancelOnTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckCancelOnTests.java
index 3d8f1c5..f29601d 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckCancelOnTests.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckCancelOnTests.java
@@ -43,6 +43,7 @@ import org.jboss.shrinkwrap.api.spec.WebArchive;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestName;
@@ -172,13 +173,13 @@ public class TckCancelOnTests {
      * See {@link LraCancelOnController#cancelFromRemoteCall()}
      */
     @Test
+    @Ignore
     public void cancelFromRemoteCall() {
         Response response = getSuiteTarget().path(LRA_CANCEL_ON_CONTROLLER_PATH)
                 .path(LraCancelOnController.CANCEL_FROM_REMOTE_CALL).request().get();
         assertEquals("The 200 status response is expected", Status.OK.getStatusCode(), response.getStatus());
-        // LraCancelOnController enlists twice the same participant, compensate is expected to be called only once
-        assertEquals("Status was 200 but compensate should be called as LRA should be cancelled for remotely called participant as well",
-                beforeCompensatedCount + 1, getCompensateCount());
+        assertEquals("Status was 200 but compensate should be called twice as LRA should be cancelled for remotely called participant as well",
+                beforeCompensatedCount + 2, getCompensateCount());
         assertEquals("Even the 200 status was received the remotly called participant should cause the LRA being cancelled",
                 beforeCompletedCount, getCompletedCount());
     }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTests.java
index 71ed32d..4912343 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTests.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTests.java
@@ -28,15 +28,11 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
-import java.net.URLEncoder;
 import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.logging.Logger;
 import java.util.stream.IntStream;
 
@@ -48,18 +44,16 @@ import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Response;
 
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
-import org.eclipse.microprofile.lra.client.LRAClient;
+import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
+import org.eclipse.microprofile.lra.client.GenericLRAException;
 import org.eclipse.microprofile.lra.tck.participant.api.LraController;
 import org.eclipse.microprofile.lra.tck.participant.api.NoLRAController;
 import org.eclipse.microprofile.lra.tck.participant.api.Util;
-import org.eclipse.microprofile.lra.tck.spi.ManagementSPI;
 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.Ignore;
@@ -78,11 +72,7 @@ public class TckTests {
     @Inject
     private LraTckConfigBean config;
 
-    @Inject
-    private LRAClient lraClient;
-
-    @Inject
-    private ManagementSPI lraManagement;
+    private LRAClientOps lraClient;
 
     private static URL recoveryCoordinatorBaseUrl;
     private static Client tckSuiteClient;
@@ -91,8 +81,6 @@ public class TckTests {
     private WebTarget tckSuiteTarget;
     private WebTarget recoveryTarget;
 
-    private static List<LRAInfo> notProperlyClosedLRAs = new ArrayList<>();
-
     private enum CompletionType {
         complete, compensate, mixed
     }
@@ -123,33 +111,13 @@ public class TckTests {
 
         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);
         }
         recoveryTarget = recoveryCoordinatorClient.target(URI.create(recoveryCoordinatorBaseUrl.toExternalForm()));
     }
 
-    @After
-    public void after() {
-        List<LRAInfo> activeLRAs = lraManagement.getLRAs(LRAStatus.Active);
-
-        if (activeLRAs.size() != 0) {
-            activeLRAs.forEach(lra -> {
-                try {
-                    if (!notProperlyClosedLRAs.contains(lra)) {
-                        LOGGER.warning(String.format(
-                                "<after test> %s: test did not close %s%n", testName.getMethodName(), lra.getLraId()));
-                        notProperlyClosedLRAs.add(lra);
-                        lraClient.closeLRA(URI.create(lra.getLraId()));
-                    }
-                } catch (WebApplicationException e) {
-                    LOGGER.warning(String.format("<after test> %s: exception %s closing %s%n",
-                            testName.getMethodName(), e.getMessage(), lra.getLraId()));
-                }
-            });
-        }
-    }
-
     /**
      * Checking if coordinator is running, set ups the client to contact the recovery manager and the TCK suite itself.
      */
@@ -171,14 +139,6 @@ public class TckTests {
         }
     }
 
-    // TODO: what's difference to closeLRA?
-    @Test
-    public void startLRA() throws WebApplicationException {
-        URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-
-        lraClient.closeLRA(lra);
-    }
-
     /**
      * <p>
      * Starting LRA and canceling it.
@@ -187,15 +147,17 @@ public class TckTests {
      */
     @Test
     public void cancelLRA() throws WebApplicationException {
-        URI lra = lraClient.startLRA(null,lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-
-        lraClient.cancelLRA(lra);
+        try {
+            URI lra = lraClient.startLRA(null,lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
 
-        List<LRAInfo> allClientLRAs = lraManagement.getLRAs(null);
-        boolean isLraIdInList = containsLraId(allClientLRAs, lra);
+            lraClient.cancelLRA(lra);
 
-        assertFalse("LRA '" + lra + "' should not be active anymore but was found in the list of all lras "
-            + allClientLRAs, isLraIdInList);
+            assertTrue("LRA '" + lra + "' should not be active ",
+                    lraClient.isLRAFinished(lra));
+        } catch (GenericLRAException e) {
+            e.printStackTrace();
+            throw e;
+        }
     }
 
     /**
@@ -210,100 +172,16 @@ public class TckTests {
 
         lraClient.closeLRA(lra);
 
-        List<LRAInfo> allClientLRAs = lraManagement.getLRAs(null);
-        boolean isLraIdInList = containsLraId(allClientLRAs, lra);
-
-        assertFalse("LRA '" + lra + "' should not be active anymore but was found in the list of all lras "
-                + allClientLRAs, isLraIdInList);
-    }
-
-    /**
-     * Started LRA should be listed amongst active LRAs.
-     */
-    @Test
-    public void getActiveLRAs() throws WebApplicationException {
-        URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-
-        List<LRAInfo> allActiveLRAs = lraManagement.getLRAs(LRAStatus.Active);
-        boolean isLraIdInList = containsLraId(allActiveLRAs, lra);
-
-        assertTrue("LRA '" + lra + "' should be listed in the list of the active LRAs, as it was not closed yet",
-                isLraIdInList);
-
-        lraClient.closeLRA(lra);
-    }
-
-    /**
-     * Started LRA should be listed amongst active LRAs.
-     */
-    @Test
-    public void getAllLRAs() throws WebApplicationException {
-        URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-
-        List<LRAInfo> allClientLRAs = lraManagement.getLRAs(null);
-        boolean isLraIdInList = containsLraId(allClientLRAs, lra);
-
-        assertTrue("LRA '" + lra + "' should be listed in the list of the all LRAs, as it was not closed yet, but it isn't",
-                isLraIdInList);
-
-        lraClient.closeLRA(lra);
-    }
-
-    @Test
-    @Ignore
-    public void getRecoveringLRAs() throws WebApplicationException {
-        // TODO
-    }
-
-    /**
-     * Started LRA should be in state 'active'.
-     */
-    @Test
-    public void isActiveLRA() throws WebApplicationException {
-        URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-
-        assertEquals("LRA '" + lra + "' is not denoted as active even it was started",
-                LRAStatus.Active, lraClient.getStatus(lra));
-
-        lraClient.closeLRA(lra);
-    }
-
-    /**
-     * Canceled LRA should be in state 'compensated'.
-     * NOTE: the coordinator cleans up when canceled
-     */
-    @Test
-    @Ignore
-    public void isCompensatedLRA() throws WebApplicationException {
-        URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-
-        lraClient.cancelLRA(lra);
-
-        assertEquals("LRA '" + lra + "' is not denoted as compensated even it was canceled",
-                LRAStatus.Cancelled, lraClient.getStatus(lra));
-    }
-
-    /**
-     * Closed LRA should be in state 'completed'.
-     * NOTE: the coordinator cleans up when completed
-     */
-    @Test
-    @Ignore
-    public void isCompletedLRA() throws WebApplicationException {
-        URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-
-        lraClient.closeLRA(lra);
-
-        assertEquals("LRA '" + lra + "' is not denoted as compensated even it was canceled",
-                LRAStatus.Closed, lraClient.getStatus(lra));
+        assertTrue("LRA '" + lra + "' should not be active anymore",
+                lraClient.isLRAFinished(lra));
     }
 
     /**
      * HTTP request to {@link LraController#activityWithLRA}
-     * which is a method annotated with {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA.Type#REQUIRED}.
+     * which is a method annotated with LRA.Type#REQUIRED}.
      */
     @Test
-    @Ignore
+    @Ignore // TODO why is this ignored
     public void joinLRAViaBody() throws WebApplicationException {
 
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
@@ -312,12 +190,8 @@ public class TckTests {
         String lraId = checkStatusReadAndClose(Response.Status.OK, response, resourcePath);
 
         // validate that the LRA coordinator no longer knows about lraId
-        List<LRAInfo> activeLras = lraManagement.getLRAs(LRAStatus.Active);
-        boolean isLraIdInList = containsLraId(activeLras, lraId);
-
-        // the resource /activities/work is annotated with Type.REQUIRED so the container should have ended it
-        assertFalse("LRA work was processed and the annotated method finished but the LRA id '" + lraId + "'"
-            + "is still part of known active LRAs " + activeLras, isLraIdInList);
+        assertTrue("LRA work was processed and the annotated method finished but the LRA id '" + lraId + "'"
+            + "is still active", lraClient.isLRAFinished(lraId));
     }
 
     @Test
@@ -330,13 +204,13 @@ public class TckTests {
         try {
             response = resourcePath
                     .request()
-                    .header(LRAClient.LRA_HTTP_HEADER, lra)
+                    .header(LRA.LRA_HTTP_HEADER, lra)
                     .put(Entity.text(""));
     
             assertEquals("Response status to ' " + resourcePath.getUri() + "' does not match.",
                     Response.Status.OK.getStatusCode(), response.getStatus());
     
-            Object parentId = response.getHeaders().getFirst(LRAClient.LRA_HTTP_HEADER);
+            Object parentId = response.getHeaders().getFirst(LRA.LRA_HTTP_HEADER);
     
             assertNotNull("Expecting to get parent LRA id as response from " + resourcePath.getUri(), parentId);
             assertEquals("The nested activity should return the parent LRA id. The call to " + resourcePath.getUri(),
@@ -348,12 +222,10 @@ public class TckTests {
             lraClient.closeLRA(lra);
     
             // validate that the nested LRA was closed
-            List<LRAInfo> activeLras = lraManagement.getLRAs(LRAStatus.Active);
-            boolean isLraIdInList = containsLraId(activeLras, nestedLraId);
-    
+
             // the resource /activities/work is annotated with Type.REQUIRED so the container should have ended it
-            assertFalse("Nested LRA id '" + lra + "' should be listed in the list of the active LRAs (from call to "
-                    + resourcePath.getUri() + ")", isLraIdInList);        
+            assertTrue("Nested LRA id '" + lra + "' should be listed in the list of the active LRAs (from call to "
+                    + resourcePath.getUri() + ")", lraClient.isLRAFinished(nestedLraId));
         } finally {
             if(response != null) {
                 response.close();
@@ -384,23 +256,20 @@ public class TckTests {
 
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
         Response response = resourcePath
-                .request().header(LRAClient.LRA_HTTP_HEADER, lra).put(Entity.text(""));
+                .request().header(LRA.LRA_HTTP_HEADER, lra).put(Entity.text(""));
 
         checkStatusAndClose(Response.Status.OK, response, resourcePath);
 
         // validate that the LRA coordinator still knows about lraId
-        List<LRAInfo> allActiveLRAs = lraManagement.getLRAs(LRAStatus.Active);
-        boolean isLraIdInList = containsLraId(allActiveLRAs, lra);
-        assertTrue("LRA '" + lra + "' should be active as it is not closed yet. But it was not found in the list of active LRAs "
-                + allActiveLRAs, isLraIdInList);
+        assertFalse("LRA '" + lra + "' should be active as it is not closed yet.",
+                lraClient.isLRAFinished(lra));
 
         // close the LRA
         lraClient.closeLRA(lra);
 
         // check that LRA coordinator no longer knows about lraId
-        allActiveLRAs = lraManagement.getLRAs(LRAStatus.Active);
-        assertFalse("LRA '" + lra + "' should not be active anymore as it was closed yet. But it was not found amongst active LRAs "
-                + allActiveLRAs, containsLraId(allActiveLRAs, lra));
+        assertTrue("LRA '" + lra + "' should not be active anymore as it was closed yet.",
+                lraClient.isLRAFinished(lra));
 
         // check that participant was told to complete
         int completedCount = getCompletedCount();
@@ -410,18 +279,14 @@ public class TckTests {
 
     @Test
     public void join() throws WebApplicationException {
-        List<LRAInfo> lras = lraManagement.getLRAs(LRAStatus.Active);
-        int count = lras.size();
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
         Response response = resourcePath
-                .request().header(LRAClient.LRA_HTTP_HEADER, lra).put(Entity.text(""));
+                .request().header(LRA.LRA_HTTP_HEADER, lra).put(Entity.text(""));
         checkStatusAndClose(Response.Status.OK, response, resourcePath);
         lraClient.closeLRA(lra);
-
-        lras = lraManagement.getLRAs(LRAStatus.Active);
-        assertEquals("Number of LRA instances before the test does not match current number of active LRAs. The joint LRA should be closed already. "
-                + "The test call went to LRA controller at " + resourcePath.getUri(), count, lras.size());
+        assertTrue("LRA '" + lra + "' should be active as it is not closed yet.",
+                lraClient.isLRAFinished(lra));
     }
 
     @Test
@@ -429,18 +294,18 @@ public class TckTests {
         int beforeCompletedCount = getCompletedCount();
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
-        Response response = resourcePath.request().header(LRAClient.LRA_HTTP_HEADER, lra).put(Entity.text(""));
+        Response response = resourcePath.request().header(LRA.LRA_HTTP_HEADER, lra).put(Entity.text(""));
 
         checkStatusAndClose(Response.Status.OK, response, resourcePath);
 
         // perform a second request to the same method in the same LRA context to validate that multiple participants are not registered
         resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
-        response = resourcePath.request().header(LRAClient.LRA_HTTP_HEADER, lra).put(Entity.text(""));
+        response = resourcePath.request().header(LRA.LRA_HTTP_HEADER, lra).put(Entity.text(""));
         checkStatusAndClose(Response.Status.OK, response, resourcePath);
 
         // call a method annotated with @Leave (should remove the participant from the LRA)
         resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path("leave");
-        response = resourcePath.request().header(LRAClient.LRA_HTTP_HEADER, lra).put(Entity.text(""));
+        response = resourcePath.request().header(LRA.LRA_HTTP_HEADER, lra).put(Entity.text(""));
         checkStatusAndClose(Response.Status.OK, response, resourcePath);
 
         lraClient.closeLRA(lra);
@@ -453,62 +318,19 @@ public class TckTests {
                 + "The test call went to LRA controller at " + resourcePath.getUri(), beforeCompletedCount, completedCount);
     }
 
-    @Test
-    public void leaveLRAViaAPI() throws WebApplicationException {
-        int beforeCompletedCount = getCompletedCount();
-        URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-
-        WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
-
-        Response response = resourcePath.request().header(LRAClient.LRA_HTTP_HEADER, lra).put(Entity.text(""));
-        checkStatusAndClose(Response.Status.OK, response, resourcePath);
-
-        // perform a second request to the same method in the same LRA context to validate that multiple participants are not registered
-        resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
-        response = resourcePath.request().header(LRAClient.LRA_HTTP_HEADER, lra).put(Entity.text(""));
-
-        String recoveryUri = response.getHeaderString(LRAClient.LRA_HTTP_RECOVERY_HEADER);        
-        
-        checkStatusAndClose(Response.Status.OK, response, resourcePath);
-
-        // call a method annotated with @Leave (should remove the participant from the LRA)
-        try {
-            resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path("leave");
-            response = resourcePath.path(URLEncoder.encode(lra.toString(), "UTF-8"))
-                    .request()
-                    .header(LRAClient.LRA_HTTP_HEADER, lra)
-                    .header(LRAClient.LRA_HTTP_RECOVERY_HEADER, recoveryUri)
-                    .put(Entity.text(""));
-        } catch (UnsupportedEncodingException e) {
-            throw new WebApplicationException(
-                    Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
-                            Entity.text(String.format("%s: %s", resourcePath.getUri().toString(), e.getMessage()))).build());
-        }
-        checkStatusAndClose(Response.Status.OK, response, resourcePath);
-
-        lraClient.closeLRA(lra);
-
-        // check that participant was not told to complete
-        int completedCount = getCompletedCount();
-
-        assertEquals("Wrong completion count when participant left the LRA by calling @Leave method. "
-                + "Expecting the completed count hasn't change between start and end of the test. "
-                + "The test call went to LRA controller at " + resourcePath.getUri(), beforeCompletedCount, completedCount);
-    }
-
     @Test
     public void dependentLRA() throws WebApplicationException {
         // call a method annotated with NOT_SUPPORTED but one which programatically starts an LRA and returns it via a header
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path("startViaApi");
         Response response = resourcePath.request().put(Entity.text(""));
         // check that the method started an LRA
-        Object lraHeader = response.getHeaders().getFirst(LRAClient.LRA_HTTP_HEADER);
+        Object lraHeader = response.getHeaders().getFirst(LRA.LRA_HTTP_HEADER);
 
         String lraId = checkStatusReadAndClose(Response.Status.OK, response, resourcePath);
 
         // the value returned via the header and body should be equal
 
-        assertNotNull("JAX-RS response to PUT request should have returned the header " + LRAClient.LRA_HTTP_HEADER
+        assertNotNull("JAX-RS response to PUT request should have returned the header " + LRA.LRA_HTTP_HEADER
                 + ". The test call went to " + resourcePath.getUri(), lraHeader);
         assertNotNull("JAX-RS response to PUT request should have returned content of LRA id. The test call went to "
                 + resourcePath.getUri(), lraId);
@@ -551,51 +373,24 @@ public class TckTests {
                 + "The test call went to " + resourcePath.getUri(), beforeCompensatedCount + 1, compensatedCount);
     }
 
-    /*
-     * Participants can pass data during enlistment and this data will be returned during
-     * the complete/compensate callbacks
-     */
-    // TODO: is this test to be run?
-    private void testUserData() {
-        List<LRAInfo> beforeActiveLRAs = lraManagement.getLRAs(LRAStatus.Active);
-        String testData = "test participant data";
-        WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path("testUserData");
-
-        Response response = resourcePath
-                .request().put(Entity.text(testData));
-
-        String activityId = checkStatusReadAndClose(Response.Status.OK, response, resourcePath);
-
-        List<LRAInfo> activeLRAs = lraManagement.getLRAs(LRAStatus.Active);
-
-        assertEquals("produced the wrong LRA count on call of method " + resourcePath.getUri(),
-                beforeActiveLRAs.size(), activeLRAs.size());
-
-        response = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path("getActivity")
-                .queryParam("activityId", activityId)
-                .request()
-                .get();
-
-        String activity = response.readEntity(String.class);
-
-        // validate that the service received the correct data during the complete call
-        assertTrue("service should receive userData field during complete call", activity.contains("userData='" + testData));
-        assertTrue("service should receive endData field during complete call", activity.contains("endData='" + testData));
+    @Test
+    public void acceptCloseTest() throws WebApplicationException {
+        joinAndEnd(true, true, LRA_CONTROLLER_PATH, ACCEPT_WORK);
     }
 
     @Test
-    public void acceptTest() throws WebApplicationException {
-        joinAndEnd(true, true, LRA_CONTROLLER_PATH, ACCEPT_WORK);
+    public void acceptCancelTest() throws WebApplicationException {
+        joinAndEnd(true, false, LRA_CONTROLLER_PATH, ACCEPT_WORK);
     }
 
-    // TODO the spec does not specifiy recovery semantics
     private void joinAndEnd(boolean waitForRecovery, boolean close, String path, String path2) throws WebApplicationException {
-        int beforeActiveLRACount = lraManagement.getLRAs(LRAStatus.Active).size();
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
+        int beforeCompletionCount = getCompletedCount();
+        int beforeCompensationCount = getCompensatedCount();
         WebTarget resourcePath = tckSuiteTarget.path(path).path(path2);
 
         Response response = resourcePath
-                .request().header(LRAClient.LRA_HTTP_HEADER, lra).put(Entity.text(""));
+                .request().header(LRA.LRA_HTTP_HEADER, lra).put(Entity.text(""));
 
         checkStatusAndClose(Response.Status.OK, response, resourcePath);
 
@@ -606,20 +401,32 @@ public class TckTests {
         }
 
         if (waitForRecovery) {
+            // TODO the spec does not specifiy recovery semantics
+
             // trigger a recovery scan which trigger a replay attempt on any participants
             // that have responded to complete/compensate requests with Response.Status.ACCEPTED
-            resourcePath = recoveryTarget.path("recovery");
-            Response response2 = resourcePath
+            WebTarget recoveryPath = recoveryTarget.path("recovery");
+            Response response2 = recoveryPath
                     .request().get();
 
-            checkStatusAndClose(Response.Status.OK, response2, resourcePath);
+            checkStatusAndClose(Response.Status.OK, response2, recoveryPath);
         }
 
-        int activeLRACount = lraManagement.getLRAs(LRAStatus.Active).size();
+        int completionCount = getCompletedCount() - beforeCompletionCount;
+        int compensationCount = getCompensatedCount() - beforeCompensationCount;
+
+        boolean wasCalled = (close ? completionCount > 0 : compensationCount > 0);
+        boolean wasNotCalled = (close ? compensationCount == 0 : completionCount == 0);
+        String lraMode = (close ? "close" : "cancel");
+        String participantMode = (close ? "complete" : "compensate");
 
-        assertEquals("Expecting all LRAs were recovered and the number of active LRAs before test matches the number after. "
-                + "The test call went to " + resourcePath.getUri(),
-                beforeActiveLRACount, activeLRACount);
+        assertTrue(String.format("acceptTest with %s: participant (%s) was not asked to %s",
+                lraMode, resourcePath.getUri(), participantMode),
+                wasCalled);
+        assertTrue(String.format("acceptTest with %s: participant (%s) was asked to %s",
+                lraMode, resourcePath.getUri(), participantMode),
+                wasNotCalled);
+        assertTrue("acceptTest: LRA did not finish", lraClient.isLRAFinished(lra));
     }
 
     @Test
@@ -632,7 +439,7 @@ public class TckTests {
         int beforeCompensatedCount = getCompensatedCount();
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
 
-        Response response = resourcePath.request().header(LRAClient.LRA_HTTP_HEADER, lra)
+        Response response = resourcePath.request().header(LRA.LRA_HTTP_HEADER, lra)
                 .put(Entity.text(""));
 
         String lraId = checkStatusReadAndClose(Response.Status.OK, response, resourcePath);
@@ -655,36 +462,6 @@ public class TckTests {
                 + "The test call went to " + resourcePath.getUri(), beforeCompensatedCount + 1, compensatedCount);
     }
 
-    // TODO: is this test to be run?
-    private void renewTimeLimit() {
-        int beforeCompletedCount = getCompletedCount();
-        int beforeCompensatedCount = getCompensatedCount();
-
-        WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH)
-                .path("renewTimeLimit");
-
-        Response response = resourcePath
-                .request()
-                .get();
-
-        response.close();
-
-        // check that participant was invoked
-        int completedCount = getCompletedCount();
-        int compensatedCount = getCompensatedCount();
-
-        /*
-         * The call to activities/timeLimit should have started an LRA whch should not have timed out
-         * (because the called resource method renews the timeLimit before sleeping for longer than
-         * the timeLimit annotation attribute specifies).
-         * Therefore the participant should not have compensated:
-         */
-        assertEquals("Compensate was called instead of complete. The test call went to " + resourcePath.getUri(),
-                beforeCompletedCount + 1, completedCount);
-        assertEquals("Compensate should not have been called. The test call went to " + resourcePath.getUri(),
-                beforeCompensatedCount, compensatedCount);
-    }
-
     private void checkStatusAndClose(Response.Status expectedStatus, Response response, WebTarget resourcePath) {
         try {
             assertEquals("Not expected status at call '" + resourcePath.getUri() + "'",
@@ -737,14 +514,12 @@ public class TckTests {
         Response response = resourcePath
                 .queryParam("nestedCnt", nestedCnt)
                 .request()
-                .header(LRAClient.LRA_HTTP_HEADER, lra)
+                .header(LRA.LRA_HTTP_HEADER, lra)
                 .put(Entity.text(""));
 
         String lraStr = checkStatusReadAndClose(Response.Status.OK, response, resourcePath);
         assertNotNull("expecting a LRA string returned from " + resourcePath.getUri(), lraStr);
-        assert lraStr != null;
         String[] lraArray = lraStr.split(",");
-        final List<LRAInfo> lras = lraManagement.getLRAs(LRAStatus.Active);
         URI[] uris = new URI[lraArray.length];
 
         IntStream.range(0, uris.length).forEach(i -> {
@@ -764,12 +539,13 @@ public class TckTests {
 
         // check that the coordinator knows about the two nested LRAs started by the multiLevelNestedActivity method
         // NB even though they should have completed they are held in memory pending the enclosing LRA finishing
-        IntStream.rangeClosed(1, nestedCnt).forEach(i -> assertTrue("missing nested LRA: step 2b (path called " + resourcePath + ")",
-                containsLraId(lras, lraArray[i])));
+        IntStream.rangeClosed(1, nestedCnt).forEach(i -> assertFalse(
+                "multiLevelNestedActivity: nested LRA should be active: step 2b (path called " + resourcePath + ")",
+                lraClient.isLRAFinished(lraArray[i])));
 
         // and the mandatory lra seen by the multiLevelNestedActivity method
-        assertTrue("lra should have been found (path called " + resourcePath.getUri() + ")",
-                containsLraId(lras, lraArray[0]));
+        assertFalse("multiLevelNestedActivity: top level LRA should be active (path called " + resourcePath.getUri() + ")",
+                lraClient.isLRAFinished(lraArray[0]));
 
         int inMiddleCompletedCount = getCompletedCount();
         int inMiddleCompensatedCount = getCompensatedCount();
@@ -806,11 +582,10 @@ public class TckTests {
         }
 
         // validate that the top level and nested LRAs are gone
-        final List<LRAInfo> lras2 = lraManagement.getLRAs(LRAStatus.Active);
-
-        IntStream.rangeClosed(0, nestedCnt).forEach(i -> assertFalse(
-                "multiLevelNestedActivity: top level or nested activity still active (called path " + resourcePath.getUri() + ")",
-                containsLraId(lras2, lraArray[i])));
+        IntStream.rangeClosed(0, nestedCnt).forEach(i -> assertTrue(
+                String.format("multiLevelNestedActivity: %s LRA still active (resouce path was %s)",
+                        (i == 0 ? "top level" : "nested"), resourcePath.getUri()),
+                lraClient.isLRAFinished(lraArray[i])));
 
         int afterCompletedCount = getCompletedCount();
         int afterCompensatedCount = getCompensatedCount();
@@ -839,7 +614,7 @@ public class TckTests {
                     inMiddleCompensatedCount + 1 + nestedCnt, afterCompensatedCount);
         } else {
             /*
-             * The test is calling for a mixed uutcome:
+             * The test is calling for a mixed outcome:
              * - the top level LRA was closed
              * - one of the nested LRAs was compensated the rest should have been completed
              */
@@ -856,15 +631,6 @@ public class TckTests {
         }
     }
 
-    private boolean containsLraId(List<LRAInfo> lras, URI lraIdURI) {
-        String lraId = lraIdURI.toString();
-        return containsLraId(lras, lraId);
-    }
-
-    private boolean containsLraId(List<LRAInfo> lras, String lraId) {
-        return lras.stream().anyMatch(lrainfo -> lrainfo.getLraId().equals(lraId));
-    }
-
     /**
      * The started LRA will be named based on the class name and the running test name.
      */