From 430e04ed10d701e36e18176837cc6bd1900926ed Mon Sep 17 00:00:00 2001 From: xstefank Date: Tue, 30 Apr 2019 18:48:02 +0200 Subject: [PATCH] issue35 Do not define JAX-RS annotations on @Compensate methods resolves #35 Signed-off-by: xstefank --- .../lra/annotation/Compensate.java | 19 +- .../microprofile/lra/annotation/Complete.java | 20 +- .../microprofile/lra/annotation/Forget.java | 24 ++- .../microprofile/lra/annotation/Status.java | 29 ++- ...alidLRAParticipantDefinitionException.java | 38 ++++ .../main/asciidoc/microprofile-lra-spec.adoc | 175 +++++++++++++++--- .../TckInvalidParticipantSignaturesTests.java | 123 ++++++++++++ .../microprofile/lra/tck/TckLRATypeTests.java | 8 +- .../lra/tck/TckParticipantTests.java | 125 +++++++++++++ .../microprofile/lra/tck/TckTestBase.java | 6 +- .../InvalidArgumentTypesParticipant.java | 54 ++++++ .../InvalidReturnTypeParticipant.java | 46 +++++ .../nonjaxrs/TooManyArgsParticipant.java | 54 ++++++ .../nonjaxrs/ValidLRAParticipant.java | 172 +++++++++++++++++ 14 files changed, 837 insertions(+), 56 deletions(-) create mode 100644 api/src/main/java/org/eclipse/microprofile/lra/participant/InvalidLRAParticipantDefinitionException.java create mode 100644 tck/src/main/java/org/eclipse/microprofile/lra/tck/TckInvalidParticipantSignaturesTests.java create mode 100644 tck/src/main/java/org/eclipse/microprofile/lra/tck/TckParticipantTests.java create mode 100644 tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidArgumentTypesParticipant.java create mode 100644 tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidReturnTypeParticipant.java create mode 100644 tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/TooManyArgsParticipant.java create mode 100644 tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/ValidLRAParticipant.java diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java index 87d285b4..b56dbc2a 100644 --- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java +++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java @@ -35,11 +35,24 @@ * will be chosen). The spec makes no guarantees about when it will be invoked, * just that is will eventually be called. * - * The id of the currently running LRA can be obtained by inspecting the incoming - * JAX-RS headers. If this LRA is nested then the parent LRA MUST be present - * in the header with the name + * If the annotated method is a JAX-RS resource method the id of the currently + * running LRA can be obtained by inspecting the incoming JAX-RS headers. If + * this LRA is nested then the parent LRA MUST be present in the header with the name * {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA#LRA_HTTP_PARENT_CONTEXT_HEADER}. * + * If the annotated method is not a JAX-RS resource method the id of the currently + * running LRA can be obtained by adhering to a predefined method signature as + * defined in the LRA specification document. Similarly the method may determine + * whether or not it runs with a nested LRA by providing a parameter to hold the parent id. + * For example, + *
+ *     
+ *          @Compensate
+ *          public void compensate(URI lraId, URI parentId) { ...}
+ *     
+ * 
+ * would be a valid compensation method declaration. + * * Note that, according to the state model {@link LRAStatus} once an LRA has been * asked to cancel it is no longer possible to join with it as a participant. * Therefore combining this annotation with an `@LRA` annotation that does not diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java index 9be2901a..30fad764 100644 --- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java +++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java @@ -37,11 +37,25 @@ * If the annotation is present on more than one method then an arbitrary one * will be chosen. * - * The id of the currently running LRA can be obtained by inspecting the incoming - * JAX-RS headers. If this LRA is nested then the parent LRA MUST be present - * in the header with the name + * If the annotated method is a JAX-RS resource method the id of the currently + * running LRA can be obtained by inspecting the incoming JAX-RS headers. If + * this LRA is nested then the parent LRA MUST be present in the header with the name * {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA#LRA_HTTP_PARENT_CONTEXT_HEADER}. * + * If the annotated method is not a JAX-RS resource method the id of the currently + * running LRA can be obtained by adhering to a predefined method signature as + * defined in the LRA specification document. Similarly the method may determine + * whether or not it runs with a nested LRA by providing a parameter to hold the parent id. + * For example, + *
+ *     
+ *          @Complete
+ *          public void complete(URI lraId, URI parentId) { ...}
+ *     
+ * 
+ * would be a valid completion method declaration. + * + * * Note that, according to the state model {@link LRAStatus} once an LRA has been * asked to close it is no longer possible to join with it as a participant. * Therefore combining this annotation with an `@LRA` annotation that does not diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Forget.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Forget.java index 1b26e16e..aeb07dae 100644 --- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Forget.java +++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Forget.java @@ -33,9 +33,7 @@ * in progress) or because of a failure (ie will never be able to finish) * then it must remember the fact (by reporting it when asked for its' * {@link Status})) until explicitly told that it can clean - * up using this @Forget annotation. The annotated method - * must be a standard JAX-RS endpoint annotated with the JAX-RS - * @DELETE annotation. + * up using this @Forget annotation. * * A similar remark applies if the participant was enlisted in a * nested LRA {@link LRA#value()}. Actions performed in the context @@ -43,11 +41,25 @@ * is explicitly told it can clean up using this @Forget * annotation. * - * The id of the currently running LRA can be obtained by inspecting the - * incoming JAX-RS headers. If this LRA is nested then the parent LRA - * MUST be present in the header with the name + * If the annotated method is a JAX-RS resource method the id of the currently + * running LRA can be obtained by inspecting the incoming JAX-RS headers. If + * this LRA is nested then the parent LRA MUST be present in the header with the name * {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA#LRA_HTTP_PARENT_CONTEXT_HEADER}. * + * If the annotated method is not a JAX-RS resource method the id of the currently + * running LRA can be obtained by adhering to a predefined method signature as + * defined in the LRA specification document. Similarly the method may determine + * whether or not it runs with a nested LRA by providing a parameter to hold the parent id. + * For example, + *
+ *     
+ *          @Forget
+ *          public void forget(URI lraId, URI parentId) { ...}
+ *     
+ * 
+ * would be a valid forget method declaration. + * + * * Since the participant generally needs to know the id of the LRA in order * to clean up there is generally no benefit to combining this annotation * with the `@LRA` annotation (though it is not prohibited). diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java index a6160f3a..726a55a8 100644 --- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java +++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java @@ -36,18 +36,33 @@ * or compensation notification but if the participant (the class that * contains the Compensate and Complete annotations) does not * support idempotency then it must be able to report its' status by - * by annotating one of the methods with this @Status annotation - * together with the @GET JAX-RS annotation. + * by annotating one of the methods with this @Status. * The annotated method should report the status according to one of the * {@link ParticipantStatus} enum values. * + * If the annotated method is a JAX-RS resource method the id of the currently + * running LRA can be obtained by inspecting the incoming JAX-RS headers. If + * this LRA is nested then the parent LRA MUST be present in the header with the name + * {@link org.eclipse.microprofile.lra.annotation.ws.rs.LRA#LRA_HTTP_PARENT_CONTEXT_HEADER}. + * + * If the annotated method is not a JAX-RS resource method the id of the currently + * running LRA can be obtained by adhering to a predefined method signature as + * defined in the LRA specification document. Similarly the method may determine + * whether or not it runs with a nested LRA by providing a parameter to hold the parent id. + * For example, + *
+ *     
+ *          @Status
+ *          public void status(URI lraId, URI parentId) { ...}
+ *     
+ * 
+ * would be a valid status method declaration. + * * If the participant has already responded successfully to an invocation * of the @Compensate or @Complete method then it may - * report 404 Not Found HTTP status code. This enables the - * participant to free up resources. - * - * The id of the currently running LRA can be obtained by inspecting the - * incoming JAX-RS headers. + * report 404 Not Found HTTP status code or in case of + * non-JAX-RS method to return null. + * This enables the participant to free up resources. * * Since the participant generally needs to know the id of the LRA in order * to report its status there is generally no benefit to combining this diff --git a/api/src/main/java/org/eclipse/microprofile/lra/participant/InvalidLRAParticipantDefinitionException.java b/api/src/main/java/org/eclipse/microprofile/lra/participant/InvalidLRAParticipantDefinitionException.java new file mode 100644 index 00000000..d92fb7ba --- /dev/null +++ b/api/src/main/java/org/eclipse/microprofile/lra/participant/InvalidLRAParticipantDefinitionException.java @@ -0,0 +1,38 @@ +/* + ******************************************************************************* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package org.eclipse.microprofile.lra.participant; + +/** + * Runtime exception thrown when invalid non-JAX-RS LRA signature definition is detected + */ +public class InvalidLRAParticipantDefinitionException extends RuntimeException { + + public InvalidLRAParticipantDefinitionException(String message) { + super(message); + } + + public InvalidLRAParticipantDefinitionException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidLRAParticipantDefinitionException(Throwable cause) { + super(cause); + } +} diff --git a/spec/src/main/asciidoc/microprofile-lra-spec.adoc b/spec/src/main/asciidoc/microprofile-lra-spec.adoc index 742af9e5..6453f009 100644 --- a/spec/src/main/asciidoc/microprofile-lra-spec.adoc +++ b/spec/src/main/asciidoc/microprofile-lra-spec.adoc @@ -506,9 +506,9 @@ the method finishes and the `end = true` element on the confirmTrip method force ==== Compensating Activities The application developer indicates which JAX-RS method to use for a compensating -activity by annotating it with both the `@Compensate` and the JAX-RS `@PUT` annotations. +activity by annotating it with the `@Compensate` annotation. Whenever the associated resource is invoked in the context of -an LRA the endpoint corresponding to the `@Compensate` method MUST be enlisted with +an LRA the method corresponding to the `@Compensate` method MUST be enlisted with the LRA. If the LRA is subsequently cancelled then the compensation method MUST be invoked. The specification does not mandate when this method is invoked @@ -530,17 +530,17 @@ An example of how to use this annoation is: } ---- -The resource class MAY also contain a method marked with the `@Complete` and the -JAX-RS `@PUT` annotations. If such a method is present then the method MUST -be invoked when the associated LRA is closed. Again, the specification does -not MANDATE when the method is called, just that it will eventually be invoked. -Typically the resource would use this call to perform any clean up actions. The -method is optional since such clean up actions may not be necessary, -for example consider a system that just tracks hotel reservations and has operations -for booking a room or cancelling the reservation (`@Compensate`). Since this system -is passive, once a room is booked, it does not make any difference if the LRA is -completed or not: the room will be unavailable for others. If it receives a call -to `@Compensate` then it will free the room. But it won't do anything on `@complete`. +The resource class MAY also contain a method marked with the `@Complete` annotation. +If such a method is present then the method MUST be invoked when the associated LRA +is closed. Again, the specification does not MANDATE when the method is called, +just that it will eventually be invoked. Typically the resource would use this call +to perform any clean up actions. The method is optional since such clean up actions +may not be necessary, for example consider a system that just tracks hotel reservations +and has operations for booking a room or cancelling the reservation (`@Compensate`). +Since this system is passive, once a room is booked, it does not make any difference +if the LRA is completed or not: the room will be unavailable for others. If it receives +a call to `@Compensate` then it will free the room. But it won't do anything on +`@Complete`. If the participant successfully compensates or completes then it may forget about the LRA. Otherwise it should remember that it is still associated with the LRA and @@ -555,11 +555,143 @@ of these methods. If it knows it will never be able to compensate or complete th it MUST remember the fact until explicitly told that it can clean up by providing a method annotated with `@Forget` (the requirement is marked MUST because message delivery is not guaranteed in a distributed system). -The forget method MUST be annotated with the JAX-RS `@DELETE` annotation. If there is no `@Status` then the `@Compensate` or `@Complete` methods will continue to be invoked until the implementation knows it has the final status. +If the `@Compensate` or `@Complete` annotation is present on multiple methods +then an arbitrary one is chosen. + +The javadoc for the <> provides +more details about this annotation. + +Similarly, the javadoc for the <> +provides details about the `@Complete` annotation. + +==== Participant marker annotations method signatures + +The participant marker annotations are annotations that allow users to mark a method +for the execution by the LRA implemenatation according to the <>. These annotations are: + +* `@Compensate` -- a method to be executed when the LRA is cancelled +* `@Complete` -- a method to be executed when the LRA is completed +* `@Status` -- a method that allow user to state status of the participant with regards +to a paricular LRA +* `@Forget` -- a method to be executed when the LRA allows participant to +clear all associated information + +This specification is generally expected to be associated with the JAX-RS runtime. However, +we acknowledge that the mapping to JAX-RS resources may not always be feasible which is +why we distinguish two types of participant method definitions -- associated with the +JAX-RS resource method or not bound to JAX-RS. + +[[jaxrs-participant-methods]] +===== JAX-RS participant methods + +The following table presents expectations that are placed on individual participant +annotations when associated with JAX-RS resource methods: + +[[jaxrs-response-table]] +|=== +| Annotation | HTTP method | Expected status codes | Response + +| `@Compensate` +| PUT +| 200, 202, 404, 412 +| <> + +| `@Complete` +| PUT +| 200, 202, 404, 412 +| <> + +| `@Status` +| GET +| 200, 202, 404, 412 +| <> + +| `@Forget` +| DELETE +| 200, 404, 412 +| no expectations + +|=== + +If the participant annotation is not accompanied by an associated JAX-RS HTTP method +annotation according to the <>, the error SHOULD be reported using a JAX-RS +exception mapper that maps to a `412 Precondition Failed` HTTP status code. + +[[non-jaxrs-participant-methods]] +===== Non-JAX-RS participant methods + +When the participant annotations are applied to the non-JAX-RS resource methods they +MUST adhere to these predefined signatures: + +* *Return type*: +** `void`: successfull execution is mapped to `Compensated` or `Completed` participant statuses, + error execution is handled by exception mapping +*** applicable only for `@Compensate` and `@Complete` participant methods +** <> +** `javax.ws.rs.core.Response`: handled similarly as for +<> +** `java.util.concurrent.CompletionStage`: with the parameter of any of the previously +defined types +* *Arguments*: up to 2 arguments of types in this order: +** `java.net.URI`: representing current LRA context identification +** `java.net.URI`: representing potentional parent LRA context identification + +Declaring more than two arguments, different types of arguments or different return type +for any non-JAX-RS method annotatated with the participant marker annotation MUST result +in the deployment time `InvalidLRAParticipantDefinitionException`. +Please note that both arguments are optional but the order is required. + +Examples of valid signatures: + +[source,java] +---- +@Compensate +public void compensate(URI lraId, URI parentId) + +@Complete +public Response complete(URI lraId) + +@Status +public CompletionStage status(URI lraId) +---- + +Examples of invalid signatures: + +[source,java] +---- +@Compensate +public void compensate(String lraId, String parentId) // invalid types of arguments + +@Compensate +public String compensate(URI lraId) // invalid return type + +@Forget +public void forget(URI lraId, URI parentId, String additional) // too many arguments +---- + +If any of the described methods throws an exception, we distinguish two cases depending +on the exception type: + +* `WebApplicationException` -- the exception is mapped to the HTTP response it carries +and then handled as defined in the section +<> +* *any other exception* results into `FailedToComplete` or `FailedToCompensate` +participant states + + +footnote:[issue #15 (Provide an annotation to supply opaque data during +participant registration) allows participants to register opaque data when +joining with an LRA. This data is made available to the completion and +compensation callbacks] + +[[eventual-compensations]] +==== Eventual compensations + [[async-compensators]] If the resource cannot perform a compensation activity immediately the `@Compensate` method SHOULD do one or more of the following: @@ -573,21 +705,6 @@ The `@Status` method, if present, MUST report the progress of the compensation. Similarly if it cannot perform a completion activity immediately. -If the `@Compensate` or `@Complete` annotation is present on multiple methods -then an arbitrary one is chosen. If the annotation is not accompanied by -a JAX-RS `@PUT` annotation the error SHOULD be reported using a JAX-RS -exception mapper that maps to a `412 Precondition Failed` HTTP status code. - -footnote:[issue #15 (Provide an annotation to supply opaque data during -participant registration) allows participants to register opaque data when -joining with an LRA. This data is made available to the completion and -compensation callbacks] - -The javadoc for the <> provides -more details about this annotation. - -Similarly, the javadoc for the <> -provides details about the `@Complete` annotation. [[nesting-lras]] ==== Nesting LRAs diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckInvalidParticipantSignaturesTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckInvalidParticipantSignaturesTests.java new file mode 100644 index 00000000..e321a73a --- /dev/null +++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckInvalidParticipantSignaturesTests.java @@ -0,0 +1,123 @@ +/* + ******************************************************************************* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package org.eclipse.microprofile.lra.tck; + +import org.eclipse.microprofile.lra.participant.InvalidLRAParticipantDefinitionException; +import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.InvalidArgumentTypesParticipant; +import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.InvalidReturnTypeParticipant; +import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.TooManyArgsParticipant; +import org.jboss.arquillian.container.spi.client.container.DeploymentException; +import org.jboss.arquillian.container.test.api.Deployer; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; + +/** + *

+ * TCK that verifies that invalid non-JAX-RS participant method signatures are reported during deployment + *

+ * + *

+ * Each test deploys an archive containing single invalid participant containing an error in its participant + * method signature and expects that such deployment is aborted with {@link InvalidLRAParticipantDefinitionException} + * according to the specification. + *

+ */ +@RunWith(Arquillian.class) +public class TckInvalidParticipantSignaturesTests { + + private static final String INVALID_RETURN_TYPE_DEPLOYMENT = "nonjaxrs-return-type-deploy"; + private static final String TOO_MANY_ARGS_DEPLOYMENT = "too-many-args-deploy"; + private static final String INVALID_ARGUMENT_TYPE_DEPLOYMENT = "nonjaxrs-argument-type-deploy"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public DeploymentNameRule deploymentNameRule = new DeploymentNameRule(); + + @ArquillianResource + private Deployer deployer; + + @Deployment(name = INVALID_RETURN_TYPE_DEPLOYMENT, managed = false) + public static WebArchive deployInvalidReturnTypeParticipant() { + return createArchive(InvalidReturnTypeParticipant.class); + } + + @Deployment(name = TOO_MANY_ARGS_DEPLOYMENT, managed = false) + public static WebArchive deployTooManyArgsParticipant() { + return createArchive(TooManyArgsParticipant.class); + } + + @Deployment(name = INVALID_ARGUMENT_TYPE_DEPLOYMENT, managed = false) + public static WebArchive deployInvalidArgumentTypeParticipant() { + return createArchive(InvalidArgumentTypesParticipant.class); + } + + private static WebArchive createArchive(Class participantClass) { + String archiveName = participantClass.getSimpleName(); + return ShrinkWrap + .create(WebArchive.class, archiveName + ".war") + .addClasses(participantClass, JaxRsActivator.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @After + public void after() { + deployer.undeploy(deploymentNameRule.deploymentName); + } + + @Test + public void invalidReturnTypeInParticipantMethodTest() { + testInvalidDeployment(INVALID_RETURN_TYPE_DEPLOYMENT); + } + + @Test + public void tooManyArgsInParticipantMethodTest() { + testInvalidDeployment(TOO_MANY_ARGS_DEPLOYMENT); + } + + @Test + public void invalidArgumentTypeInParticipantMethodTest() { + testInvalidDeployment(INVALID_ARGUMENT_TYPE_DEPLOYMENT); + } + + private void testInvalidDeployment(String deploymentName) { + deploymentNameRule.deploymentName = deploymentName; + expectedException.expect(DeploymentException.class); + expectedException.expectMessage(InvalidLRAParticipantDefinitionException.class.getSimpleName()); + + deployer.deploy(deploymentName); + } + + private static final class DeploymentNameRule extends TestName { + + String deploymentName; + } +} diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckLRATypeTests.java index 87f42537..4092cb7f 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 @@ -23,8 +23,6 @@ import org.eclipse.microprofile.lra.tck.participant.api.Util; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.After; import org.junit.AfterClass; @@ -105,11 +103,7 @@ public class TckLRATypeTests { @Deployment(name = "lra-type-tck-tests") public static WebArchive deploy() { - String archiveName = TckLRATypeTests.class.getSimpleName().toLowerCase(); - return ShrinkWrap - .create(WebArchive.class, archiveName + ".war") - .addPackages(true, "org.eclipse.microprofile.lra.tck") - .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + return TckTestBase.deploy(TckLRATypeTests.class.getSimpleName().toLowerCase()); } @AfterClass diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckParticipantTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckParticipantTests.java new file mode 100644 index 00000000..d2c9cda6 --- /dev/null +++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckParticipantTests.java @@ -0,0 +1,125 @@ +/* + ******************************************************************************* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package org.eclipse.microprofile.lra.tck; + +import org.eclipse.microprofile.lra.tck.participant.nonjaxrs.ValidLRAParticipant; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.ws.rs.core.Response; + +import static org.junit.Assert.assertEquals; + +/** + * TCK to verify that valid non-JAX-RS participant method signatures are respected + */ +@RunWith(Arquillian.class) +public class TckParticipantTests extends TckTestBase { + + private static final String VALID_DEPLOYMENT = "valid-deploy"; + + private int beforeCompletedCount; + private int beforeCompensatedCount; + private int beforeStatusCount; + private int beforeForgetCount; + + @Deployment(name = VALID_DEPLOYMENT) + public static WebArchive deployValidParticipant() { + return TckTestBase.deploy(VALID_DEPLOYMENT) + .addClasses(ValidLRAParticipant.class); + } + + @Before + public void before() { + super.before(); + + beforeCompensatedCount = getCompensateCount(); + beforeCompletedCount = getCompletedCount(); + beforeStatusCount = getStatusCount(); + beforeForgetCount = getForgetCount(); + } + + @Test + public void validWebApplicationExceptionReturnedTest() { + Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH) + .path(ValidLRAParticipant.ENLIST_WITH_COMPLETE) + .request() + .get(); + + assertEquals("The 200 status response is expected", + Response.Status.OK.getStatusCode(), response.getStatus()); + assertEquals("Non JAX-RS @Complete method throwing WebApplicationException shoud have been called", + beforeCompletedCount + 1, getCompletedCount()); + assertEquals("@Compensate method should not have been called as LRA completed succesfully", + beforeCompensatedCount, getCompensateCount()); + + } + + @Test + public void validSignaturesChainTest() throws InterruptedException { + Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH) + .path(ValidLRAParticipant.ENLIST_WITH_COMPENSATE) + .request() + .get(); + + assertEquals("The 500 status response is expected", + Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus()); + assertEquals("Non JAX-RS @Compensate method should have been called", + beforeCompensatedCount + 1, getCompensateCount()); + assertEquals("@Complete method should not have been called as LRA compensated", + beforeCompletedCount, getCompletedCount()); + + replayEndPhase(ValidLRAParticipant.RESOURCE_PATH); + + assertEquals("Non JAX-RS @Status method should have been called", + beforeStatusCount + 1, getStatusCount()); + assertEquals("Non JAX-RS @Forget method should have been called", + beforeForgetCount + 1, getForgetCount()); + + } + + private int getCompletedCount() { + Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH) + .path(ValidLRAParticipant.COMPLETED_COUNT_PATH).request().get(); + return response.readEntity(Integer.class); + } + + private int getCompensateCount() { + Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH) + .path(ValidLRAParticipant.COMPENSATED_COUNT_PATH).request().get(); + return response.readEntity(Integer.class); + } + + private int getStatusCount() { + Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH) + .path(ValidLRAParticipant.STATUS_COUNT_PATH).request().get(); + return response.readEntity(Integer.class); + } + + private int getForgetCount() { + Response response = tckSuiteTarget.path(ValidLRAParticipant.RESOURCE_PATH) + .path(ValidLRAParticipant.FORGET_COUNT_PATH).request().get(); + return response.readEntity(Integer.class); + } +} diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java index 8195a290..00c061b1 100644 --- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java +++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java @@ -20,6 +20,8 @@ package org.eclipse.microprofile.lra.tck; import org.eclipse.microprofile.lra.annotation.ws.rs.LRA; +import org.eclipse.microprofile.lra.tck.participant.activity.Activity; +import org.eclipse.microprofile.lra.tck.participant.api.LraController; import org.eclipse.microprofile.lra.tck.participant.api.Util; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; @@ -64,7 +66,9 @@ public class TckTestBase { static WebArchive deploy(String archiveName) { return ShrinkWrap .create(WebArchive.class, archiveName + ".war") - .addPackages(true, "org.eclipse.microprofile.lra.tck") + .addPackages(false, TckTestBase.class.getPackage(), + Activity.class.getPackage(), + LraController.class.getPackage()) .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); } diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidArgumentTypesParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidArgumentTypesParticipant.java new file mode 100644 index 00000000..d5e40abf --- /dev/null +++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidArgumentTypesParticipant.java @@ -0,0 +1,54 @@ +/* + ******************************************************************************* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package org.eclipse.microprofile.lra.tck.participant.nonjaxrs; + +import org.eclipse.microprofile.lra.annotation.Compensate; +import org.eclipse.microprofile.lra.annotation.Forget; +import org.eclipse.microprofile.lra.annotation.ParticipantStatus; +import org.eclipse.microprofile.lra.annotation.ws.rs.LRA; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; +import java.net.URI; + +/** + * TCK invalid LRA participant containing invalid type of arguments in the participant method signature + */ +@Path("nonjaxrs-argument-type-nonjaxrs") +public class InvalidArgumentTypesParticipant { + + @GET + @Path("enlist") + @LRA(LRA.Type.REQUIRED) + public Response doInLRA() { + return Response.ok().build(); + } + + @Compensate + public ParticipantStatus compensate(URI lraId) { + return ParticipantStatus.Compensated; + } + + @Forget + public void forget(int lraId, URI parentId) { + // intentionally empty + } +} diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidReturnTypeParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidReturnTypeParticipant.java new file mode 100644 index 00000000..fb738348 --- /dev/null +++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/InvalidReturnTypeParticipant.java @@ -0,0 +1,46 @@ +/* + ******************************************************************************* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package org.eclipse.microprofile.lra.tck.participant.nonjaxrs; + +import org.eclipse.microprofile.lra.annotation.Compensate; +import org.eclipse.microprofile.lra.annotation.ws.rs.LRA; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +/** + * TCK invalid LRA participant containing invalid return type in the participant method signature + */ +@Path("nonjaxrs-return-type") +public class InvalidReturnTypeParticipant { + + @GET + @Path("enlist") + @LRA(LRA.Type.REQUIRED) + public Response doInLRA() { + return Response.ok().build(); + } + + @Compensate + public String compensate() { + return "compensated"; + } +} diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/TooManyArgsParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/TooManyArgsParticipant.java new file mode 100644 index 00000000..168b9cdb --- /dev/null +++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/TooManyArgsParticipant.java @@ -0,0 +1,54 @@ +/* + ******************************************************************************* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package org.eclipse.microprofile.lra.tck.participant.nonjaxrs; + +import org.eclipse.microprofile.lra.annotation.Compensate; +import org.eclipse.microprofile.lra.annotation.Complete; +import org.eclipse.microprofile.lra.annotation.ParticipantStatus; +import org.eclipse.microprofile.lra.annotation.ws.rs.LRA; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; +import java.net.URI; + +/** + * TCK invalid LRA participant containing too many arguments in the participant method signature + */ +@Path("too-many-args-nonjaxrs") +public class TooManyArgsParticipant { + + @GET + @Path("enlist") + @LRA(LRA.Type.REQUIRED) + public Response doInLRA() { + return Response.ok().build(); + } + + @Compensate + public ParticipantStatus compensate(URI lraId) { + return ParticipantStatus.Compensated; + } + + @Complete + public ParticipantStatus complete(URI lraId, URI parentId, Object additional) { + return ParticipantStatus.Completed; + } +} diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/ValidLRAParticipant.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/ValidLRAParticipant.java new file mode 100644 index 00000000..ed9926f4 --- /dev/null +++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/nonjaxrs/ValidLRAParticipant.java @@ -0,0 +1,172 @@ +/* + ******************************************************************************* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package org.eclipse.microprofile.lra.tck.participant.nonjaxrs; + +import org.eclipse.microprofile.lra.annotation.Compensate; +import org.eclipse.microprofile.lra.annotation.Complete; +import org.eclipse.microprofile.lra.annotation.Forget; +import org.eclipse.microprofile.lra.annotation.ParticipantStatus; +import org.eclipse.microprofile.lra.annotation.Status; +import org.eclipse.microprofile.lra.annotation.ws.rs.LRA; +import org.eclipse.microprofile.lra.annotation.ws.rs.LRA.Type; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.net.URI; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Logger; + +import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.ACCEPT_PATH; +import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.RECOVERY_PARAM; + +/** + * TCK valid LRA participant containing a combination of valid participant method signatures + */ +@ApplicationScoped +@Path(ValidLRAParticipant.RESOURCE_PATH) +public class ValidLRAParticipant { + private static final Logger LOGGER = Logger.getLogger(ValidLRAParticipant.class.getName()); + public static final String RESOURCE_PATH = "valid-nonjaxrs"; + public static final String COMPLETED_COUNT_PATH = "completed"; + public static final String COMPENSATED_COUNT_PATH = "compensated"; + public static final String STATUS_COUNT_PATH = "status"; + public static final String FORGET_COUNT_PATH = "forget"; + + private final AtomicInteger completedCount = new AtomicInteger(0); + private final AtomicInteger compensatedCount = new AtomicInteger(0); + private final AtomicInteger statusCount = new AtomicInteger(0); + private final AtomicInteger forgetCount = new AtomicInteger(0); + + public static final String ENLIST_WITH_COMPLETE = "nonjaxrs-enlist-complete"; + private int recoveryPasses; + + @GET + @Path(ENLIST_WITH_COMPLETE) + @LRA(value = Type.REQUIRED) + public Response enlistWithComplete() { + return Response.ok().build(); + } + + public static final String ENLIST_WITH_COMPENSATE = "nonjaxrs-enlist-compensate"; + + @GET + @Path(ENLIST_WITH_COMPENSATE) + @LRA(value = Type.REQUIRED, cancelOn = Response.Status.INTERNAL_SERVER_ERROR) + public Response enlistWithCompensate() { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + + + @Complete + public void completeWithException(URI lraId, URI parentId) { + verifyLRAId(lraId); + + completedCount.incrementAndGet(); + + LOGGER.info(String.format("LRA id '%s' was completed", lraId)); + throw new WebApplicationException(Response.ok().build()); + } + + @Compensate + public ParticipantStatus compensate(URI lraId) { + verifyLRAId(lraId); + + compensatedCount.incrementAndGet(); + + LOGGER.info(String.format("LRA id '%s' was compensated", lraId)); + return ParticipantStatus.Compensating; + } + + @Status + public Response status(URI lraId) { + verifyLRAId(lraId); + + statusCount.incrementAndGet(); + + LOGGER.info(String.format("LRA id '%s' status called, return FailedToCompensate to get @Forget called", lraId)); + return Response.ok(ParticipantStatus.FailedToCompensate).build(); + } + + @Forget + public void forget(URI lraId) { + verifyLRAId(lraId); + + forgetCount.incrementAndGet(); + + LOGGER.info(String.format("LRA id '%s' forget called", lraId)); + } + + private void verifyLRAId(URI lraId) { + if (lraId == null) { + throw new NullPointerException("lraId cannot be null"); + } + } + + @GET + @Path(COMPLETED_COUNT_PATH) + @Produces(MediaType.TEXT_PLAIN) + public Response completed() { + return Response.ok(completedCount.toString()).build(); + } + + @GET + @Path(COMPENSATED_COUNT_PATH) + @Produces(MediaType.TEXT_PLAIN) + public Response compensated() { + return Response.ok(compensatedCount.toString()).build(); + } + + @GET + @Path(STATUS_COUNT_PATH) + @Produces(MediaType.TEXT_PLAIN) + public Response statusCount() { + return Response.ok(statusCount.toString()).build(); + } + + @GET + @Path(FORGET_COUNT_PATH) + @Produces(MediaType.TEXT_PLAIN) + public Response forgetCount() { + return Response.ok(forgetCount.toString()).build(); + } + + @PUT + @Path(ACCEPT_PATH) + @LRA(value = LRA.Type.REQUIRES_NEW) + public Response acceptLRA(@QueryParam(RECOVERY_PARAM) @DefaultValue("0") Integer recoveryPasses) { + this.recoveryPasses = recoveryPasses; + + return Response.ok().build(); + } + + @GET + @Path(ACCEPT_PATH) + public Response getAcceptLRA() { + return Response.ok(this.recoveryPasses).build(); + } +}