Skip to content

Commit

Permalink
docs
Browse files Browse the repository at this point in the history
  • Loading branch information
danielkec committed Mar 12, 2024
1 parent 5dce964 commit c597948
Show file tree
Hide file tree
Showing 6 changed files with 528 additions and 11 deletions.
201 changes: 196 additions & 5 deletions docs/src/main/asciidoc/mp/lra.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
///////////////////////////////////////////////////////////////////////////////

Copyright (c) 2021, 2023 Oracle and/or its affiliates.
Copyright (c) 2021, 2024 Oracle and/or its affiliates.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,7 @@
:spec-version: 1.0-RC3
:spec-name: MicroProfile {feature-name} specification
:javadoc-link: https://download.eclipse.org/microprofile/microprofile-lra-{spec-version}/apidocs/org/eclipse/microprofile/lra/annotation/
:microtx-link: https://docs.oracle.com/en/database/oracle/transaction-manager-for-microservices/index.html
:rootdir: {docdir}/..
include::{rootdir}/includes/mp.adoc[]
Expand All @@ -36,6 +37,7 @@ include::{rootdir}/includes/mp.adoc[]
* <<API, API>>
* <<Configuration, Configuration>>
* <<Examples, Examples>>
* <<Testing, Testing>>
* <<Additional Information, Additional Information>>
** <<Coordinator, Coordinator>>
** <<Helidon LRA Coordinator, Helidon LRA Coordinator>>
Expand Down Expand Up @@ -394,6 +396,142 @@ public Response compensateExample(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) URI lraI
<5> Method which will be called by coordinator when LRA is completed
<6> Method which will be called by coordinator when LRA is canceled
== Testing
Testing of JAX-RS resources with LRA can be challenging as LRA participant running in parallel with the test is needed.
Helidon provides test coordinator which can be started automatically with additional socket on a random port within your
own Helidon application. You only need one extra test dependency to enable test coordinator in your xref:testing.adoc[@HelidonTest].
[source, xml]
.Dependency
----
<dependency>
<groupId>io.helidon.microprofile.lra</groupId>
<artifactId>helidon-microprofile-lra-testing</artifactId>
<scope>test</scope>
</dependency>
----
Considering that you have LRA enabled JAX-RS resource you want to test.
[source, java]
.Example JAX-RS resource with LRA.
----
@ApplicationScoped
@Path("/test")
public class WithdrawResource {
...
@PUT
@Path("/withdraw")
@LRA(LRA.Type.REQUIRES_NEW)
public Response withdraw(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) Optional<URI> lraId, String content) {
if ("BOOM".equals(content)) {
throw new IllegalArgumentException("BOOM");
}
return Response.ok().build();
}
@Complete
public void complete(URI lraId) {
completedLras.add(lraId.toString());
}
@Compensate
public void rollback(URI lraId) {
cancelledLras.add(lraId.toString());
}
...
----
Helidon test with enabled CDI discovery can look like this.
[source, java]
.HelidonTest with LRA test support.
----
@HelidonTest
//@AddBean(WithdrawResource.class) //<1>
@AddBean(TestLraCoordinator.class) //<2>
public class LraExternalResourceTest {
@Inject
private WithdrawResource withdrawTestResource;
@Inject
private TestLraCoordinator coordinator; //<3>
@Inject
private WebTarget target;
@Test
public void testLraComplete() {
try (Response res = target
.path("/test/withdraw")
.request()
.put(Entity.entity("test", MediaType.TEXT_PLAIN_TYPE))) {
assertThat(res.getStatus(), is(200));
String lraId = res.getHeaderString(LRA.LRA_HTTP_CONTEXT_HEADER);
Lra lra = coordinator.lra(lraId); //<4>
assertThat(lra.status(), is(LRAStatus.Closed)); //<5>
assertThat(withdrawTestResource.getCompletedLras(), contains(lraId));
}
}
----
<1> Resource is discovered automatically
<2> Test coordinator needs to be added manually
<3> Injecting test coordinator to access state of LRA managed by coordinator mid-test
<4> Retrieving LRA managed by coordinator by LraId
<5> Asserting LRA state in coordinator
LRA testing feature has the following default configuration:
* port: `0` - coordinator is started on random port(Helidon LRA participant is capable to discover test coordinator automatically)
* bind-address: `localhost` - bind address of the coordinator
* helidon.lra.coordinator.persistence: `false` - LRAs managed by test coordinator are not persisted
* helidon.lra.participant.use-build-time-index: `false` - Participant annotation inspection ignores Jandex index files created in build time, it helps to avoid issues with additional test resources
Testing LRA coordinator is started on additional named socket `test-lra-coordinator` configured with default index `500`.
Default index can be changed with system property `helidon.lra.coordinator.test-socket.index`.
Example: `-Dhelidon.lra.coordinator.test-socket.index=20`.
[source, java]
.HelidonTest override LRA test feature default settings.
----
@HelidonTest
@AddBean(TestLraCoordinator.class)
@AddConfig(key = "server.sockets.500.port", value = "8070") //<1>
@AddConfig(key = "server.sockets.500.bind-address", value = "custom.bind.name") //<2>
@AddConfig(key = "helidon.lra.coordinator.persistence", value = "true") //<3>
@AddConfig(key = "helidon.lra.participant.use-build-time-index", value = "true") //<4>
public class LraTest {
...
}
----
<1> Start test LRA coordinator always on the same port 8070(default is random port)
<2> Test LRA coordinator socket bind address (default is localhost)
<3> Persist LRA managed by coordinator(default is false)
<4> Use build time Jandex index(default is false)
When CDI bean auto-discovery is not desired, LRA and Config CDI extensions needs to be added manually.
[source, java]
.HelidonTest setup with disabled discovery.
----
@HelidonTest
@DisableDiscovery
@AddJaxRs
@AddBean(TestLraCoordinator.class)
@AddExtension(LraCdiExtension.class)
@AddExtension(ConfigCdiExtension.class)
@AddBean(WithdrawTestResource.class)
public class LraTest {
...
}
----
== Additional Information
=== Coordinator
Expand All @@ -405,9 +543,61 @@ In addition, participant also keeps track of timeouts, retries participant calls
* Helidon LRA coordinator
* https://narayana.io/lra[Narayana coordinator].
=== MicroTx LRA Coordinator
Oracle Transaction Manager for Microservices - {microtx-link}[MicroTx] is an enterprise grade transaction manager for microservices,
among other it manages LRA transactions and is compatible with Narayana LRA clients.
MicroTx LRA coordinator is compatible with Narayana clients when `narayanaLraCompatibilityMode` is on,
you need to add another dependency to enable Narayana client:
[source,xml]
.Dependency needed for using Helidon LRA with Narayana compatible coordinator
----
<dependency>
<groupId>io.helidon.lra</groupId>
<artifactId>helidon-lra-coordinator-narayana-client</artifactId>
</dependency>
----
[source, bash]
.Run MicroTx in Docker
----
docker container run --name otmm -v "$(pwd)":/app/config \
-w /app/config -p 8080:8080/tcp --env CONFIG_FILE=tcs.yaml \
--add-host host.docker.internal:host-gateway -d tmm:<version>
----
To use MicroTx with Helidon LRA participant, `narayanaLraCompatibilityMode` needs to be enabled.
[source, yaml]
.Configure MicroTx for development
----
tmmAppName: tcs
tmmConfiguration:
listenAddr: 0.0.0.0:8080
internalAddr: http://host.docker.internal:8080
externalUrl: http://lra-coordinator.acme.com:8080
xaCoordinator:
enabled: false
lraCoordinator:
enabled: true
tccCoordinator:
enabled: false
storage:
type: memory
authentication:
enabled: false
authorization:
enabled: false
serveTLS:
enabled: false
narayanaLraCompatibilityMode:
enabled: true #<1>
----
<1> Enable Narayana compatibility mode
=== Helidon LRA Coordinator
CAUTION: Experimental tool, usage in production is not advised.
CAUTION: Test tool, usage in production is not advised.
[source,bash]
.Build and run Helidon LRA coordinator
Expand All @@ -416,7 +606,7 @@ docker build -t helidon/lra-coordinator https://github.com/oracle/helidon.git#:l
docker run --name lra-coordinator --network="host" helidon/lra-coordinator
----
Helidon LRA coordinator is compatible with Narayana clients, you need to add an additional dependency for Narayana client:
Helidon LRA coordinator is compatible with Narayana clients, you need to add another dependency for Narayana client:
[source,xml]
.Dependency needed for using Helidon LRA with Narayana compatible coordinator
----
Expand All @@ -428,7 +618,7 @@ Helidon LRA coordinator is compatible with Narayana clients, you need to add an
=== Narayana
https://narayana.io[Narayana] is a transaction manager supporting LRA.
To use Narayana LRA coordinator with Helidon LRA client you need to add an additional dependency for Narayana client:
To use Narayana LRA coordinator with Helidon LRA client you need to add another dependency for Narayana client:
[source,xml]
.Dependency needed for using Helidon LRA with Narayana coordinator
Expand Down Expand Up @@ -459,4 +649,5 @@ with port `8070` defined in the snippet above you need to configure your Helidon
* https://github.com/eclipse/microprofile-lra[MicroProfile LRA GitHub Repository]
* {microprofile-lra-spec-url}[{spec-name}]
* https://download.eclipse.org/microprofile/microprofile-lra-{spec-version}/apidocs/org/eclipse/microprofile/lra/[Microprofile LRA JavaDoc]
* https://helidon.io/docs/v4/apidocs/io.helidon.lra.coordinator.client/module-summary.html[Helidon LRA Client JavaDoc]
* https://helidon.io/docs/v4/apidocs/io.helidon.lra.coordinator.client/module-summary.html[Helidon LRA Client JavaDoc]
* {microtx-link}[MicroTx - Oracle Transaction Manager for Microservices]
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* 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 io.helidon.microprofile.testing.lra;

import java.net.URI;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import io.helidon.lra.coordinator.Lra;
import io.helidon.microprofile.config.ConfigCdiExtension;
import io.helidon.microprofile.lra.LraCdiExtension;
import io.helidon.microprofile.testing.junit5.AddBean;
import io.helidon.microprofile.testing.junit5.AddExtension;
import io.helidon.microprofile.testing.junit5.AddJaxRs;
import io.helidon.microprofile.testing.junit5.DisableDiscovery;
import io.helidon.microprofile.testing.junit5.HelidonTest;

import jakarta.inject.Inject;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
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.LRAStatus;
import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;

@HelidonTest
@DisableDiscovery
@AddJaxRs
@AddBean(TestLraCoordinator.class)
@AddExtension(LraCdiExtension.class)
@AddExtension(ConfigCdiExtension.class)
@Path("/test/internal")
public class LraDisabledDiscoveryResourceTest {

private final WebTarget target;
private final Set<String> completedLras;
private final Set<String> cancelledLras;

@Inject
private TestLraCoordinator coordinator;

@Inject
public LraDisabledDiscoveryResourceTest(WebTarget target) {
this.target = target;
this.completedLras = new CopyOnWriteArraySet<>();
this.cancelledLras = new CopyOnWriteArraySet<>();
}

@PUT
@Path("/withdraw")
@LRA(LRA.Type.REQUIRES_NEW)
public Response withdraw(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) Optional<URI> lraId, String content) {
if ("BOOM".equals(content)) {
throw new IllegalArgumentException("BOOM");
}
return Response.ok().build();
}

@Complete
public void complete(URI lraId) {
completedLras.add(lraId.toString());
}

@Compensate
public void rollback(URI lraId) {
cancelledLras.add(lraId.toString());
}

@Test
public void testLraComplete() {
try (Response res = target
.path("/test/internal/withdraw")
.request()
.put(Entity.entity("test", MediaType.TEXT_PLAIN_TYPE))) {
assertThat(res.getStatus(), is(200));
String lraId = res.getHeaderString(LRA.LRA_HTTP_CONTEXT_HEADER);
Lra lra = coordinator.lra(lraId);
assertThat(lra.status(), is(LRAStatus.Closed));
assertThat(completedLras, contains(lraId));
}
}

@Test
public void testLraCompensate() {
try (Response res = target
.path("/test/internal/withdraw")
.request()
.put(Entity.entity("BOOM", MediaType.TEXT_PLAIN_TYPE))) {
assertThat(res.getStatus(), is(500));
String lraId = res.getHeaderString(LRA.LRA_HTTP_CONTEXT_HEADER);
Lra lra = coordinator.lra(lraId);
assertThat(lra.status(), is(LRAStatus.Cancelled));
assertThat(cancelledLras, contains(lraId));
}
}
}
Loading

0 comments on commit c597948

Please sign in to comment.