diff --git a/.github/workflows/quickstart_microprofile-lra_ci.yml b/.github/workflows/quickstart_microprofile-lra_ci.yml
new file mode 100644
index 0000000000..ba62366703
--- /dev/null
+++ b/.github/workflows/quickstart_microprofile-lra_ci.yml
@@ -0,0 +1,14 @@
+name: WildFly microprofile-lra Quickstart CI
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened, ready_for_review]
+ paths:
+ - 'microprofile-lra/**'
+ - '.github/workflows/quickstart_ci.yml'
+jobs:
+ call-quickstart_ci:
+ uses: ./.github/workflows/quickstart_ci.yml
+ with:
+ QUICKSTART_PATH: microprofile-lra
+ TEST_BOOTABLE_JAR: true
diff --git a/microprofile-lra/README.adoc b/microprofile-lra/README.adoc
new file mode 100644
index 0000000000..7ac3261e91
--- /dev/null
+++ b/microprofile-lra/README.adoc
@@ -0,0 +1,592 @@
+include::../shared-doc/attributes.adoc[]
+
+= microprofile-lra: MicroProfile LRA QuickStart
+:author: Martin Stefanko
+:level: Beginner
+:technologies: MicroProfile LRA
+
+[abstract]
+The `microprofile-lra` quickstart demonstrates the use of the MicroProfile LRA specification in {productName}.
+
+:standalone-server-type: microprofile
+:configFileName: standalone-microprofile.xml
+:archiveType: war
+:archiveName: {artifactId}
+:microprofile-lra:
+:restoreScriptName: restore-configuration.cli
+:openshift: true
+
+== What is it?
+
+link:https://github.com/eclipse/microprofile-lra[MicroProfile LRA specification] aims to provide an API that the
+applications utilize to cooperate actions in
+distributed
+transactions based on the saga pattern. The user applications enlist within the LRA whicquickstart_microprofile-openapi_ci.ymlh in turn notifies all enlisted
+participants about the LRA (transaction) outcome. The saga pattern provides different transactional guarantees than ACID
+transactions. Saga allows individual operations to execute right when they are invoked. Meaning together with the
+enlistment in the LRA. It also requires each participant to define a compensating action which is a semantic undo of the
+original operation. Note that this doesn't need to be opposite action. The compensation is required to put the state of
+the system into the semantically same state as before the action invocation, not exactly same. If your action is for
+instance sending an email, your compensation might be another email cancelling previous email.
+
+If all actions execute successfully, the LRA is closed and the optional Complete callbacks are invoked on enlisted
+participants. If any action fails, then the LRA is cancelled and all compensation actions (Compensate callbacks) of all
+enlisted participants are invoked. The state of the system is said to be eventually consistent, since if we don't start
+any new LRAs, the state is bound to become consistent eventually.
+
+The implementation used in the {productName} is provided by the
+link:https://github.com/jbosstm/narayana/tree/main/rts/lra[Narayana project].
+
+== Architecture
+
+In this quickstart, we have a simple REST application that exposes several REST endpoints that enlist the application as
+different LRA participants and provide callbacks for completions and compensations respectively. It's REST API consists
+of the following
+endpoints:
+
+- `GET /participant1/work` - work action of Participant 1
+- `GET /participant2/work` - work action of Participant 2
+- `PUT /participant1/compensate` - compensating action of Participant 1
+- `PUT /participant2/compensate` - compensating action of Participant 2
+- `PUT /participant1/complete` - complete action of Participant 1
+- `PUT /participant2/complete` - complete action of Participant 2
+
+
+// System Requirements
+include::../shared-doc/system-requirements.adoc[leveloffset=+1]
+
+// Use of {jbossHomeName}
+include::../shared-doc/use-of-jboss-home-name.adoc[leveloffset=+1]
+
+// Back Up the {productName} Standalone Server Configuration
+include::../shared-doc/back-up-server-standalone-configuration.adoc[leveloffset=+1]
+
+// Start the {productName} Standalone Server
+include::../shared-doc/start-the-standalone-server.adoc[leveloffset=+1]
+
+[[configure_the_server]]
+== Configure the Server
+
+You can configure the LRA extensions and subsystems (both for LRA coordinator and LRA participant respectively) by running CLI commands.
+For your convenience, this quickstart batches the commands into a `enable-microprofile-lra.cli` script provided in the root directory
+of this quickstart.
+
+. Before you begin, make sure you do the following:
+
+* xref:back_up_standalone_server_configuration[Back up the {productName} standalone server configuration] as described above.
+* xref:start_the_eap_standalone_server[Start the {productName} server with the standalone default profile] as described above.
+
+. Review the `enable-microprofile-lra.cli` file in the root of this quickstart directory. It enables two extensions and adds
+two subsystems, one for LRA coordinator and one for LRA participant respectively.
+. Open a new terminal, navigate to the root directory of this quickstart, and run the following command, replacing `__{jbossHomeName}__`
+with the path to your server:
++
+[source,subs="+quotes,attributes+",options="nowrap"]
+----
+$ __{jbossHomeName}__/bin/jboss-cli.sh --connect --file=enable-microprofile-lra.cli
+----
++
+NOTE: For Windows, use the `__{jbossHomeName}__\bin\jboss-cli.bat` script.
++
+
+You should see the following result when you run the script:
++
+[source,options="nowrap"]
+----
+The batch executed successfully
+----
+
+. Stop the {productName} server.
+
+== Review the Modified Server Configuration
+
+After stopping the server, open the `__{jbossHomeName}__/standalone/configuration/{configFileName}` file and review the changes.
+
+. The script added the following two extensions:
++
+[source,xml,options="nowrap"]
+----
+
+
+----
++
+
+. And also the following two subsystems:
++
+[source,xml,options="nowrap"]
+----
+
+
+----
++
+
+[[solution]]
+== Solution
+
+We recommend that you follow the instructions that
+<>. However, you can
+also go right to the completed example which is available in this directory.
+
+// Build and Deploy the Quickstart
+include::../shared-doc/build-and-deploy-the-quickstart.adoc[leveloffset=+1]
+
+// Server Distribution Testing
+include::../shared-doc/run-integration-tests-with-server-distribution.adoc[leveloffset=+2]
+
+// Undeploy the Quickstart
+include::../shared-doc/undeploy-the-quickstart.adoc[leveloffset=+1]
+
+// Restore the {productName} Standalone Server Configuration
+include::../shared-doc/restore-standalone-server-configuration.adoc[leveloffset=+1]
+
+[[creating-new-project]]
+== Creating the Maven Project
+
+[source,options="nowrap"]
+----
+mvn archetype:generate \
+ -DgroupId=org.wildfly.quickstarts \
+ -DartifactId=microprofile-lra \
+ -DinteractiveMode=false \
+ -DarchetypeGroupId=org.apache.maven.archetypes \
+ -DarchetypeArtifactId=maven-archetype-webapp
+cd microprofile-lra
+----
+
+Open the project in your favourite IDE.
+
+Open the generated `pom.xml`.
+
+The first thing to do is to change the minimum JDK to Java 11:
+
+[source,xml]
+----
+11
+11
+----
+
+Next we need to setup our dependencies. Add the following section to your
+`pom.xml`:
+
+[source,xml,subs="attributes+"]
+----
+
+
+
+ org.wildfly.bom
+ wildfly-ee-with-tools
+ ${version.bom.ee}
+ pom
+ import
+
+
+ org.wildfly.bom
+ wildfly-microprofile
+ ${version.bom.microprofile}
+ pom
+ import
+
+
+
+----
+
+Now we need to add the following dependencies:
+
+[source,xml]
+----
+
+ org.eclipse.microprofile.lra
+ microprofile-lra-api
+ provided
+
+
+ jakarta.ws.rs
+ jakarta.ws.rs-api
+ provided
+
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+ provided
+
+
+ org.jboss.logging
+ jboss-logging
+ provided
+
+----
+
+NOTE: We need Jakarta REST (JAX-RS) since LRA exposes functionality over JAX-RS resources and uses HTTP as its
+communication protocol.
+
+All dependencies can have provided scope. The versions are taken from the above
+defined BOM.
+
+As we are going to be deploying this application to the {productName} server, let's
+also add a maven plugin that will simplify the deployment operations (you can replace
+the generated build section):
+
+[source,xml]
+----
+
+
+ ${project.artifactId}
+
+
+
+ org.wildfly.plugins
+ wildfly-maven-plugin
+ ${version.plugin.wildfly}
+
+
+ org.wildfly.plugins
+ wildfly-jar-maven-plugin
+ ${version.plugin.wildfly-jar}
+
+
+
+
+----
+
+// Setup required repositories
+include::../shared-doc/setup-repositories.adoc[leveloffset=+1]
+
+Now we are ready to start working with MicroProfile LRA.
+
+== Set up JAX-RS server
+
+LRA works on top of JAX-RS. To set up JAX-RS server in our service, we need to create a new application class
+`org.wildfly.quickstarts.microprofile.lra.JaxRsApplication` in the file
+`microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/JaxRsApplication.java` that looks like this:
+
+[source,java]
+----
+package org.wildfly.quickstarts.microprofile.lra;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+
+@ApplicationPath("/")
+public class JaxRsApplication extends Application {
+}
+----
+
+Now we can declare our LRA JAX-RS resources.
+
+
+== Creating LRA participants
+
+In LRA, we define LRA execution and participation with the same `@LRA` annotation. If placed on a method, it acts
+similarly to `@Transactional` annotation from JTA. By default, it uses the `REQUIRED` LRA type meaning new LRA is
+started or existing LRA (if passed to the invocation) is joined before the method is started. The LRA is also closed
+(success) or cancelled (failure/exception) at the end of the method.
+
+LRA currently works on top of the JAX-RS resources. We can place `@LRA` annotation on any JAX-RS method and the LRA
+is already managed for us by {productName}. Let's create a simple JAX-RS resource that uses lra in `org.wildfly .quickstarts.microprofile.lra.LRAParticipant1`:
+
+[source,java]
+----
+package org.wildfly.quickstarts.microprofile.lra;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.Response;
+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.jboss.logging.Logger;
+
+import java.net.URI;
+
+@Path("/participant1")
+public class LRAParticipant1 {
+
+ private static final Logger LOGGER = Logger.getLogger(LRAParticipant1.class);
+
+ @LRA
+ @GET
+ @Path("/work")
+ public Response work(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId,
+ @QueryParam("failLRA") boolean failLRA) {
+ LOGGER.infof("Executing action of Participant 1 enlisted in LRA %s " +
+ "that was assigned %s participant Id.", lraId, participantId);
+
+ return failLRA ? Response.status(Response.Status.INTERNAL_SERVER_ERROR).build() : Response.ok().build();
+ }
+
+ @Compensate
+ @PUT
+ @Path("/compensate")
+ public Response compensateWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Compensating action for Participant 1 (%s) in LRA %s.", participantId, lraId);
+ return Response.ok().build();
+ }
+
+ @Complete
+ @PUT
+ @Path("/complete")
+ public Response completeWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Complete action for Participant 1 (%s) in LRA %s.", participantId, lraId);
+ return Response.ok().build();
+ }
+}
+----
+
+Let's look at it part by part.
+
+The most important method is called `work` and it looks like this:
+
+[source,java]
+----
+@LRA
+@GET
+@Path("/work")
+public Response work(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId,
+ @QueryParam("failLRA") boolean failLRA) {
+ LOGGER.infof("Executing action of Participant 1 enlisted in LRA %s " +
+ "that was assigned %s participant Id.", lraId, participantId);
+
+ return failLRA ? Response.status(Response.Status.INTERNAL_SERVER_ERROR).build() : Response.ok().build();
+}
+----
+
+In this `GET` JAX-RS method, we also use the `@LRA` annotation that either starts a new LRA or joins an existing one
+which is defined by the default LRA type `REQUIRED`. This is decided based on the `LRA.LRA_HTTP_CONTEXT_HEADER` header
+we called `lraId`. If the framework starts a new LRA,
+this header is automatically populated with its ID. If the caller specifies this `LRA.LRA_HTTP_CONTEXT_HEADER`
+manually in the request, the received LRA is joined. As you can see, the LRA context or ID is propagated in HTTP
+headers.
+
+The second header parameter `LRA.LRA_HTTP_RECOVERY_HEADER` is considered a unique participant ID for a particular
+enlistment within LRA. If we would like to enlist `LRAParticipant1` in the same LRA (`LRA.LRA_HTTP_CONTEXT_HEADER`)
+multiple times, this recovery ID would be different so we can associate the invocations of compensate and complete
+methods.
+
+Each LRA participant needs to define the `@Compensate` method that defines the compensating action.
+
+[source,java]
+----
+@Compensate
+@PUT
+@Path("/compensate")
+public Response compensateWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Compensating action for Participant 1 (%s) in LRA %s.", participantId, lraId);
+ return Response.ok().build();
+}
+----
+
+The compensation is defined by the `@Compensate` annotation which needs to be placed on the JAX-RS PUT method so the LRA
+coordinator knows how to call it. For simplicity, we are just printing the messages to the console. The participant can
+control how it finishes its participation in LRA via the returned status code. Please see the
+link:https://github.com/eclipse/microprofile-lra/blob/main/spec/src/main/asciidoc/microprofile-lra-spec.asciidoc[specification]
+for more details.
+
+The complete method looks similarly. It uses the `@Complete` annotation and it also needs to be the JAX-RS PUT method.
+
+[source,java]
+----
+@Complete
+@PUT
+@Path("/complete")
+public Response completeWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Complete action for Participant 1 (%s) in LRA %s.", participantId, lraId);
+ return Response.ok().build();
+}
+----
+
+The LRA coordinator invokes the `@Compensate` method when the LRA cancels on failure and it invokes the `@Complete`
+method when the LRA closes successfully.
+
+NOTE: The `@Complete` and `@Compensate` methods don't need to be JAX-RS methods. See the specification for details.
+
+Now we are already able to start our first LRA. You can deploy the application to the {productName} as demonstrated in
+the <> section. Remember that you need to enable the LRA extensions and subsystems with the
+`enable-microprofile-lra.cli` script.
+
+Then you can invoke the `LRAParticipant1` JAX-RS resource as:
+
+[source,bash]
+----
+$ curl http://localhost:8080/microprofile-lra/participant1/work
+----
+
+or if you want to simulate LRA failure as:
+
+[source,bash]
+----
+$ curl "http://localhost:8080/microprofile-lra/participant1/work?failLRA=true"
+----
+
+In either case, you will see the LRA execution message printed in the {productName} console:
+
+[source,bash]
+----
+INFO [org.wildfly.quickstarts.microprofile.lra.LRAParticipant1] (default task-1) Executing action of Participant 1 enlisted in LRA http://localhost:8080/lra-coordinator/lra-coordinator/0_ffff0aca949a_-4998614b_64e74427_48 that was assigned http://localhost:8080/lra-coordinator/lra-coordinator/recoveryhttp%3A%2F%2Flocalhost%3A8080%2Flra-coordinator%2Flra-coordinator%2F0_ffff0aca949a_-4998614b_64e74427_48/0_ffff0aca949a_-4998614b_64e74427_4a participant Id.
+----
+
+And either the complete or compensate message depending on the `failLRA` paramater that can fail the LRA causing it
+to cancel:
+
+[source,bash]
+----
+INFO [org.wildfly.quickstarts.microprofile.lra.LRAParticipant1] (default task-4) Complete action for Participant 1 (http://localhost:8080/lra-coordinator/lra-coordinator/recoveryhttp%3A%2F%2Flocalhost%3A8080%2Flra-coordinator%2Flra-coordinator%2F0_ffff0aca949a_-4998614b_64e74427_37/0_ffff0aca949a_-4998614b_64e74427_39) in LRA http://localhost:8080/lra-coordinator/lra-coordinator/0_ffff0aca949a_-4998614b_64e74427_37.
+
+
+INFO [org.wildfly.quickstarts.microprofile.lra.LRAParticipant1] (default task-4) Compensating action for Participant 1 (http://localhost:8080/lra-coordinator/lra-coordinator/recoveryhttp%3A%2F%2Flocalhost%3A8080%2Flra-coordinator%2Flra-coordinator%2F0_ffff0aca949a_-4998614b_64e74427_48/0_ffff0aca949a_-4998614b_64e74427_4a) in LRA http://localhost:8080/lra-coordinator/lra-coordinator/0_ffff0aca949a_-4998614b_64e74427_48.
+----
+
+== Multiple participants in the LRA
+
+One participant that starts and ends the LRA is probably enough to demonstrate the functionality, but it rarely makes
+sense in distributed microservices architecture to only have one service that participates in a distributed
+transaction. So let's add another participant into the LRA started in the `LRAParticipant1`.
+
+Copy the `LRAParticipant1` into a new class `LRAParticipant2` and change all references to `participant1` to
+`participant2`.
+The
+full class is provided for convenience also here:
+
+[source,java]
+----
+package org.wildfly.quickstarts.microprofile.lra;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Response;
+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.jboss.logging.Logger;
+
+import java.net.URI;
+
+@Path("/participant2")
+public class LRAParticipant2 {
+
+ private static final Logger LOGGER = Logger.getLogger(LRAParticipant2.class);
+
+ @LRA(end = false)
+ @GET
+ @Path("/work")
+ public Response work(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Executing action of Participant 2 enlisted in LRA %s " +
+ "that was assigned %s participant Id.", lraId, participantId);
+ return Response.ok().build();
+ }
+
+ @Compensate
+ @PUT
+ @Path("/compensate")
+ public Response compensateWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Compensating action for Participant 2 (%s) in LRA %s.", participantId, lraId);
+ return Response.ok().build();
+ }
+
+ @Complete
+ @PUT
+ @Path("/complete")
+ public Response completeWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Complete action for Participant 2 (%s) in LRA %s.", participantId, lraId);
+ return Response.ok().build();
+ }
+
+}
+----
+
+The only notable change is the `LRA` annotation that now contains the `@LRA(end = false)`. This parameter states that
+the LRA should not be ended when this business method ends. If we ended the LRA here, it would still invoke
+compensate or complete callbacks on all enlisted participants (including `LRAParticipant1` which will propagate the
+LRA into this class soon). However, it would also try to close/cancel LRA at the end of the `LRAParticipant1#work`
+method and by this time the LRA would already be ended. This would be reported by the coordinator.
+
+We also need to add the call to the newly created JAX-RS resource to the `LRAParticipant1#work` method as showed in
+this
+snipped:
+
+[source,java]
+----
+@LRA
+@GET
+@Path("/work")
+public Response work(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId,
+ @QueryParam("failLRA") boolean failLRA) {
+ LOGGER.infof("Executing action of Participant 1 enlisted in LRA %s " +
+ "that was assigned %s participant Id.", lraId, participantId);
+
+ // call Participant 2 to propagate the LRA
+ try (Client client = ClientBuilder.newClient()) {
+ client.target("http://localhost:8080/microprofile-lra/participant2/work")
+ .request().get();
+ }
+
+ return failLRA ? Response.status(Response.Status.INTERNAL_SERVER_ERROR).build() : Response.ok().build();
+}
+----
+
+You might remember that we need to propagate the LRA id (LRA context) in the `LRA.LRA_HTTP_CONTEXT_HEADER`. However, if
+we make the outgoing JAX-RS call in the JAX-RS method that already carries an active LRA context, the context is
+automatically added to the outgoing call. So we don't need to pass it manually to each outgoing call.
+
+Now we are ready to propagate LRA started in Participant 1 to the Participant 2, enlist both in the newly started
+LRA, and finish the LRA when the Participant 1 ends its `work` method.
+
+Redeploy the application into the {productName} as showed in <>. Then you can repeat the calls to the
+`LRAParticipant1` JAX-RS resource as we used them previously:
+
+[source,bash]
+----
+$ curl http://localhost:8080/microprofile-lra/participant1/work
+----
+
+or if you want to simulate LRA failure as:
+
+[source,bash]
+----
+$ curl "http://localhost:8080/microprofile-lra/participant1/work?failLRA=true"
+----
+
+But this time, you will see the LRA is propagated to the `LRAParticipant2` and its (complete or compensate) callbacks
+are invoked by the LRA coordinator in the same way as for `LRAParticipant1`:
+
+[source,bash]
+----
+INFO [org.wildfly.quickstarts.microprofile.lra.LRAParticipant1] (default task-1) Executing action of Participant 1 enlisted in LRA http://localhost:8080/lra-coordinator/lra-coordinator/0_ffff0aca949a_-4998614b_64e74427_38b that was assigned http://localhost:8080/lra-coordinator/lra-coordinator/recoveryhttp%3A%2F%2Flocalhost%3A8080%2Flra-coordinator%2Flra-coordinator%2F0_ffff0aca949a_-4998614b_64e74427_38b/0_ffff0aca949a_-4998614b_64e74427_38d participant Id.
+
+INFO [org.wildfly.quickstarts.microprofile.lra.LRAParticipant2] (default task-2) Executing action of Participant 2 enlisted in LRA http://localhost:8080/lra-coordinator/lra-coordinator/0_ffff0aca949a_-4998614b_64e74427_38b that was assigned http://localhost:8080/lra-coordinator/lra-coordinator/recoveryhttp%3A%2F%2Flocalhost%3A8080%2Flra-coordinator%2Flra-coordinator%2F0_ffff0aca949a_-4998614b_64e74427_38b/0_ffff0aca949a_-4998614b_64e74427_38f participant Id.
+
+INFO [org.wildfly.quickstarts.microprofile.lra.LRAParticipant1] (default task-5) Compensating action for Participant 1 (http://localhost:8080/lra-coordinator/lra-coordinator/recoveryhttp%3A%2F%2Flocalhost%3A8080%2Flra-coordinator%2Flra-coordinator%2F0_ffff0aca949a_-4998614b_64e74427_38b/0_ffff0aca949a_-4998614b_64e74427_38d) in LRA http://localhost:8080/lra-coordinator/lra-coordinator/0_ffff0aca949a_-4998614b_64e74427_38b.
+
+INFO [org.wildfly.quickstarts.microprofile.lra.LRAParticipant2] (default task-5) Compensating action for Participant 2 (http://localhost:8080/lra-coordinator/lra-coordinator/recoveryhttp%3A%2F%2Flocalhost%3A8080%2Flra-coordinator%2Flra-coordinator%2F0_ffff0aca949a_-4998614b_64e74427_38b/0_ffff0aca949a_-4998614b_64e74427_38f) in LRA http://localhost:8080/lra-coordinator/lra-coordinator/0_ffff0aca949a_-4998614b_64e74427_38b.
+----
+
+
+//Bootable JAR
+include::../shared-doc/build-and-run-the-quickstart-with-bootable-jar.adoc[leveloffset=+1]
+
+// OpenShift
+include::../shared-doc/build-and-run-the-quickstart-with-openshift.adoc[leveloffset=+1]
+
+== Conclusion
+
+MicroProfile LRA provides a simple API for the distributed transactions based on the saga pattern. To use it
+{productName} we need to enable the appropriate extensions and subsystems for the LRA Coordinator (a service that
+manages LRAs) and the LRA participant (client API). The LRAs are controlled trough annotations provided by the
+specification.
+
+Congratulations! You have reached the end of this tutorial. You can find more information
+about the MicroProfile LRA in the specification https://github.com/eclipse/microprofile-lra[github repository].
diff --git a/microprofile-lra/charts/heml.yaml b/microprofile-lra/charts/heml.yaml
new file mode 100644
index 0000000000..61fba52af3
--- /dev/null
+++ b/microprofile-lra/charts/heml.yaml
@@ -0,0 +1,6 @@
+build:
+ uri: https://github.com/wildfly/quickstart.git
+ ref: main
+ contextDir: microprofile-lra
+deploy:
+ replicas: 1
\ No newline at end of file
diff --git a/microprofile-lra/enable-microprofile-lra.cli b/microprofile-lra/enable-microprofile-lra.cli
new file mode 100644
index 0000000000..c563814a27
--- /dev/null
+++ b/microprofile-lra/enable-microprofile-lra.cli
@@ -0,0 +1,8 @@
+batch
+
+/extension=org.wildfly.extension.microprofile.lra-coordinator:add
+/extension=org.wildfly.extension.microprofile.lra-participant:add
+/subsystem=microprofile-lra-coordinator:add
+/subsystem=microprofile-lra-participant:add
+
+run-batch
diff --git a/microprofile-lra/pom.xml b/microprofile-lra/pom.xml
new file mode 100644
index 0000000000..382db54304
--- /dev/null
+++ b/microprofile-lra/pom.xml
@@ -0,0 +1,255 @@
+
+ 4.0.0
+
+ org.wildfly.quickstarts
+ wildfly-quickstart-parent
+
+ 5
+
+
+
+ microprofile-lra
+ 31.0.0.Beta1-SNAPSHOT
+ war
+ Quickstart: microprofile-lra
+
+
+
+ 30.0.0.Final
+
+ ${version.server}
+ ${version.server}
+ 5.0.0.Final
+ 4.2.0.Final
+ 10.0.0.Final
+
+
+
+
+ jboss-public-maven-repository
+ JBoss Public Maven Repository
+ https://repository.jboss.org/nexus/content/groups/public/
+
+ true
+ never
+
+
+ true
+ never
+
+ default
+
+
+ redhat-ga-maven-repository
+ Red Hat GA Maven Repository
+ https://maven.repository.redhat.com/ga/
+
+ true
+ never
+
+
+ true
+ never
+
+ default
+
+
+
+
+ jboss-public-maven-repository
+ JBoss Public Maven Repository
+ https://repository.jboss.org/nexus/content/groups/public/
+
+ true
+
+
+ true
+
+
+
+ redhat-ga-maven-repository
+ Red Hat GA Maven Repository
+ https://maven.repository.redhat.com/ga/
+
+ true
+
+
+ true
+
+
+
+
+
+
+
+ org.wildfly.bom
+ wildfly-ee-with-tools
+ ${version.bom.ee}
+ pom
+ import
+
+
+ org.wildfly.bom
+ wildfly-microprofile
+ ${version.bom.microprofile}
+ pom
+ import
+
+
+
+
+
+
+ org.eclipse.microprofile.lra
+ microprofile-lra-api
+ provided
+
+
+ jakarta.ws.rs
+ jakarta.ws.rs-api
+ provided
+
+
+ jakarta.enterprise
+ jakarta.enterprise.cdi-api
+ provided
+
+
+ org.jboss.logging
+ jboss-logging
+ provided
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.jboss.resteasy
+ resteasy-client
+ test
+
+
+ org.jboss.resteasy
+ resteasy-jackson2-provider
+ test
+
+
+
+
+
+ ${project.artifactId}
+
+
+
+ org.wildfly.plugins
+ wildfly-maven-plugin
+ ${version.plugin.wildfly}
+
+
+ org.wildfly.plugins
+ wildfly-jar-maven-plugin
+ ${version.plugin.wildfly-jar}
+
+
+
+
+
+
+
+ bootable-jar
+
+
+
+ org.wildfly.plugins
+ wildfly-jar-maven-plugin
+
+ wildfly@maven(org.jboss.universe:community-universe)#${version.server}
+
+ jaxrs-server
+ microprofile-lra-coordinator
+ microprofile-lra-participant
+
+
+ true
+
+
+
+
+
+ package
+
+
+
+
+
+
+
+
+ openshift
+
+
+
+ org.wildfly.plugins
+ wildfly-maven-plugin
+
+
+
+ org.wildfly:wildfly-galleon-pack:${version.server}
+
+
+ org.wildfly.cloud:wildfly-cloud-galleon-pack:${version.pack.cloud}
+
+
+
+ jaxrs-server
+ microprofile-lra-coordinator
+ microprofile-lra-participant
+
+ ROOT.war
+
+
+
+
+ package
+
+
+
+
+
+
+
+
+ integration-testing
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ **/BasicRuntimeIT
+ **/MicroProfileLRAIT
+
+
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+
+
+
+
diff --git a/microprofile-lra/restore-configuration.cli b/microprofile-lra/restore-configuration.cli
new file mode 100644
index 0000000000..2205990669
--- /dev/null
+++ b/microprofile-lra/restore-configuration.cli
@@ -0,0 +1,12 @@
+# This script restores the configuration with the enabled MicroProfile LRA extensions and subsystems.
+
+batch
+
+/subsystem=microprofile-lra-participant:remove
+/subsystem=microprofile-lra-coordinator:remove
+/extension=org.wildfly.extension.microprofile.lra-participant:remove
+/extension=org.wildfly.extension.microprofile.lra-coordinator:remove
+
+run-batch
+
+reload
diff --git a/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/JaxRsApplication.java b/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/JaxRsApplication.java
new file mode 100644
index 0000000000..49b9f96676
--- /dev/null
+++ b/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/JaxRsApplication.java
@@ -0,0 +1,30 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2023, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.wildfly.quickstarts.microprofile.lra;
+
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+
+@ApplicationPath("/")
+public class JaxRsApplication extends Application {
+}
diff --git a/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/LRAParticipant1.java b/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/LRAParticipant1.java
new file mode 100644
index 0000000000..7b66902856
--- /dev/null
+++ b/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/LRAParticipant1.java
@@ -0,0 +1,121 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2023, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.wildfly.quickstarts.microprofile.lra;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriInfo;
+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.jboss.logging.Logger;
+
+import java.net.URI;
+
+@Path("/participant1")
+@ApplicationScoped
+public class LRAParticipant1 {
+
+ private static final Logger LOGGER = Logger.getLogger(LRAParticipant1.class);
+
+ private String workLRAId;
+ private String workRecoveryId;
+ private String completeLRAId;
+ private String completeRecoveryId;
+ private String compensateLRAId;
+ private String compensateRecoveryId;
+
+ @Context
+ UriInfo uriInfo;
+
+ @LRA
+ @GET
+ @Path("/work")
+ public Response work(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId,
+ @QueryParam("failLRA") boolean failLRA) {
+ LOGGER.infof("Executing action of Participant 1 enlisted in LRA %s " +
+ "that was assigned %s participant Id.", lraId, participantId);
+
+ workLRAId = lraId.toASCIIString();
+ workRecoveryId = participantId.toASCIIString();
+ compensateLRAId = null;
+ compensateRecoveryId = null;
+ completeLRAId = null;
+ completeRecoveryId = null;
+
+ // call Participant 2 to propagate the LRA
+ try (Client client = ClientBuilder.newClient()) {
+ client.target(uriInfo.getBaseUri() + "/participant2/work")
+ .request().get();
+ }
+
+ return failLRA ? Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(lraId.toASCIIString()).build() :
+ Response.ok(lraId.toASCIIString()).build();
+ }
+
+ @Compensate
+ @PUT
+ @Path("/compensate")
+ public Response compensateWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Compensating action for Participant 1 (%s) in LRA %s.", participantId, lraId);
+
+ compensateLRAId = lraId.toASCIIString();
+ compensateRecoveryId = participantId.toASCIIString();
+
+ return Response.ok().build();
+ }
+
+ @Complete
+ @PUT
+ @Path("/complete")
+ public Response completeWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Complete action for Participant 1 (%s) in LRA %s.", participantId, lraId);
+
+ completeLRAId = lraId.toASCIIString();
+ completeRecoveryId = participantId.toASCIIString();
+
+ return Response.ok().build();
+ }
+
+ @GET
+ @Path("/result")
+ @Produces(MediaType.APPLICATION_JSON)
+ public ParticipantResult getParticipantResult() {
+ return new ParticipantResult(workLRAId, workRecoveryId,
+ completeLRAId, completeRecoveryId,
+ compensateLRAId, compensateRecoveryId);
+ }
+}
diff --git a/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/LRAParticipant2.java b/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/LRAParticipant2.java
new file mode 100644
index 0000000000..60e67c421e
--- /dev/null
+++ b/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/LRAParticipant2.java
@@ -0,0 +1,106 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2023, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.wildfly.quickstarts.microprofile.lra;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.HeaderParam;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+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.jboss.logging.Logger;
+
+import java.net.URI;
+
+@Path("/participant2")
+@ApplicationScoped
+public class LRAParticipant2 {
+
+ private static final Logger LOGGER = Logger.getLogger(LRAParticipant2.class);
+
+ private String workLRAId;
+ private String workRecoveryId;
+ private String completeLRAId;
+ private String completeRecoveryId;
+ private String compensateLRAId;
+ private String compensateRecoveryId;
+
+ @LRA(end = false)
+ @GET
+ @Path("/work")
+ public Response work(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Executing action of Participant 2 enlisted in LRA %s " +
+ "that was assigned %s participant Id.", lraId, participantId);
+
+ workLRAId = lraId.toASCIIString();
+ workRecoveryId = participantId.toASCIIString();
+ compensateLRAId = null;
+ compensateRecoveryId = null;
+ completeLRAId = null;
+ completeRecoveryId = null;
+
+ return Response.ok().build();
+ }
+
+ @Compensate
+ @PUT
+ @Path("/compensate")
+ public Response compensateWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Compensating action for Participant 2 (%s) in LRA %s.", participantId, lraId);
+
+ compensateLRAId = lraId.toASCIIString();
+ compensateRecoveryId = participantId.toASCIIString();
+
+ return Response.ok().build();
+ }
+
+ @Complete
+ @PUT
+ @Path("/complete")
+ public Response completeWork(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) URI lraId,
+ @HeaderParam(LRA.LRA_HTTP_RECOVERY_HEADER) URI participantId) {
+ LOGGER.infof("Complete action for Participant 2 (%s) in LRA %s.", participantId, lraId);
+
+ completeLRAId = lraId.toASCIIString();
+ completeRecoveryId = participantId.toASCIIString();
+
+ return Response.ok().build();
+ }
+
+ @GET
+ @Path("/result")
+ @Produces(MediaType.APPLICATION_JSON)
+ public ParticipantResult getParticipantResult() {
+ return new ParticipantResult(workLRAId, workRecoveryId,
+ completeLRAId, completeRecoveryId,
+ compensateLRAId, compensateRecoveryId);
+ }
+
+}
diff --git a/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/ParticipantResult.java b/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/ParticipantResult.java
new file mode 100644
index 0000000000..720f429233
--- /dev/null
+++ b/microprofile-lra/src/main/java/org/wildfly/quickstarts/microprofile/lra/ParticipantResult.java
@@ -0,0 +1,84 @@
+package org.wildfly.quickstarts.microprofile.lra;
+
+public class ParticipantResult {
+
+ private String workLRAId;
+ private String workRecoveryId;
+ private String completeLRAId;
+ private String completeRecoveryId;
+ private String compensateLRAId;
+ private String compensateRecoveryId;
+
+ public ParticipantResult() {}
+
+ public ParticipantResult(String workLRAId, String workRecoveryId,
+ String completeLRAId, String completeRecoveryId,
+ String compensateLRAId, String compensateRecoveryId) {
+ this.workLRAId = workLRAId;
+ this.workRecoveryId = workRecoveryId;
+ this.completeLRAId = completeLRAId;
+ this.completeRecoveryId = completeRecoveryId;
+ this.compensateLRAId = compensateLRAId;
+ this.compensateRecoveryId = compensateRecoveryId;
+ }
+
+ public String getWorkLRAId() {
+ return workLRAId;
+ }
+
+ public void setWorkLRAId(String workLRAId) {
+ this.workLRAId = workLRAId;
+ }
+
+ public String getWorkRecoveryId() {
+ return workRecoveryId;
+ }
+
+ public void setWorkRecoveryId(String workRecoveryId) {
+ this.workRecoveryId = workRecoveryId;
+ }
+
+ public String getCompleteLRAId() {
+ return completeLRAId;
+ }
+
+ public void setCompleteLRAId(String completeLRAId) {
+ this.completeLRAId = completeLRAId;
+ }
+
+ public String getCompleteRecoveryId() {
+ return completeRecoveryId;
+ }
+
+ public void setCompleteRecoveryId(String completeRecoveryId) {
+ this.completeRecoveryId = completeRecoveryId;
+ }
+
+ public String getCompensateLRAId() {
+ return compensateLRAId;
+ }
+
+ public void setCompensateLRAId(String compensateLRAId) {
+ this.compensateLRAId = compensateLRAId;
+ }
+
+ public String getCompensateRecoveryId() {
+ return compensateRecoveryId;
+ }
+
+ public void setCompensateRecoveryId(String compensateRecoveryId) {
+ this.compensateRecoveryId = compensateRecoveryId;
+ }
+
+ @Override
+ public String toString() {
+ return "ParticipantResult{" +
+ "workLRAId='" + workLRAId + '\'' +
+ ", workRecoveryId='" + workRecoveryId + '\'' +
+ ", completeLRAId='" + completeLRAId + '\'' +
+ ", completeRecoveryId='" + completeRecoveryId + '\'' +
+ ", compensateLRAId='" + compensateLRAId + '\'' +
+ ", compensateRecoveryId='" + compensateRecoveryId + '\'' +
+ '}';
+ }
+}
diff --git a/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/BasicRuntimeIT.java b/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/BasicRuntimeIT.java
new file mode 100644
index 0000000000..4c0124cd32
--- /dev/null
+++ b/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/BasicRuntimeIT.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 JBoss by Red Hat.
+ *
+ * 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.wildfly.quickstarts.microprofile.lra;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * The very basic runtime integration testing.
+ * @author emartins
+ */
+public class BasicRuntimeIT {
+
+ @Test
+ public void testHTTPEndpointIsAvailable() throws IOException, InterruptedException, URISyntaxException {
+ String serverHost = TestUtils.getServerHost() + "/participant1/work";
+ final HttpRequest request = HttpRequest.newBuilder()
+ .uri(new URI(serverHost))
+ .GET()
+ .build();
+ final HttpClient client = HttpClient.newBuilder()
+ .followRedirects(HttpClient.Redirect.ALWAYS)
+ .connectTimeout(Duration.ofMinutes(1))
+ .build();
+ final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
+ assertEquals(200, response.statusCode());
+ }
+}
\ No newline at end of file
diff --git a/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/MicroProfileLRAIT.java b/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/MicroProfileLRAIT.java
new file mode 100644
index 0000000000..e85873aa8d
--- /dev/null
+++ b/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/MicroProfileLRAIT.java
@@ -0,0 +1,128 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2023, Red Hat, Inc. and/or its affiliates, and individual
+ * contributors by the @authors tag. See the copyright.txt in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.wildfly.quickstarts.microprofile.lra;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.function.Function;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.wildfly.quickstarts.microprofile.lra.TestUtils.getServerHost;
+
+public class MicroProfileLRAIT {
+
+ private Client client;
+
+ @Before
+ public void before() {
+ client = ClientBuilder.newClient();
+ }
+
+ @After
+ public void after() {
+ client.close();
+ }
+
+ @Test
+ public void testLRAExecutionSuccess() {
+ Response response = getResponse("/participant1/work");
+ assertEquals(200, response.getStatus());
+ String lraId = response.readEntity(String.class);
+
+ response = getResponse("/participant1/result");
+ assertEquals(200, response.getStatus());
+ ParticipantResult participantResult1 = response.readEntity(ParticipantResult.class);
+
+ response = getResponse("/participant2/result");
+ assertEquals(200, response.getStatus());
+ ParticipantResult participantResult2 = response.readEntity(ParticipantResult.class);
+
+ assertEquals(lraId, participantResult1.getWorkLRAId());
+ String recoveryId1 = participantResult1.getWorkRecoveryId();
+
+ assertEquals(lraId, participantResult2.getWorkLRAId());
+ String recoveryId2 = participantResult2.getWorkRecoveryId();
+
+ // LRA closed successfully, Complete callbacks called
+ assertEquals(lraId, participantResult1.getCompleteLRAId());
+ assertEquals(recoveryId1, participantResult1.getCompleteRecoveryId());
+ assertEquals(lraId, participantResult2.getCompleteLRAId());
+ assertEquals(recoveryId2, participantResult2.getCompleteRecoveryId());
+
+ // Compensate callbacks should not be called
+ assertNull(participantResult1.getCompensateLRAId());
+ assertNull(participantResult1.getCompensateRecoveryId());
+ assertNull(participantResult2.getCompensateLRAId());
+ assertNull(participantResult2.getCompensateRecoveryId());
+ }
+
+ @Test
+ public void testLRAExecutionFailure() {
+ Response response = getResponse("/participant1/work",
+ webTarget -> webTarget.queryParam("failLRA", "true"));
+ assertEquals(500, response.getStatus());
+ String lraId = response.readEntity(String.class);
+
+ response = getResponse("/participant1/result");
+ assertEquals(200, response.getStatus());
+ ParticipantResult participantResult1 = response.readEntity(ParticipantResult.class);
+
+ response = getResponse("/participant2/result");
+ assertEquals(200, response.getStatus());
+ ParticipantResult participantResult2 = response.readEntity(ParticipantResult.class);
+
+ assertEquals(lraId, participantResult1.getWorkLRAId());
+ String recoveryId1 = participantResult1.getWorkRecoveryId();
+
+ assertEquals(lraId, participantResult2.getWorkLRAId());
+ String recoveryId2 = participantResult2.getWorkRecoveryId();
+
+ // LRA canceled on failure, Compensate callbacks called
+ assertEquals(lraId, participantResult1.getCompensateLRAId());
+ assertEquals(recoveryId1, participantResult1.getCompensateRecoveryId());
+ assertEquals(lraId, participantResult2.getCompensateLRAId());
+ assertEquals(recoveryId2, participantResult2.getCompensateRecoveryId());
+
+ // Complete callbacks should not be called
+ assertNull(participantResult1.getCompleteLRAId());
+ assertNull(participantResult1.getCompleteRecoveryId());
+ assertNull(participantResult2.getCompleteLRAId());
+ assertNull(participantResult2.getCompleteRecoveryId());
+ }
+
+ private Response getResponse(String path) {
+ return getResponse(path, null);
+ }
+
+ private Response getResponse(String path, Function weTargetProcessor) {
+ WebTarget target = client.target(getServerHost())
+ .path(path);
+
+ if (weTargetProcessor != null) {
+ target = weTargetProcessor.apply(target);
+ }
+
+ return target.request().get();
+ }
+}
diff --git a/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/TestUtils.java b/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/TestUtils.java
new file mode 100644
index 0000000000..a20614b286
--- /dev/null
+++ b/microprofile-lra/src/test/java/org/wildfly/quickstarts/microprofile/lra/TestUtils.java
@@ -0,0 +1,16 @@
+package org.wildfly.quickstarts.microprofile.lra;
+
+public class TestUtils {
+ static final String DEFAULT_SERVER_HOST = "http://localhost:8080/microprofile-lra";
+
+ static String getServerHost() {
+ String serverHost = System.getenv("SERVER_HOST");
+ if (serverHost == null) {
+ serverHost = System.getProperty("server.host");
+ }
+ if (serverHost == null) {
+ serverHost = DEFAULT_SERVER_HOST;
+ }
+ return serverHost;
+ }
+}
diff --git a/pom.xml b/pom.xml
index d315311f55..d5fcfca8ce 100644
--- a/pom.xml
+++ b/pom.xml
@@ -335,6 +335,7 @@
microprofile-fault-tolerance
microprofile-health
microprofile-jwt
+ microprofile-lra
microprofile-openapi
microprofile-reactive-messaging-kafka
microprofile-rest-client
diff --git a/shared-doc/build-and-run-the-quickstart-with-bootable-jar.adoc b/shared-doc/build-and-run-the-quickstart-with-bootable-jar.adoc
index 89f097fe66..9ab46b12cd 100644
--- a/shared-doc/build-and-run-the-quickstart-with-bootable-jar.adoc
+++ b/shared-doc/build-and-run-the-quickstart-with-bootable-jar.adoc
@@ -25,6 +25,11 @@ ifdef::reactive-messaging[]
cloud-server
h2-default-datasource
microprofile-reactive-messaging-kafka
+endif::[]
+ifdef::microprofile-lra[]
+ jaxrs-server
+ microprofile-lra-coordinator
+ microprofile-lra-participant
endif::[]