diff --git a/saga/README.adoc b/saga/README.adoc
index 950c235b..4152370a 100644
--- a/saga/README.adoc
+++ b/saga/README.adoc
@@ -8,12 +8,16 @@ and other general information.
=== How it works
-There are 4 services as participants of the Saga:
+There are 5 modules in this example:
-- payment-service: it emulates a real payment transaction and it will be used by both flight-service and train-service
+**Service Modules:**
+- app: is the starting point and it emulates a user that starts the transaction to buy both flight and train tickets
- flight-service: it emulates the booking of a flight ticket and it uses the payment-service to execute a payment transaction
- train-service: it emulates the reservation of a train seat and it uses the payment-service to execute a payment transaction
-- app: is the starting point and it emulates a user that starts the transaction to buy both flight and train tickets
+- payment-service: it emulates a real payment transaction and it will be used by both flight-service and train-service
+
+**Test Module:**
+- integration-tests: contains automated integration tests using Testcontainers for Docker-based testing
The starting point is a REST endpoint that creates a request for a new reservation
and there is 15% probability that the payment service fails.
@@ -101,6 +105,21 @@ Transaction http://localhost:8080/lra-coordinator/0_ffff7f000001_8aad_62d16f11_7
----
+=== Running Tests
+
+The integration tests use Testcontainers to automatically start Artemis and LRA Coordinator in Docker.
+
+[source,shell]
+----
+# Ensure Docker is running
+docker ps
+
+# Run integration tests
+mvn clean test -pl saga-integration-tests
+----
+
+See link:saga-integration-tests/README.md[saga-integration-tests/README.md] for more details.
+
=== Package and run the application
Once you are done with developing you may want to package and run the application.
diff --git a/saga/pom.xml b/saga/pom.xml
index 711d5e18..f2c9dbf0 100644
--- a/saga/pom.xml
+++ b/saga/pom.xml
@@ -44,6 +44,7 @@
2.29.0
1.13.0
+ 3.0.1
5.0.0
3.15.0
3.5.0
@@ -56,6 +57,7 @@
saga-flight-service
saga-payment-service
saga-train-service
+ saga-integration-tests
@@ -87,6 +89,10 @@
org.apache.camel.quarkus
camel-quarkus-core
+
+ org.apache.camel.quarkus
+ camel-quarkus-bean
+
org.apache.camel.quarkus
camel-quarkus-direct
@@ -104,13 +110,6 @@
quarkus-artemis-jms
${quarkiverse-artemis.version}
-
-
-
- io.quarkus
- quarkus-junit
- test
-
@@ -182,6 +181,12 @@
${maven-jar-plugin.version}
+
+ io.smallrye
+ jandex-maven-plugin
+ ${jandex-maven-plugin.version}
+
+
com.mycila
license-maven-plugin
diff --git a/saga/saga-app/pom.xml b/saga/saga-app/pom.xml
index 0e71248c..d1c0ed07 100644
--- a/saga/saga-app/pom.xml
+++ b/saga/saga-app/pom.xml
@@ -31,4 +31,21 @@
Camel Quarkus :: Examples :: Saga :: App
Main application starting SAGA
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+ make-index
+
+ jandex
+
+
+
+
+
+
+
diff --git a/saga/saga-app/src/main/java/org/apache/camel/example/saga/SagaRoute.java b/saga/saga-app/src/main/java/org/apache/camel/example/saga/SagaRoute.java
index d1985326..6f31b1df 100644
--- a/saga/saga-app/src/main/java/org/apache/camel/example/saga/SagaRoute.java
+++ b/saga/saga-app/src/main/java/org/apache/camel/example/saga/SagaRoute.java
@@ -16,9 +16,11 @@
*/
package org.apache.camel.example.saga;
+import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.RestParamType;
+@ApplicationScoped
public class SagaRoute extends RouteBuilder {
@Override
@@ -33,12 +35,15 @@ public void configure() throws Exception {
.compensation("direct:cancelOrder")
.log("Executing saga #${header.id} with LRA ${header.Long-Running-Action}")
.setHeader("payFor", constant("train"))
+ // Request timeout prevents indefinite waits during service failures or slow responses
.to("jms:queue:{{example.services.train}}?exchangePattern=InOut" +
- "&replyTo={{example.services.train}}.reply")
+ "&replyTo={{example.services.train}}.reply" +
+ "&requestTimeout=30000")
.log("train seat reserved for saga #${header.id} with payment transaction: ${body}")
.setHeader("payFor", constant("flight"))
.to("jms:queue:{{example.services.flight}}?exchangePattern=InOut" +
- "&replyTo={{example.services.flight}}.reply")
+ "&replyTo={{example.services.flight}}.reply" +
+ "&requestTimeout=30000")
.log("flight booked for saga #${header.id} with payment transaction: ${body}")
.setBody(header("Long-Running-Action"))
.end();
diff --git a/saga/saga-flight-service/pom.xml b/saga/saga-flight-service/pom.xml
index 79f59c2e..85b18515 100644
--- a/saga/saga-flight-service/pom.xml
+++ b/saga/saga-flight-service/pom.xml
@@ -31,4 +31,21 @@
Camel Quarkus :: Examples :: Saga :: Flight Service
Flight Service
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+ make-index
+
+ jandex
+
+
+
+
+
+
+
diff --git a/saga/saga-flight-service/src/main/java/org/apache/camel/example/saga/FlightRoute.java b/saga/saga-flight-service/src/main/java/org/apache/camel/example/saga/FlightRoute.java
index 8e0bd7e0..04694f18 100644
--- a/saga/saga-flight-service/src/main/java/org/apache/camel/example/saga/FlightRoute.java
+++ b/saga/saga-flight-service/src/main/java/org/apache/camel/example/saga/FlightRoute.java
@@ -16,9 +16,11 @@
*/
package org.apache.camel.example.saga;
+import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.SagaPropagation;
+@ApplicationScoped
public class FlightRoute extends RouteBuilder {
@Override
@@ -27,14 +29,16 @@ public void configure() throws Exception {
.saga()
.propagation(SagaPropagation.MANDATORY)
.option("id", header("id"))
- .compensation("direct:cancelPurchase")
+ .compensation("direct:cancelFlightPurchase")
.log("Buying flight #${header.id}")
+ // Request timeout prevents indefinite waits during payment service failures
.to("jms:queue:{{example.services.payment}}?exchangePattern=InOut" +
- "&replyTo={{example.services.payment}}.flight.reply")
+ "&replyTo={{example.services.payment}}.flight.reply" +
+ "&requestTimeout=30000")
.log("Payment for flight #${header.id} done with transaction ${body}")
.end();
- from("direct:cancelPurchase")
+ from("direct:cancelFlightPurchase")
.log("Flight purchase #${header.id} has been cancelled due to payment failure");
}
diff --git a/saga/saga-integration-tests/README.md b/saga/saga-integration-tests/README.md
new file mode 100644
index 00000000..86a3f139
--- /dev/null
+++ b/saga/saga-integration-tests/README.md
@@ -0,0 +1,126 @@
+# Saga Integration Tests
+
+Integration tests for the Camel Quarkus Saga example demonstrating distributed transaction coordination using the LRA (Long Running Action) pattern with JMS messaging.
+
+## Overview
+
+This module tests the Saga example by running all services (train, flight, payment) in a single JVM with Testcontainers managing external dependencies (LRA Coordinator and Artemis broker).
+
+## Test Coverage
+
+The test suite verifies:
+
+- **LRA Integration:** Saga coordination with LRA coordinator
+- **JMS Messaging:** Request-reply pattern over Artemis queues
+- **Service Participation:** Train, flight, and payment service coordination
+- **Compensation Flow:** Rollback when failures occur (15% random failure rate)
+- **End-to-End Flow:** Complete saga orchestration
+
+### Test Case
+
+`testSagaWithLRAAndRandomOutcomes()` - Comprehensive end-to-end test that verifies the complete saga flow including LRA coordination, all service participation (train, flight, payment), and validates both success and compensation scenarios. Since the payment service has a 15% random failure rate, the test accepts either outcome as valid.
+
+## Running Tests
+
+### Prerequisites
+
+- Java 17+
+- Maven 3.9+
+- Docker (for Testcontainers)
+
+### Execute Tests
+
+```bash
+# Run all tests
+mvn clean test -pl saga-integration-tests
+
+# Run specific test
+mvn test -pl saga-integration-tests -Dtest=SagaBasicTest#testCompleteSagaFlow
+
+# Native mode
+mvn clean verify -Pnative -pl saga-integration-tests
+```
+
+## Infrastructure
+
+**Testcontainers manages:**
+
+- **LRA Coordinator** (`quay.io/jbosstm/lra-coordinator:latest`) - Distributed saga coordination
+- **Artemis Broker** (`quay.io/artemiscloud/activemq-artemis-broker:latest`) - JMS messaging
+
+Both containers run on a shared Docker network with proper wait strategies.
+
+## Configuration
+
+### Test Settings (`src/test/resources/application.yml`)
+
+Key configuration settings:
+
+```yaml
+quarkus:
+ http:
+ port: 8084
+ log:
+ file:
+ enable: true
+ path: target/quarkus.log
+ category:
+ "org.apache.camel": DEBUG
+ "org.apache.camel.saga": DEBUG
+ "org.apache.camel.component.lra": DEBUG
+
+camel:
+ lra:
+ enabled: true
+ # coordinator-url and local-participant-url are set by SagaTestResource
+ component:
+ jms:
+ test-connection-on-startup: true
+ concurrent-consumers: 5
+```
+
+Dynamic configuration (Artemis URL, LRA coordinator URL) is injected by `SagaTestResource` at test runtime.
+
+## Saga Flow
+
+```
+POST /api/saga?id=1
+ → SagaRoute creates LRA transaction
+ → Sends to jms:queue:saga-train-service
+ → TrainRoute processes and sends to jms:queue:saga-payment-service
+ → PaymentRoute completes payment
+ → Sends to jms:queue:saga-flight-service
+ → FlightRoute processes and sends to jms:queue:saga-payment-service
+ → PaymentRoute completes payment
+ → LRA Coordinator commits saga
+ → Returns LRA ID
+```
+
+## Troubleshooting
+
+### Tests Fail with "Connection Refused"
+
+Docker not running. Start Docker and verify:
+```bash
+docker ps
+```
+
+### Tests Timeout
+
+Increase timeout in tests:
+```java
+await().atMost(30, TimeUnit.SECONDS) // Instead of 10-15
+```
+
+### View Container Logs
+
+```bash
+docker ps # Find container ID
+docker logs
+```
+
+## Related Links
+
+- [Camel Saga EIP](https://camel.apache.org/components/latest/eips/saga-eip.html)
+- [Camel LRA Component](https://camel.apache.org/components/latest/lra-component.html)
+- [Issue #6195](https://github.com/apache/camel-quarkus/issues/6195)
diff --git a/saga/saga-integration-tests/pom.xml b/saga/saga-integration-tests/pom.xml
new file mode 100644
index 00000000..522240e0
--- /dev/null
+++ b/saga/saga-integration-tests/pom.xml
@@ -0,0 +1,148 @@
+
+
+
+
+ 4.0.0
+
+
+ camel-quarkus-examples-saga
+ org.apache.camel.quarkus.examples
+ 3.32.0
+
+
+ camel-quarkus-example-saga-integration-tests
+ Camel Quarkus :: Examples :: Saga :: Integration Tests
+ Integration tests for Saga example
+
+
+
+
+ org.apache.camel.quarkus.examples
+ camel-quarkus-example-saga-app
+ ${project.version}
+
+
+ org.apache.camel.quarkus.examples
+ camel-quarkus-example-saga-flight
+ ${project.version}
+
+
+ org.apache.camel.quarkus.examples
+ camel-quarkus-example-saga-train
+ ${project.version}
+
+
+ org.apache.camel.quarkus.examples
+ camel-quarkus-example-saga-payment
+ ${project.version}
+
+
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+ org.testcontainers
+ testcontainers
+ test
+
+
+
+
+
+
+ ${quarkus.platform.group-id}
+ quarkus-maven-plugin
+
+
+ build
+
+ build
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+ org.jboss.logmanager.LogManager
+
+
+
+
+
+
+
+
+ native
+
+
+ native
+
+
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+
+
+ skip-testcontainers-tests
+
+
+ skip-testcontainers-tests
+
+
+
+ true
+
+
+
+
+
diff --git a/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaBasicIT.java b/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaBasicIT.java
new file mode 100644
index 00000000..c27b6790
--- /dev/null
+++ b/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaBasicIT.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.example.saga;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+/**
+ * Integration test for native mode compilation.
+ * Extends SagaBasicTest to run the same tests against native executable.
+ */
+@QuarkusIntegrationTest
+public class SagaBasicIT extends SagaBasicTest {
+ // Runs all tests from SagaBasicTest in native mode
+}
diff --git a/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaBasicTest.java b/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaBasicTest.java
new file mode 100644
index 00000000..53420cd9
--- /dev/null
+++ b/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaBasicTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.example.saga;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Basic integration tests for Saga example.
+ * Tests verify saga orchestration, service participation, routing, and compensation.
+ * Note: Payment service has 15% random failure rate to test compensation scenarios.
+ */
+@QuarkusTest
+@QuarkusTestResource(SagaTestResource.class)
+public class SagaBasicTest {
+
+ private static final String LOG_FILE = "target/quarkus.log";
+
+ /**
+ * Test saga orchestration with LRA - accepts both success and compensation outcomes.
+ * Payment service has 15% random failure rate, so either scenario is valid.
+ */
+ @Test
+ public void testSagaWithLRAAndRandomOutcomes() throws Exception {
+ // Trigger saga asynchronously (may timeout on payment failure, which is expected)
+ CompletableFuture.runAsync(() -> {
+ try {
+ RestAssured.given()
+ .queryParam("id", 1)
+ .post("/api/saga");
+ } catch (Exception e) {
+ // Expected - request may timeout on payment failure
+ }
+ });
+
+ // Wait for saga to start and process fully
+ // In native mode, we need to wait longer for all messages to be logged
+ await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> {
+ String log = new String(Files.readAllBytes(Paths.get(LOG_FILE)), StandardCharsets.UTF_8);
+ assertTrue(log.contains("Executing saga #1"), "Saga should start with LRA");
+
+ // Wait until we see evidence of completion (success or failure)
+ boolean completed = log.contains("done for order #1")
+ || log.contains("fails!")
+ || log.contains("cancelled");
+ assertTrue(completed, "Saga should complete with either success or compensation");
+ });
+
+ // Give logs a moment to flush (important in native mode)
+ Thread.sleep(1000);
+
+ String log = new String(Files.readAllBytes(Paths.get(LOG_FILE)), StandardCharsets.UTF_8);
+
+ // Verify LRA coordinator is used
+ assertTrue(log.contains("lra-coordinator"), "Should use LRA coordinator");
+
+ // Verify services participated
+ assertTrue(log.contains("Buying train") || log.contains("Buying flight"),
+ "Services should participate");
+
+ // Check outcome - either success or compensation is valid
+ boolean hasSuccess = log.contains("done for order #1");
+ boolean hasFailure = log.contains("fails!");
+ boolean hasCompensation = log.contains("cancelled");
+
+ if (hasFailure || hasCompensation) {
+ System.out.println("Saga #1: Compensation scenario tested (payment failed, saga rolled back)");
+ } else if (hasSuccess) {
+ System.out.println("Saga #1: Success scenario tested (all payments completed)");
+ }
+
+ // Either outcome is valid - test passes
+ assertTrue(hasSuccess || hasFailure,
+ "Saga should complete with either success or compensation");
+ }
+}
diff --git a/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaTestResource.java b/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaTestResource.java
new file mode 100644
index 00000000..235093bc
--- /dev/null
+++ b/saga/saga-integration-tests/src/test/java/org/apache/camel/example/saga/SagaTestResource.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.example.saga;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.Network;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+/**
+ * Testcontainers resource for Saga integration tests.
+ * Manages the lifecycle of LRA Coordinator and Artemis broker.
+ */
+public class SagaTestResource implements QuarkusTestResourceLifecycleManager {
+
+ private static final String LRA_IMAGE = "quay.io/jbosstm/lra-coordinator:latest";
+ private static final String ARTEMIS_IMAGE = "quay.io/artemiscloud/activemq-artemis-broker:latest";
+ private static final int LRA_PORT = 8080;
+ private static final int ARTEMIS_PORT = 61616;
+
+ private GenericContainer> lraContainer;
+ private GenericContainer> artemisContainer;
+ private Network network;
+
+ @Override
+ public Map start() {
+ network = Network.newNetwork();
+
+ // Start Artemis broker
+ artemisContainer = new GenericContainer<>(ARTEMIS_IMAGE)
+ .withNetwork(network)
+ .withNetworkAliases("artemis")
+ .withEnv("AMQ_USER", "admin")
+ .withEnv("AMQ_PASSWORD", "admin")
+ .withExposedPorts(ARTEMIS_PORT)
+ .waitingFor(Wait.forListeningPort()
+ .withStartupTimeout(Duration.ofSeconds(60)));
+
+ artemisContainer.start();
+
+ // Start LRA Coordinator
+ lraContainer = new GenericContainer<>(LRA_IMAGE)
+ .withNetwork(network)
+ .withNetworkAliases("lra-coordinator")
+ .withEnv("QUARKUS_HTTP_PORT", String.valueOf(LRA_PORT))
+ .withExposedPorts(LRA_PORT)
+ .withExtraHost("host.testcontainers.internal", "host-gateway")
+ .waitingFor(Wait.forHttp("/lra-coordinator")
+ .forPort(LRA_PORT)
+ .forStatusCode(200)
+ .withStartupTimeout(Duration.ofSeconds(90)));
+
+ lraContainer.start();
+
+ Map config = new HashMap<>();
+
+ // Artemis configuration
+ String artemisUrl = String.format("tcp://%s:%d",
+ artemisContainer.getHost(),
+ artemisContainer.getMappedPort(ARTEMIS_PORT));
+ config.put("quarkus.artemis.url", artemisUrl);
+ config.put("quarkus.artemis.username", "admin");
+ config.put("quarkus.artemis.password", "admin");
+
+ // LRA configuration
+ String lraCoordinatorUrl = String.format("http://%s:%d",
+ lraContainer.getHost(),
+ lraContainer.getMappedPort(LRA_PORT));
+ config.put("camel.lra.coordinator-url", lraCoordinatorUrl);
+
+ // Set local participant URL - use host.testcontainers.internal for coordinator callbacks
+ config.put("camel.lra.local-participant-url", "http://host.testcontainers.internal:8084/api");
+
+ // Allow external connections
+ config.put("quarkus.http.host", "0.0.0.0");
+
+ return config;
+ }
+
+ @Override
+ public void stop() {
+ if (artemisContainer != null) {
+ artemisContainer.stop();
+ }
+ if (lraContainer != null) {
+ lraContainer.stop();
+ }
+ if (network != null) {
+ network.close();
+ }
+ }
+}
diff --git a/saga/saga-integration-tests/src/test/resources/application.yml b/saga/saga-integration-tests/src/test/resources/application.yml
new file mode 100644
index 00000000..13da7f78
--- /dev/null
+++ b/saga/saga-integration-tests/src/test/resources/application.yml
@@ -0,0 +1,68 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+# Test configuration
+# External dependencies (Artemis, LRA) are managed by SagaTestResource
+
+quarkus:
+ http:
+ port: 8084
+ log:
+ console:
+ format: "%d{HH:mm:ss} %-5p [%c{2.}] %s%e%n"
+ level: INFO
+ min-level: DEBUG
+ file:
+ enable: true
+ path: target/quarkus.log
+ category:
+ "org.apache.camel":
+ level: DEBUG
+ "org.apache.camel.saga":
+ level: DEBUG
+ "org.apache.camel.component.lra":
+ level: DEBUG
+ index-dependency:
+ saga-app:
+ group-id: org.apache.camel.quarkus.examples
+ artifact-id: camel-quarkus-example-saga-app
+ saga-flight:
+ group-id: org.apache.camel.quarkus.examples
+ artifact-id: camel-quarkus-example-saga-flight
+ saga-train:
+ group-id: org.apache.camel.quarkus.examples
+ artifact-id: camel-quarkus-example-saga-train
+ saga-payment:
+ group-id: org.apache.camel.quarkus.examples
+ artifact-id: camel-quarkus-example-saga-payment
+
+camel:
+ rest:
+ context-path: /api
+ component:
+ jms:
+ test-connection-on-startup: true
+ concurrent-consumers: 5
+ lra:
+ enabled: true
+ # coordinator-url and local-participant-url are set by SagaTestResource
+
+example:
+ services:
+ train: saga-train-service
+ flight: saga-flight-service
+ payment: saga-payment-service
diff --git a/saga/saga-payment-service/pom.xml b/saga/saga-payment-service/pom.xml
index 4dccbd08..84477541 100644
--- a/saga/saga-payment-service/pom.xml
+++ b/saga/saga-payment-service/pom.xml
@@ -31,4 +31,21 @@
Camel Quarkus :: Examples :: Saga :: Payment Service
Payment Service
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+ make-index
+
+ jandex
+
+
+
+
+
+
+
diff --git a/saga/saga-payment-service/src/main/java/org/apache/camel/example/saga/PaymentRoute.java b/saga/saga-payment-service/src/main/java/org/apache/camel/example/saga/PaymentRoute.java
index 342a3d87..3b2fd20b 100644
--- a/saga/saga-payment-service/src/main/java/org/apache/camel/example/saga/PaymentRoute.java
+++ b/saga/saga-payment-service/src/main/java/org/apache/camel/example/saga/PaymentRoute.java
@@ -16,9 +16,11 @@
*/
package org.apache.camel.example.saga;
+import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.SagaPropagation;
+@ApplicationScoped
public class PaymentRoute extends RouteBuilder {
@Override
@@ -33,7 +35,7 @@ public void configure() throws Exception {
.log("Paying ${header.payFor} for order #${header.id}")
.setBody(header("JMSCorrelationID"))
.choice()
- .when(x -> Math.random() >= 0.85)
+ .when(simple("${random(0,100)} >= 85"))
.log("Payment ${header.payFor} for saga #${header.id} fails!")
.throwException(new RuntimeException("Random failure during payment"))
.endChoice()
diff --git a/saga/saga-train-service/pom.xml b/saga/saga-train-service/pom.xml
index d9428f6c..d00364fa 100644
--- a/saga/saga-train-service/pom.xml
+++ b/saga/saga-train-service/pom.xml
@@ -31,4 +31,21 @@
Camel Quarkus :: Examples :: Saga :: Train Service
Train Service
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+ make-index
+
+ jandex
+
+
+
+
+
+
+
diff --git a/saga/saga-train-service/src/main/java/org/apache/camel/example/saga/TrainRoute.java b/saga/saga-train-service/src/main/java/org/apache/camel/example/saga/TrainRoute.java
index bf5b05a6..ef339ef4 100644
--- a/saga/saga-train-service/src/main/java/org/apache/camel/example/saga/TrainRoute.java
+++ b/saga/saga-train-service/src/main/java/org/apache/camel/example/saga/TrainRoute.java
@@ -16,9 +16,11 @@
*/
package org.apache.camel.example.saga;
+import jakarta.enterprise.context.ApplicationScoped;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.SagaPropagation;
+@ApplicationScoped
public class TrainRoute extends RouteBuilder {
@Override
@@ -27,14 +29,16 @@ public void configure() throws Exception {
.saga()
.propagation(SagaPropagation.MANDATORY)
.option("id", header("id"))
- .compensation("direct:cancelPurchase")
+ .compensation("direct:cancelTrainPurchase")
.log("Buying train #${header.id}")
+ // Request timeout prevents indefinite waits during payment service failures
.to("jms:queue:{{example.services.payment}}?exchangePattern=InOut" +
- "&replyTo={{example.services.payment}}.train.reply")
+ "&replyTo={{example.services.payment}}.train.reply" +
+ "&requestTimeout=30000")
.log("Payment for train #${header.id} done with transaction ${body}")
.end();
- from("direct:cancelPurchase")
+ from("direct:cancelTrainPurchase")
.log("Train purchase #${header.id} has been cancelled due to payment failure");
}