This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 888c217cae62 CAMEL-26754: camel-cluster - ClusteredRoutePolicy: start 
a route added after the CamelContext started once the initial delay elapsed 
(#29529)
888c217cae62 is described below

commit 888c217cae627a31e19563bd9d4f94e02174137d
Author: allthingssecurity <[email protected]>
AuthorDate: Tue Nov 10 13:28:21 2026 +0530

    CAMEL-26754: camel-cluster - ClusteredRoutePolicy: start a route added 
after the CamelContext started once the initial delay elapsed (#29529)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../camel/impl/cluster/ClusteredRoutePolicy.java   |  8 +++
 .../camel/cluster/ClusteredEdgeCasesTest.java      | 68 ++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git 
a/core/camel-cluster/src/main/java/org/apache/camel/impl/cluster/ClusteredRoutePolicy.java
 
b/core/camel-cluster/src/main/java/org/apache/camel/impl/cluster/ClusteredRoutePolicy.java
index 0e7115d31d60..db96dd245bfd 100644
--- 
a/core/camel-cluster/src/main/java/org/apache/camel/impl/cluster/ClusteredRoutePolicy.java
+++ 
b/core/camel-cluster/src/main/java/org/apache/camel/impl/cluster/ClusteredRoutePolicy.java
@@ -198,6 +198,14 @@ public final class ClusteredRoutePolicy extends 
RoutePolicySupport implements Ca
             this.stoppedRoutes.add(route);
         }
 
+        if (camelContext.isStarted() && initialDelay != null && 
initialDelay.toMillis() > 0) {
+            // A policy attached to a CamelContext that has already started 
gets neither the started event nor the
+            // fully started callback, so its initial delay starts now (it 
applies only once). This is done here and
+            // not when the CamelContext is set, as the initial delay may be 
set after that. Only the timer is
+            // scheduled here: the policy lock is not taken, as the caller may 
hold the CamelContext route lock.
+            listener.onCamelContextStarted();
+        }
+
         startManagedRoutes();
     }
 
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/cluster/ClusteredEdgeCasesTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/cluster/ClusteredEdgeCasesTest.java
index a2e446327f06..20907a4bc322 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/cluster/ClusteredEdgeCasesTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/cluster/ClusteredEdgeCasesTest.java
@@ -68,6 +68,74 @@ public class ClusteredEdgeCasesTest {
         }
     }
 
+    @Test
+    public void testInitialDelayForRouteAddedAfterStart() throws Exception {
+        TestClusterService cs = new TestClusterService("cs");
+        CamelContext context = new DefaultCamelContext();
+        context.addService(cs);
+        ClusteredRouteController controller = new ClusteredRouteController();
+        controller.setNamespace("my-ns");
+        controller.setInitialDelay(Duration.ofMillis(300));
+        context.setRouteController(controller);
+        context.start();
+        try {
+            long added = System.nanoTime();
+            // the controller creates the policy of the route, and sets the 
initial delay after the CamelContext
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    from("seda:foo").routeId("foo").to("mock:foo");
+                }
+            });
+            assertEquals(ServiceStatus.Stopped, 
context.getRouteController().getRouteStatus("foo"));
+
+            cs.getView().setLeader(true);
+
+            await().atMost(10, TimeUnit.SECONDS).untilAsserted(
+                    () -> assertEquals(ServiceStatus.Started, 
context.getRouteController().getRouteStatus("foo")));
+            // and the initial delay still applies to the route added after 
start
+            long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - 
added);
+            assertTrue(elapsed >= 300, "The route started after " + elapsed + 
" ms, before the initial delay of 300 ms");
+        } finally {
+            context.stop();
+        }
+    }
+
+    @Test
+    public void testInitialDelayForRouteAddedAfterStartOnLeader() throws 
Exception {
+        TestClusterService cs = new TestClusterService("cs");
+        CamelContext context = new DefaultCamelContext();
+        context.addService(cs);
+        context.start();
+        // creates and starts the view of the namespace, so this node is 
already the leader when the route is added
+        CamelClusterView view = cs.getView("my-ns");
+        try {
+            cs.getView().setLeader(true);
+
+            ClusteredRoutePolicy policy = 
ClusteredRoutePolicy.forNamespace("my-ns");
+            policy.setInitialDelay(Duration.ofMillis(200));
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    
from("seda:foo").routeId("foo").routePolicy(policy).to("mock:foo");
+                }
+            });
+            // the route controller starts the route as usual
+            assertEquals(ServiceStatus.Started, 
context.getRouteController().getRouteStatus("foo"));
+
+            // the node loses the leadership, and takes it again later
+            cs.getView().setLeader(false);
+            await().atMost(10, TimeUnit.SECONDS).untilAsserted(
+                    () -> assertEquals(ServiceStatus.Stopped, 
context.getRouteController().getRouteStatus("foo")));
+            cs.getView().setLeader(true);
+            await().atMost(10, TimeUnit.SECONDS).untilAsserted(
+                    () -> assertEquals(ServiceStatus.Started, 
context.getRouteController().getRouteStatus("foo")));
+        } finally {
+            cs.releaseView(view);
+            context.stop();
+        }
+    }
+
     @Test
     public void testRemovedRouteIdReusedByOtherRoute() throws Exception {
         TestClusterService cs = new TestClusterService("cs");

Reply via email to