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 f1a7de3974bd CAMEL-25616: camel-core - route reload no longer 
duplicates a templated route without routeId, nor removes all routes when the 
route template is defined in Java (#27774)
f1a7de3974bd is described below

commit f1a7de3974bdeca97627e14f5896e37435100413
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Oct 14 09:57:42 2026 +0200

    CAMEL-25616: camel-core - route reload no longer duplicates a templated 
route without routeId, nor removes all routes when the route template is 
defined in Java (#27774)
    
    Signed-off-by: Claus Ibsen <[email protected]>
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../org/apache/camel/builder/RouteBuilder.java     | 10 ++++-
 .../camel/support/RouteWatcherReloadStrategy.java  |  7 +++-
 .../yaml/RouteReloadTemplatedRouteSourceTest.java  | 36 ++++++++++++++++++
 .../dsl/yaml/RouteReloadTemplatedRouteTest.java    | 43 ++++++++++++++++++++--
 4 files changed, 90 insertions(+), 6 deletions(-)

diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
index 35d2ce7e1db0..1c9f214c5376 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
@@ -828,7 +828,12 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
     @Override
     public Set<String> updateTemplatedRoutesToCamelContext(CamelContext 
context) throws Exception {
         Set<String> answer = new LinkedHashSet<>();
-        if (getTemplatedRouteCollection().getTemplatedRoutes().isEmpty()) {
+        // only a templated route with a route id replaces its existing route: 
one without an id would be created
+        // again next to the existing route on every update, so it is left as 
it is (CAMEL-25616)
+        List<TemplatedRouteDefinition> templatedRoutes = 
getTemplatedRouteCollection().getTemplatedRoutes().stream()
+                .filter(t -> t.getRouteId() != null)
+                .toList();
+        if (templatedRoutes.isEmpty()) {
             return answer;
         }
 
@@ -838,7 +843,8 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         Set<RouteDefinition> before = Collections.newSetFromMap(new 
IdentityHashMap<>());
         before.addAll(model.getRouteDefinitions());
 
-        populateTemplatedRoutes(context);
+        getTemplatedRouteCollection().setCamelContext(context);
+        model.addRouteFromTemplatedRoutes(templatedRoutes);
 
         for (RouteDefinition route : model.getRouteDefinitions()) {
             if (!before.contains(route) && route.getRouteId() != null) {
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
index 7aad6a8ad530..22be1ad2f8fc 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/RouteWatcherReloadStrategy.java
@@ -378,10 +378,15 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
                 // that exist, so its routes are removed like those of a 
changed file, CAMEL-25427)
                 // (a route from a templated route needs the file with the 
route template, and the file with the
                 // templated route, CAMEL-25598; a route from a Kamelet is 
created again by its parent route)
+                // (the file with the templated route is only loaded again 
when its route template comes from a file:
+                // a route template added from Java is removed below and 
nothing adds it again, so loading the
+                // templated route would fail the whole reload, CAMEL-25616)
                 getCamelContext().getRoutes().forEach(r -> {
                     if (!r.isCreatedByKamelet()) {
                         addRouteSource(resources, sources, 
r.getSourceResource());
-                        addRouteSource(resources, sources, 
r.getTemplatedRouteResource());
+                        if (r.getSourceResource() != null) {
+                            addRouteSource(resources, sources, 
r.getTemplatedRouteResource());
+                        }
                     }
                 });
                 // first stop and remove all routes
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteSourceTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteSourceTest.java
index d6b3d0fe6af4..114d2cf48074 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteSourceTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteSourceTest.java
@@ -25,6 +25,7 @@ import java.nio.file.attribute.BasicFileAttributes;
 
 import org.apache.camel.Route;
 import org.apache.camel.ServiceStatus;
+import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.dsl.yaml.support.YamlTestSupport;
 import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.support.RouteWatcherReloadStrategy;
@@ -137,6 +138,41 @@ class RouteReloadTemplatedRouteSourceTest extends 
YamlTestSupport {
         }
     }
 
+    @Test
+    void reloadOfAnotherFileWithTheRouteTemplateFromJava() throws Exception {
+        // the route template comes from Java (no file) and the templated 
route from a file: a reload of another file
+        // must not fail on the route template that the reload removed 
(CAMEL-25616)
+        context.getRouteController().removeAllRoutes();
+        context.removeRouteTemplates("*");
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() {
+                routeTemplate("greeting").templateParameter("greeting")
+                        
.from("direct:greet").setBody(constant("{{greeting}}"));
+            }
+        });
+        loadRoutes(ResourceHelper.resolveResource(context, "file:" + uses),
+                ResourceHelper.resolveResource(context, "file:" + plain));
+        assertThat(context.getRoute("greet").getSourceResource()).isNull();
+
+        TestableStrategy strategy = new TestableStrategy(dir.toString());
+        strategy.setCamelContext(context);
+        strategy.setPattern("*.yaml");
+        strategy.doStart();
+        try {
+            Files.writeString(plain, plainRoute("plain changed"));
+            strategy.getResourceReload().onReload(plain.toString(),
+                    ResourceHelper.resolveResource(context, "file:" + plain));
+
+            assertThat(strategy.getFailedCounter()).isZero();
+            assertThat(strategy.getLastError()).isNull();
+            
assertThat(context.createFluentProducerTemplate().to("direct:plain").request(String.class))
+                    .isEqualTo("plain changed");
+        } finally {
+            strategy.doStop();
+        }
+    }
+
     private static String plainRoute(String body) {
         return """
                 - route:
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteTest.java
index 2913ba7a8d39..1d30755bfa30 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteTest.java
@@ -99,7 +99,45 @@ class RouteReloadTemplatedRouteTest extends YamlTestSupport {
         }
     }
 
+    @Test
+    void reloadKeepingTheOtherRoutesTemplatedRouteWithoutId() throws Exception 
{
+        // a templated route without a route id cannot replace its existing 
route, so it is left as it is (CAMEL-25616)
+        Files.writeString(file, routesWithoutTemplatedRouteId("Hello"));
+        context.getRouteController().removeAllRoutes();
+        context.removeRouteTemplates("*");
+        loadRoutes(ResourceHelper.resolveResource(context, "file:" + file));
+        assertThat(context.getRoutes()).hasSize(2);
+
+        TestableStrategy strategy = new TestableStrategy(dir.toString());
+        strategy.setCamelContext(context);
+        strategy.setPattern("*.yaml");
+        strategy.setRemoveAllRoutes(false);
+        strategy.doStart();
+        try {
+            for (int i = 0; i < 3; i++) {
+                Files.writeString(file, routesWithoutTemplatedRouteId("Hi" + 
i));
+                strategy.getResourceReload().onReload(file.toString(),
+                        ResourceHelper.resolveResource(context, "file:" + 
file));
+
+                assertThat(strategy.getFailedCounter()).isZero();
+                assertThat(context.getRoutes()).hasSize(2);
+                
assertThat(context.createFluentProducerTemplate().to("direct:plain").request(String.class))
+                        .isEqualTo("plain Hi" + i);
+            }
+        } finally {
+            strategy.doStop();
+        }
+    }
+
     private static String routes(String greeting) {
+        return routes(greeting, true);
+    }
+
+    private static String routesWithoutTemplatedRouteId(String greeting) {
+        return routes(greeting, false);
+    }
+
+    private static String routes(String greeting, boolean templatedRouteId) {
         return """
                 - routeTemplate:
                     id: "greeting"
@@ -112,8 +150,7 @@ class RouteReloadTemplatedRouteTest extends YamlTestSupport 
{
                             constant: "{{greeting}}"
                 - templatedRoute:
                     routeTemplateRef: "greeting"
-                    routeId: "greet"
-                    parameters:
+                %s    parameters:
                       - name: "greeting"
                         value: "%s"
                 - route:
@@ -123,7 +160,7 @@ class RouteReloadTemplatedRouteTest extends YamlTestSupport 
{
                       steps:
                         - setBody:
                             constant: "plain %s"
-                """.formatted(greeting, greeting);
+                """.formatted(templatedRouteId ? "    routeId: \"greet\"\n" : 
"", greeting, greeting);
     }
 
     private static void deleteRecursively(Path path) throws IOException {

Reply via email to