You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by "oscerd (via GitHub)" <gi...@apache.org> on 2026/10/13 09:43:47 UTC

[PR] [backport camel-4.18.x] CAMEL-25584: camel-lra - only join LRAs hosted by the configured LRA coordinator [camel]

oscerd opened a new pull request, #27772:
URL: https://github.com/apache/camel/pull/27772

   ## Backport of #27725
   
   Cherry-pick of #27725 onto `camel-4.18.x`.
   
   **Original PR:** #27725 - CAMEL-25584: camel-lra - only join LRAs hosted by the configured LRA coordinator
   **Original author:** @oscerd
   **Target branch:** `camel-4.18.x`
   
   Not a straight cherry-pick, so it goes through a normal review:
   - `LRASagaService` is ported by hand: `camel-4.18.x` predates CAMEL-24150 and CAMEL-24192, so only the CAMEL-25584 changes are applied (the `allowedCoordinatorUrls` option, the check in `getSaga()` and its helpers, the startup validation), without the unrelated main changes around them.
   - `LRAPropagatedSagaTest` uses `camel-test-junit5`, as the rest of the module on this branch.
   - The upgrade-guide notes are left out, as they live on `main` (the 4.18.6 note is already in main's `camel-4x-upgrade-guide-4_18.adoc`).
   
   Tested on the branch: the camel-lra unit tests, a full reactor build without tests, and the `LRA*IT` tests against the LRA coordinator container. `LRAFailuresIT.testCompletionAfterFailures` fails on this branch with and without this change (checked on a clean `camel-4.18.x`): the branch does not have the CAMEL-23905 test fix that triggers the coordinator recovery. The other 11 `LRA*IT` tests pass.
   
   ### Original description
   
   ## Description
   
   [CAMEL-25584](https://issues.apache.org/jira/browse/CAMEL-25584)
   
   When a message with a `Long-Running-Action` header reaches a saga route, the LRA saga service joins that LRA by calling the URL in the header, and `saga:complete` / `saga:compensate` close or cancel it the same way. That URL was not checked against the configured LRA coordinator.
   
   - `LRASagaService.getSaga()`, which every lookup of an existing LRA goes through, now accepts an LRA only when its URL is under the configured coordinator (`coordinatorUrl` + `coordinatorContextPath`): same scheme, host and port (read through `java.net.URL`, so host names with an underscore work; case-insensitive, default ports normalized), no user info, no path parameter or encoded dot, slash or backslash in the path (`;`, `%2e`, `%2f`, `%5c`), and a normalized path under the coordinator path. Otherwise the lookup fails with an `IllegalArgumentException` that names the option to use, and the exchange fails without any call to that URL.
   - New `allowedCoordinatorUrls` option, also `camel.lra.allowedCoordinatorUrls` in camel-main: comma-separated further coordinator URLs to accept, for participants that reach the same coordinator under different URLs. An entry that is not an `http` or `https` URL with a host fails at startup.
   - LRAs started by the service come from the configured coordinator and keep working: the LRA ITs against the coordinator container (multi-route `MANDATORY` sagas, manual completion) pass unchanged.
   - Docs: the Saga EIP page. Upgrade guide: a `camel-lra` section in the 4.24 guide, plus the 4.22.2 and 4.18.6 sections on main, as a message carrying the `Long-Running-Action` of another coordinator now fails. They also say that the LRAs the service starts itself are checked too, for a coordinator behind a proxy that returns LRA URLs under another base URL.
   
   ## Tests
   
   - `LRASagaServiceAllowedCoordinatorTest`: accepted and rejected LRA URLs (host, port, scheme, case, default port, context path, `..` segments, encoded and `;` dot segments, a sibling path prefix, user info, host names with an underscore, allowed coordinator URLs), and the startup validation.
   - `LRAPropagatedSagaTest`: through the Saga EIP, a propagated LRA of the configured coordinator is joined, and one of another coordinator fails the exchange with no call to the LRA client.
   - All the `LRA*IT` tests against the LRA coordinator container: 12 tests, green locally.
   
   ## Notes
   
   - This touches the same files as #27719 (`LRASagaService`, `LraConfigurationProperties`, the Saga EIP page and the generated camel-main metadata), so whichever merges second needs a rebase and a regen.
   - camel-spring-boot: `camel-lra-starter` sets the LRA options one by one, so it needs `allowedCoordinatorUrls` too, in the same releases, tracked in [CAMEL-25590](https://issues.apache.org/jira/browse/CAMEL-25590).
   - Backports to `camel-4.22.x` and `camel-4.18.x` after this is merged.
   
   _Claude Code on behalf of oscerd_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@camel.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [backport camel-4.18.x] CAMEL-25584: camel-lra - only join LRAs hosted by the configured LRA coordinator [camel]

Posted by "oscerd (via GitHub)" <gi...@apache.org>.
oscerd commented on code in PR #27772:
URL: https://github.com/apache/camel/pull/27772#discussion_r4257210408


##########
components/camel-lra/src/main/java/org/apache/camel/service/lra/LRASagaService.java:
##########
@@ -75,17 +84,107 @@ public boolean isLongRunningActionHeaderSupported() {
         return true;
     }
 
+    /**
+     * Returns the coordinator of an LRA, which is joined, completed or compensated by calling its URL. Only an LRA
+     * hosted by the configured coordinator, or by one of the allowed coordinator URLs, is accepted.
+     */
     @Override
     public CompletableFuture<CamelSagaCoordinator> getSaga(String id) {
         CompletableFuture<CamelSagaCoordinator> coordinator;
         try {
-            coordinator = CompletableFuture.completedFuture(new LRASagaCoordinator(this, URI.create(id).toURL()));
+            URI lra = URI.create(id);
+            if (!isHostedByAllowedCoordinator(lra)) {
+                throw new IllegalArgumentException(
+                        "The Long-Running-Action " + id + " is not hosted by the LRA coordinator configured on the LRA"
+                                                   + " saga service. If it is a trusted coordinator, add its URL to"
+                                                   + " allowedCoordinatorUrls.");
+            }
+            coordinator = CompletableFuture.completedFuture(new LRASagaCoordinator(this, lra.toURL()));
         } catch (Exception ex) {
             coordinator = CompletableFuture.failedFuture(ex);
         }
         return coordinator;
     }
 
+    private boolean isHostedByAllowedCoordinator(URI lra) {
+        for (URI coordinator : allowedCoordinators()) {
+            if (isHostedBy(lra, coordinator)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * The URLs of the coordinators whose LRAs are accepted: the configured coordinator, and the allowed coordinator
+     * URLs.
+     */
+    private List<URI> allowedCoordinators() {
+        List<URI> coordinators = new ArrayList<>();
+        coordinators.add(URI.create(new LRAUrlBuilder().host(coordinatorUrl).path(coordinatorContextPath).build()));
+        coordinators.addAll(additionalCoordinators());
+        return coordinators;
+    }

Review Comment:
   Thanks. This backport keeps the code of #27725 as merged on `main`, where the same point came up and the list stays computed per call: every accepted lookup is followed by an HTTP request to the coordinator (join, close or cancel), so building the list from one or two URLs is noise next to it, and reading the current options avoids a cache to keep in sync with the setters. Changing it only on `camel-4.18.x` would make the branches differ, so if caching turns out to be worth it, it would go to `main` first and be backported from there.
   
   _Claude Code on behalf of oscerd_
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@camel.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [backport camel-4.18.x] CAMEL-25584: camel-lra - only join LRAs hosted by the configured LRA coordinator [camel]

Posted by "gnodet (via GitHub)" <gi...@apache.org>.
gnodet merged PR #27772:
URL: https://github.com/apache/camel/pull/27772


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@camel.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [backport camel-4.18.x] CAMEL-25584: camel-lra - only join LRAs hosted by the configured LRA coordinator [camel]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #27772:
URL: https://github.com/apache/camel/pull/27772#discussion_r4257189097


##########
components/camel-lra/src/main/java/org/apache/camel/service/lra/LRASagaService.java:
##########
@@ -75,17 +84,107 @@ public boolean isLongRunningActionHeaderSupported() {
         return true;
     }
 
+    /**
+     * Returns the coordinator of an LRA, which is joined, completed or compensated by calling its URL. Only an LRA
+     * hosted by the configured coordinator, or by one of the allowed coordinator URLs, is accepted.
+     */
     @Override
     public CompletableFuture<CamelSagaCoordinator> getSaga(String id) {
         CompletableFuture<CamelSagaCoordinator> coordinator;
         try {
-            coordinator = CompletableFuture.completedFuture(new LRASagaCoordinator(this, URI.create(id).toURL()));
+            URI lra = URI.create(id);
+            if (!isHostedByAllowedCoordinator(lra)) {
+                throw new IllegalArgumentException(
+                        "The Long-Running-Action " + id + " is not hosted by the LRA coordinator configured on the LRA"
+                                                   + " saga service. If it is a trusted coordinator, add its URL to"
+                                                   + " allowedCoordinatorUrls.");
+            }
+            coordinator = CompletableFuture.completedFuture(new LRASagaCoordinator(this, lra.toURL()));
         } catch (Exception ex) {
             coordinator = CompletableFuture.failedFuture(ex);
         }
         return coordinator;
     }
 
+    private boolean isHostedByAllowedCoordinator(URI lra) {
+        for (URI coordinator : allowedCoordinators()) {
+            if (isHostedBy(lra, coordinator)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * The URLs of the coordinators whose LRAs are accepted: the configured coordinator, and the allowed coordinator
+     * URLs.
+     */
+    private List<URI> allowedCoordinators() {
+        List<URI> coordinators = new ArrayList<>();
+        coordinators.add(URI.create(new LRAUrlBuilder().host(coordinatorUrl).path(coordinatorContextPath).build()));
+        coordinators.addAll(additionalCoordinators());
+        return coordinators;
+    }

Review Comment:
   💡 **Nit:** `allowedCoordinators()` rebuilds a new `ArrayList`, re-instantiates `LRAUrlBuilder`, re-invokes `URI.create()`, and re-parses `allowedCoordinatorUrls` on every call. Since `coordinatorUrl`, `coordinatorContextPath`, and `allowedCoordinatorUrls` are effectively immutable after `doStart()`, consider caching the list once — e.g. populating it in `doStart()` or lazily with a `volatile` field — to avoid repeated allocation and string splitting on every LRA lookup.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@camel.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org