Repository: eclipse/microprofile-lra (java)

Issue:
TCK: Replace LRAClientOps#isLRAFinished with afterLRA calls
As per Gitter discussion, replace the current way of checking of active LRA in the TCK (try enlisting) with direct SPI.

Pull request description:
#298 Replace LRAClientOps#isLRAFinished with afterLRA calls
resolves #298 

Patch:
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
index df4c3d0..0d37ea9 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java
@@ -42,9 +42,6 @@ import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingT
 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_EXISTING_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 {
     private static final Logger LOGGER = Logger.getLogger(TckLRATypeTests.class.getName());
@@ -59,28 +56,6 @@ public class LRAClientOps {
         this.lraTasks = new ConcurrentHashMap<>();
     }
 
-    // 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(URI lra) {
-        // call a JAX-RS endpoint that should result in the enlistment of a resource into the LRA
-        int status = invokeRestEndpointAndReturnStatus(lra, TCK_PARTICIPANT_RESOURCE_PATH, JOIN_WITH_EXISTING_LRA_PATH, 200);
-
-        if (status == 200) {
-            // leave the LRA otherwise any tests checking completion/compensation counts would fail
-
-            leaveLRA(lra, TCK_PARTICIPANT_RESOURCE_PATH, LEAVE_PATH);
-
-        }
-
-        return status;
-    }
-
-    boolean isLRAFinished(URI lra) {
-        // if the LRA has finished/finishing or does not exist 412 or 410 MUST be be reported
-        int status = tryToEnlistWithAnLRA(lra);
-
-        return status == Response.Status.GONE.getStatusCode() || status == Response.Status.PRECONDITION_FAILED.getStatusCode();
-    }
-
     // synchronize access to the connection since it is shared with the LRA background cancellation code
     private synchronized Response invokeRestEndpoint(URI lra, String basePath, String path, int coerceResponse) {
         WebTarget resourcePath = target.path(basePath).path(path).queryParam(STATUS_CODE_QUERY_NAME, coerceResponse);
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/AfterLRAListener.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/AfterLRAListener.java
index 0d37b4e..ff690aa 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/AfterLRAListener.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/AfterLRAListener.java
@@ -22,8 +22,7 @@ package org.eclipse.microprofile.lra.tck.participant.api;
 import org.eclipse.microprofile.lra.annotation.LRAStatus;
 import org.eclipse.microprofile.lra.annotation.AfterLRA;
 import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
-import org.eclipse.microprofile.lra.tck.service.LRAMetricService;
-import org.eclipse.microprofile.lra.tck.service.LRAMetricType;
+import org.eclipse.microprofile.lra.tck.service.LRATestService;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
@@ -44,14 +43,14 @@ import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_RECOVER
  */
 @ApplicationScoped
 @Path(AfterLRAListener.AFTER_LRA_LISTENER_PATH)
-public class AfterLRAListener {
+public class AfterLRAListener extends ResourceParent {
     public static final String AFTER_LRA_LISTENER_PATH = "after-lra-listener";
     public static final String AFTER_LRA_LISTENER_WORK = "work";
 
     private static final String AFTER_LRA = "/after";
 
     @Inject
-    private LRAMetricService lraMetricService;
+    private LRATestService lraTestService;
 
     @PUT
     @Path(AFTER_LRA_LISTENER_WORK)
@@ -65,21 +64,7 @@ public class AfterLRAListener {
     @Path(AFTER_LRA)
     @AfterLRA // this method will be called when the LRA associated with the method activityWithLRA finishes
     public Response afterEnd(@HeaderParam(LRA_HTTP_ENDED_CONTEXT_HEADER) URI lraId, LRAStatus status) {
-        switch (status) {
-            case Closed:
-                // FALLTHRU
-            case Cancelled:
-                // FALLTHRU
-            case FailedToCancel:
-                // FALLTHRU
-            case FailedToClose:
-                lraMetricService.incrementMetric(
-                        LRAMetricType.valueOf(status.name()),
-                        lraId,
-                        AfterLRAListener.class);
-                return Response.ok().build();
-            default:
-                return Response.status(Response.Status.BAD_REQUEST).build();
-        }
+        return lraTestService.processAfterLRAInfo(lraId, status, AfterLRAListener.class,
+            AFTER_LRA_LISTENER_PATH + AFTER_LRA);
     }
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/AfterLRAParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/AfterLRAParticipant.java
index 652d444..e8ec044 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/AfterLRAParticipant.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/AfterLRAParticipant.java
@@ -27,6 +27,7 @@ import org.eclipse.microprofile.lra.annotation.LRAStatus;
 import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
 import org.eclipse.microprofile.lra.tck.service.LRAMetricService;
 import org.eclipse.microprofile.lra.tck.service.LRAMetricType;
+import org.eclipse.microprofile.lra.tck.service.LRATestService;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
@@ -56,6 +57,9 @@ public class AfterLRAParticipant {
     @Inject
     private LRAMetricService lraMetricService;
 
+    @Inject
+    private LRATestService lraTestService;
+
     @PUT
     @Path("/complete")
     @Complete
@@ -84,34 +88,8 @@ public class AfterLRAParticipant {
     @PUT
     @Path(AFTER_LRA)
     @AfterLRA // this method will be called when the LRA associated with the method activityWithLRA finishes
-    public Response afterEnd(@HeaderParam(LRA_HTTP_ENDED_CONTEXT_HEADER) URI lraId,
-                             @HeaderParam(LRA_HTTP_RECOVERY_HEADER) URI recoverId,
-                             LRAStatus status) {
-
-        assertHeaderPresent(recoverId, LRA_HTTP_RECOVERY_HEADER);
-
-        switch (status) {
-            case Closed:
-                // FALLTHRU
-            case Cancelled:
-                // FALLTHRU
-            case FailedToCancel:
-                // FALLTHRU
-            case FailedToClose:
-                lraMetricService.incrementMetric(
-                        LRAMetricType.valueOf(status.name()),
-                        lraId,
-                        AfterLRAParticipant.class);
-                return Response.ok().build();
-            default:
-                return Response.status(Response.Status.BAD_REQUEST).build();
-        }
+    public Response afterEnd(@HeaderParam(LRA_HTTP_ENDED_CONTEXT_HEADER) URI lraId, LRAStatus status) {
+        return lraTestService.processAfterLRAInfo(lraId, status, AfterLRAParticipant.class,
+            AFTER_LRA_PARTICIPANT_PATH + AFTER_LRA);
     }
-
-    private void assertHeaderPresent(URI lraId, String headerName) {
-        if (lraId == null) {
-            throw new WrongHeaderException(String.format("%s: missing '%s' header", AFTER_LRA_PARTICIPANT_PATH + AFTER_LRA, headerName));
-        }
-    }
-
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckInterface.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckInterface.java
index dddbfdc..81d0174 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckInterface.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckInterface.java
@@ -19,8 +19,6 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.participant.api;
 
-import org.eclipse.microprofile.lra.annotation.AfterLRA;
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
 import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
 
 import javax.ws.rs.GET;
@@ -106,7 +104,4 @@ public interface LRATypeTckInterface {
     @Path(NEVER_WITH_END_FALSE_PATH)
     @LRA(value = LRA.Type.NEVER, end = false)
     Response neverEndLRA(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId);
-
-    @AfterLRA
-    void afterLRA(URI lraId, LRAStatus status);
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckInterfaceResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckInterfaceResource.java
index 5a9e549..96c85aa 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckInterfaceResource.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckInterfaceResource.java
@@ -19,14 +19,12 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.participant.api;
 
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
-
 import javax.ws.rs.Path;
 import javax.ws.rs.core.Response;
 import java.net.URI;
 
 @Path(LRATypeTckInterfaceResource.TCK_LRA_TYPE_INTERFACE_RESOURCE_PATH)
-public class LRATypeTckInterfaceResource implements LRATypeTckInterface {
+public class LRATypeTckInterfaceResource extends ResourceParent implements LRATypeTckInterface {
 
     public static final String TCK_LRA_TYPE_INTERFACE_RESOURCE_PATH = "lra-type-tck-interface-resource";
 
@@ -78,8 +76,4 @@ public class LRATypeTckInterfaceResource implements LRATypeTckInterface {
         return Response.ok(lraId).build();
     }
 
-    public void afterLRA(URI lraId, LRAStatus status) {
-        // no-op, required by the specification (see https://github.com/eclipse/microprofile-lra/pull/265)
-    }
-
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckResource.java
index ce9bb19..988c6aa 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckResource.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckResource.java
@@ -19,23 +19,20 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.participant.api;
 
-import org.eclipse.microprofile.lra.annotation.AfterLRA;
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
 import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
 
 import javax.enterprise.context.ApplicationScoped;
-import javax.ws.rs.HeaderParam;
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.Path;
 import javax.ws.rs.core.Response;
-
 import java.net.URI;
 
 import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_CONTEXT_HEADER;
 
 @ApplicationScoped
 @Path(LRATypeTckResource.TCK_LRA_TYPE_RESOURCE_PATH)
-public class LRATypeTckResource {
+public class LRATypeTckResource extends ResourceParent {
 
     public static final String TCK_LRA_TYPE_RESOURCE_PATH = "lra-type-tck-resource";
 
@@ -140,9 +137,4 @@ public class LRATypeTckResource {
     public Response neverEndLRA(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId) {
         return Response.ok(lraId).build();
     }
-
-    @AfterLRA
-    public void afterLRA(URI lraId, LRAStatus status) {
-        // no-op, required by the specification (see https://github.com/eclipse/microprofile-lra/pull/265)
-    }
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckSuperclass.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckSuperclass.java
index 60e3859..bb82d7f 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckSuperclass.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckSuperclass.java
@@ -19,8 +19,6 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.participant.api;
 
-import org.eclipse.microprofile.lra.annotation.AfterLRA;
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
 import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
 
 import javax.ws.rs.GET;
@@ -31,7 +29,7 @@ import java.net.URI;
 
 import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_CONTEXT_HEADER;
 
-public abstract class LRATypeTckSuperclass {
+public abstract class LRATypeTckSuperclass extends ResourceParent {
 
     public static final String REQUIRED_PATH = "/required";
     public static final String REQUIRES_NEW_PATH = "/requires-new";
@@ -106,9 +104,4 @@ public abstract class LRATypeTckSuperclass {
     @Path(NEVER_WITH_END_FALSE_PATH)
     @LRA(value = LRA.Type.NEVER, end = false)
     public abstract Response neverEndLRA(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraId);
-
-    @AfterLRA
-    public void afterLRA(URI lraId, LRAStatus status) {
-        // no-op, required by the specification (see https://github.com/eclipse/microprofile-lra/pull/265)
-    }
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckSuperclassResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckSuperclassResource.java
index a3fd819..221380a 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckSuperclassResource.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRATypeTckSuperclassResource.java
@@ -19,8 +19,6 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.participant.api;
 
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
-
 import javax.ws.rs.Path;
 import javax.ws.rs.core.Response;
 import java.net.URI;
@@ -77,8 +75,4 @@ public class LRATypeTckSuperclassResource extends LRATypeTckSuperclass {
     public Response neverEndLRA(URI lraId) {
         return Response.ok(lraId).build();
     }
-
-    public void afterLRA(URI lraId, LRAStatus status) {
-        // no-op, required by the specification (see https://github.com/eclipse/microprofile-lra/pull/265)
-    }
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRAUnknownResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRAUnknownResource.java
index e66bf77..f10e0d2 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRAUnknownResource.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LRAUnknownResource.java
@@ -19,10 +19,8 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.participant.api;
 
-import org.eclipse.microprofile.lra.annotation.AfterLRA;
 import org.eclipse.microprofile.lra.annotation.Compensate;
 import org.eclipse.microprofile.lra.annotation.Complete;
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
 import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
 import org.eclipse.microprofile.lra.tck.service.LRAMetricService;
 import org.eclipse.microprofile.lra.tck.service.LRAMetricType;
@@ -43,14 +41,13 @@ import java.util.Map;
 import java.util.logging.Logger;
 
 import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_CONTEXT_HEADER;
-import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_ENDED_CONTEXT_HEADER;
 
 /**
  * TCK Tests related to the 410 status code handling. Version without a Status method.
  */
 @ApplicationScoped
 @Path(LRAUnknownResource.LRA_CONTROLLER_PATH)
-public class LRAUnknownResource {
+public class LRAUnknownResource extends ResourceParent {
     public static final String LRA_CONTROLLER_PATH = "lraUnknownController";
     public static final String TRANSACTIONAL_WORK_PATH = "work";
 
@@ -127,28 +124,4 @@ public class LRAUnknownResource {
         LOGGER.info(String.format("LRA id '%s' was compensated", lraId));
         return Response.status(responseCode).build();
     }
-
-    @PUT
-    @Path(AFTER_LRA)
-    @AfterLRA // this method will be called when the LRA associated with the method activityWithLRA finishes
-    public Response afterEnd(@HeaderParam(LRA_HTTP_ENDED_CONTEXT_HEADER) URI lraId, LRAStatus status) {
-        lraMetricService.incrementMetric(LRAMetricType.AfterLRA, lraId, LRAUnknownResource.class);
-        switch (status) {
-            case Closed:
-                // FALLTHRU
-            case Cancelled:
-                // FALLTHRU
-            case FailedToCancel:
-                // FALLTHRU
-            case FailedToClose:
-                lraMetricService.incrementMetric(
-                        LRAMetricType.valueOf(status.name()),
-                        lraId,
-                        LRAUnknownResource.class);
-                return Response.ok().build();
-            default:
-                return Response.status(Response.Status.BAD_REQUEST).build();
-        }
-    }
-
 }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraResource.java
index 79ddc22..f80b954 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraResource.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraResource.java
@@ -68,7 +68,7 @@ import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_RECOVER
 @ApplicationScoped
 @Path(LraResource.LRA_RESOURCE_PATH)
 @LRA(value = LRA.Type.SUPPORTS, end = false)
-public class LraResource {
+public class LraResource extends ResourceParent {
     public static final String LRA_RESOURCE_PATH = "lraresource";
     public static final String TRANSACTIONAL_WORK_PATH = "work";
     public static final String ACCEPT_WORK = "acceptWork";
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
index 49e0792..64478ef 100644
--- 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
@@ -19,8 +19,6 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.participant.api;
 
-import org.eclipse.microprofile.lra.annotation.AfterLRA;
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
 import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
 import org.eclipse.microprofile.lra.tck.LRAClientOps;
 
@@ -43,7 +41,7 @@ import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_CONTEXT
 @ApplicationScoped
 @Path(NonParticipatingTckResource.TCK_NON_PARTICIPANT_RESOURCE_PATH)
 @LRA(value = LRA.Type.SUPPORTS, end = false)
-public class NonParticipatingTckResource {
+public class NonParticipatingTckResource extends ResourceParent {
     public static final String TCK_NON_PARTICIPANT_RESOURCE_PATH = "non-participating-tck-resource";
 
     public static final String START_AND_END_PATH = "/start-and-end";
@@ -128,11 +126,6 @@ public class NonParticipatingTckResource {
                 200)).build();
     }
 
-    @AfterLRA
-    public void afterLRA(URI lraId, LRAStatus status) {
-        // no-op, required by the specification (see https://github.com/eclipse/microprofile-lra/pull/265)
-    }
-
     private String invokeRestEndpoint(URI lra, String basePath, String path, int coerceResponse) {
         Client client = ClientBuilder.newClient();
 
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ResourceParent.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ResourceParent.java
new file mode 100644
index 0000000..be9bd9a
--- /dev/null
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ResourceParent.java
@@ -0,0 +1,61 @@
+/*
+ *******************************************************************************
+ * Copyright (c) 2020 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.AfterLRA;
+import org.eclipse.microprofile.lra.annotation.LRAStatus;
+import org.eclipse.microprofile.lra.tck.service.LRAMetricService;
+import org.eclipse.microprofile.lra.tck.service.LRAMetricType;
+import org.eclipse.microprofile.lra.tck.service.LRATestService;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_ENDED_CONTEXT_HEADER;
+
+@ApplicationScoped
+public class ResourceParent {
+
+    @Inject
+    private LRATestService lraTestService;
+
+    @Inject
+    private LRAMetricService lraMetricService;
+
+    @Context
+    private UriInfo context;
+
+    @PUT
+    @Path("/after")
+    @AfterLRA
+    public Response afterLRA(@HeaderParam(LRA_HTTP_ENDED_CONTEXT_HEADER) URI endedLRAId, LRAStatus status) {
+        Response response = lraTestService.processAfterLRAInfo(endedLRAId, status, this.getClass(), context.getPath());
+        lraMetricService.incrementMetric(LRAMetricType.AfterLRA, endedLRAId, this.getClass());
+        return response;
+    }
+
+}
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRAMetricAssertions.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRAMetricAssertions.java
index 67c8b7c..f2fac77 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRAMetricAssertions.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRAMetricAssertions.java
@@ -38,6 +38,9 @@ public final class LRAMetricAssertions {
     @Inject
     private LRAMetricService lraMetricService;
 
+    @Inject
+    private LRATestService lraTestService;
+
     // ----------------------------- COMPENSATED -----------------------------------
     /**
      * Asserts that <b>compensated</b> was called for given LRA and participant class translated to fully qualified classname as String,
@@ -326,7 +329,7 @@ public final class LRAMetricAssertions {
      * @param participantClazz  the participant class used as resource name in the map
      */
     public void assertFinished(String message, URI lraId, Class<?> participantClazz) {
-        assertTrue(message, lraMetricService.isLRAFinished(lraId, participantClazz));
+        assertTrue(message, lraTestService.isLRAFinished(lraId, participantClazz.getName()));
     }
 
     private void assertYes(String message, LRAMetricType metricType, URI lraId, Class<?> participantClazz) {
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRAMetricService.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRAMetricService.java
index 04626b4..b2bfd55 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRAMetricService.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRAMetricService.java
@@ -19,8 +19,6 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.service;
 
-import org.eclipse.microprofile.lra.annotation.LRAStatus;
-
 import javax.enterprise.context.ApplicationScoped;
 import java.net.URI;
 import java.util.Arrays;
@@ -70,19 +68,37 @@ public class LRAMetricService {
         return result.get();
     }
 
+    /**
+     * Returns count number for particular metric type.
+     *
+     * @param metricType counter for which the metric type will be returned
+     * @param lraId counter for which lra id will be returned
+     * @return metric counter defined based on the method parameters
+     */
+    public int getMetric(LRAMetricType metricType, URI lraId) {
+        if (metricsPerLra.containsKey(lraId)) {
+            return metricsPerLra.get(lraId).values().stream()
+                .mapToInt(lraMetric -> lraMetric.get(metricType))
+                .sum();
+        } else {
+            return 0;
+        }
+    }
+
     /**
      * Returns count number for particular metric type filtered by LRA id and the participant class
      * which defines the participant name (the fully qualified class name is used for it).
      *
-     * @param metricType  counter for which the metric type will be returned
-     * @param lraId  counter for which lra id will be returned
-     * @param participantClazz  counter for which the participant will be returned
+     * @param metricType counter for which the metric type will be returned
+     * @param lraId counter for which lra id will be returned
+     * @param participantClazz counter for which the participant will be returned
      * @return metric counter defined based on the method parameters
      */
     public int getMetric(LRAMetricType metricType, URI lraId, Class<?> participantClazz) {
         return getMetric(metricType, lraId, participantClazz.getName());
     }
 
+
     /**
      * Returns count number for particular metric type filtered by LRA id and the participant's name.
      * It's expected that the participant name is defined as fully qualified participant class name.
@@ -106,31 +122,7 @@ public class LRAMetricService {
     public void clear() {
         metricsPerLra.clear();
     }
-
-    /**
-     * TODO if the current PR is acceptable then delete the old isLRAFinished method
-     * (which tests whether an LRA is active by making an attempt to enlist with it
-     * which some spec implementations report as a stack trace WARNING if the LRA is
-     * no longer active).
-     *
-     * @param lraId the LRA id to test
-     * @param participantClazz class of the participant that the metrics parameter applies to
-     * @return whether or not an LRA has finished
-     */
-    boolean isLRAFinished(URI lraId, Class<?> participantClazz) {
-        LRAStatus metricStatus = null;
-        if (getMetric(LRAMetricType.Closed, lraId, participantClazz) >= 1) {
-            metricStatus = LRAStatus.Closed;
-        } else if (getMetric(LRAMetricType.FailedToClose, lraId, participantClazz) >= 1) {
-            metricStatus = LRAStatus.FailedToClose;
-        } else if (getMetric(LRAMetricType.Cancelled, lraId, participantClazz) >= 1) {
-            metricStatus = LRAStatus.Cancelled;
-        } else if (getMetric(LRAMetricType.FailedToCancel, lraId, participantClazz) >= 1) {
-            metricStatus = LRAStatus.FailedToCancel;
-        }
-        return metricStatus != null;
-    }
-
+    
     /**
      * A class to hold all of the metrics gathered in the context of a single LRA.
      * We need stats per LRA since a misbehaving test may leave an LRA in need of


Test patch:
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java
index ff0795d..eacfc25 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java
@@ -616,11 +616,11 @@ public class TckLRATypeTests extends TckTestBase {
                 // validate that the method ran with an LRA and that it is still active
                 assertNotEquals(testName.getMethodName() + ": Resource method should not have run with an LRA: " + methodLraId,
                         0, methodLraId.length());
-                assertFalse(lraClient.isLRAFinished(URI.create(methodLraId)));
+                assertFalse(lraTestService.isLRAFinished(URI.create(methodLraId)));
                 lraClient.closeLRA(methodLraId);
             } else if (methodLraId.length() != 0) {
                 // otherwise it should be finished
-                assertTrue(lraClient.isLRAFinished(URI.create(methodLraId)));
+                assertTrue(lraTestService.isLRAFinished(URI.create(methodLraId)));
             }
 
             if (lra != null) {
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
index b5af593..2505283 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
@@ -56,7 +56,7 @@ public class TckTestBase {
     private LraTckConfigBean config;
     
     @Inject
-    private LRATestService lraTestService;
+    LRATestService lraTestService;
 
     @Inject
     private LRAMetricService lraMetricService;
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 a4fc0b8..16be2fa 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
@@ -100,7 +100,7 @@ public class TckTests extends TckTestBase {
             lraClient.cancelLRA(lra);
 
             assertTrue("LRA '" + lra + "' should not be active but is not marked as finished",
-                    lraClient.isLRAFinished(lra));
+                    lraTestService.isLRAFinished(lra));
         } catch (GenericLRAException e) {
             e.printStackTrace();
             throw e;
@@ -120,7 +120,7 @@ public class TckTests extends TckTestBase {
         lraClient.closeLRA(lra);
 
         assertTrue("LRA '" + lra + "' should not be active anymore but is not marked as finished",
-                lraClient.isLRAFinished(lra));
+                lraTestService.isLRAFinished(lra));
     }
 
     @Test
@@ -154,7 +154,7 @@ public class TckTests extends TckTestBase {
 
             // the resource /activities/work is annotated with Type.REQUIRED so the container should have ended it
             assertTrue("Nested LRA id '" + lra + "' should be listed in the list of the active LRAs (from call to "
-                    + resourcePath.getUri() + ")", lraClient.isLRAFinished(nestedLraId));
+                    + resourcePath.getUri() + ")", lraTestService.isLRAFinished(nestedLraId));
         } finally {
             if(response != null) {
                 response.close();
@@ -189,7 +189,7 @@ public class TckTests extends TckTestBase {
 
         // validate that the implementation still knows about lraId
         assertFalse("LRA '" + lra + "' should be active as it is not closed yet but it is marked as finished",
-                lraClient.isLRAFinished(lra));
+                lraTestService.isLRAFinished(lra));
 
         // close the LRA
         lraClient.closeLRA(lra);
@@ -202,7 +202,7 @@ public class TckTests extends TckTestBase {
 
         // check that implementation no longer knows about lraId
         assertTrue("LRA '" + lra + "' should not be active anymore as it was closed yet but is not marked as finished",
-            lraClient.isLRAFinished(lra));
+            lraTestService.isLRAFinished(lra));
     }
 
     @Test
@@ -214,7 +214,7 @@ public class TckTests extends TckTestBase {
         checkStatusAndCloseResponse(Response.Status.OK, response, resourcePath);
         lraClient.closeLRA(lra);
         assertTrue("LRA '" + lra + "' should be active as it is not closed yet but it is marked as finished",
-                lraClient.isLRAFinished(lra));
+                lraTestService.isLRAFinished(lra));
     }
 
     /**
@@ -404,7 +404,7 @@ public class TckTests extends TckTestBase {
             lraMetric.assertNotCompleted(String.format("acceptTest with cancel: participant (%s) was asked to complete",
                     resourcePath.getUri()), lra, LraResource.class);
         }
-        assertTrue("acceptTest: LRA " + lra + " did not finish", lraClient.isLRAFinished(lra));
+        assertTrue("acceptTest: LRA " + lra + " did not finish", lraTestService.isLRAFinished(lra));
     }
 
     @Test
@@ -602,7 +602,7 @@ public class TckTests extends TckTestBase {
 
         // and the mandatory lra seen by the multiLevelNestedActivity method
         assertFalse("multiLevelNestedActivity: top level LRA should be active (path called " + resourcePath.getUri() + ")",
-                lraClient.isLRAFinished(URI.create( lraArray[0])));
+                lraTestService.isLRAFinished(URI.create(lraArray[0])));
 
         lraTestService.waitForCallbacks(lra);
 
@@ -647,7 +647,7 @@ public class TckTests extends TckTestBase {
         IntStream.rangeClosed(0, nestedCnt).forEach(i -> assertTrue(
                 String.format("multiLevelNestedActivity: %s LRA still active (resource path was %s)",
                         (i == 0 ? "top level" : "nested"), resourcePath.getUri()),
-                lraClient.isLRAFinished(URI.create(lraArray[i]))));
+                lraTestService.isLRAFinished(URI.create(lraArray[i]))));
 
         lraTestService.waitForCallbacks(lra);
 
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRATestService.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRATestService.java
index fb28f97..259aabc 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRATestService.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/service/LRATestService.java
@@ -19,15 +19,19 @@
  *******************************************************************************/
 package org.eclipse.microprofile.lra.tck.service;
 
+import org.eclipse.microprofile.lra.annotation.LRAStatus;
 import org.eclipse.microprofile.lra.tck.LRAClientOps;
+import org.eclipse.microprofile.lra.tck.participant.api.WrongHeaderException;
 import org.eclipse.microprofile.lra.tck.service.spi.LRACallbackException;
 import org.eclipse.microprofile.lra.tck.service.spi.LRARecoveryService;
 import org.junit.Assert;
 
 import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
 import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
 import java.net.URI;
 import java.net.URL;
 import java.util.Iterator;
@@ -35,6 +39,8 @@ import java.util.ServiceLoader;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import static org.eclipse.microprofile.lra.annotation.ws.rs.LRA.LRA_HTTP_ENDED_CONTEXT_HEADER;
+
 @ApplicationScoped
 public class LRATestService {
     private static final Logger LOG = Logger.getLogger(LRATestService.class.getName());
@@ -47,6 +53,9 @@ public class LRATestService {
 
     private LRARecoveryService lraRecoveryService = loadService(LRARecoveryService.class);
 
+    @Inject
+    private LRAMetricService lraMetricService;
+
     public void start(URL deploymentURL) {
         tckSuiteClient = ClientBuilder.newClient();
         tckSuiteTarget = tckSuiteClient.target(URI.create(deploymentURL.toExternalForm()));
@@ -105,4 +114,59 @@ public class LRATestService {
 
         return iterator.next();
     }
+
+    public void assertHeaderPresent(URI lraId, String path, String headerName) {
+        if (lraId == null) {
+            throw new WrongHeaderException(String.format("%s: missing '%s' header", path, headerName));
+        }
+    }
+
+    public Response processAfterLRAInfo(URI endedLRAId, LRAStatus status, Class<?> resourceClass, String path) {
+        assertHeaderPresent(endedLRAId, path, LRA_HTTP_ENDED_CONTEXT_HEADER);
+
+        switch (status) {
+            case Closed:
+                // FALLTHRU
+            case Cancelled:
+                // FALLTHRU
+            case FailedToCancel:
+                // FALLTHRU
+            case FailedToClose:
+                lraMetricService.incrementMetric(
+                    LRAMetricType.valueOf(status.name()),
+                    endedLRAId,
+                    resourceClass);
+                return Response.ok().build();
+            default:
+                return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+
+    }
+
+    /**
+     * Returns whether the passed LRA and resource are in finished state.
+     *
+     * @param lra the LRA to test
+     * @param resourceName name of the resource that the metrics parameter applies to
+     * @return whether or not an LRA has finished
+     */
+    public boolean isLRAFinished(URI lra, String resourceName) {
+        return lraMetricService.getMetric(LRAMetricType.Closed, lra, resourceName) > 0 ||
+            lraMetricService.getMetric(LRAMetricType.FailedToClose, lra, resourceName) > 0 ||
+            lraMetricService.getMetric(LRAMetricType.Cancelled, lra, resourceName) > 0 ||
+            lraMetricService.getMetric(LRAMetricType.FailedToCancel, lra, resourceName) > 0;
+    }
+
+    /**
+     * Returns whether the passed LRA is in finished state.
+     *
+     * @param lra the LRA to test
+     * @return whether or not an LRA has finished
+     */
+    public boolean isLRAFinished(URI lra) {
+        return lraMetricService.getMetric(LRAMetricType.Closed, lra) > 0 ||
+            lraMetricService.getMetric(LRAMetricType.FailedToClose, lra) > 0 ||
+            lraMetricService.getMetric(LRAMetricType.Cancelled, lra) > 0 ||
+            lraMetricService.getMetric(LRAMetricType.FailedToCancel, lra) > 0;
+    }
 }