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

davsclaus pushed a commit to branch CAMEL-25619-reload-route-templates
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 65e5ab73390307f4dd18bf1737dc73e4bfeb18b5
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Nov 12 22:19:05 2026 +0100

    CAMEL-25619: camel-core - route reload keeps route templates defined in 
Java, and loads a route template file used through kamelet: again
    
    Dev mode route reload (RouteWatcherReloadStrategy) removed all route
    templates with removeRouteTemplates("*") and loaded the route files again:
    
    - A route template added from Java (a RouteBuilder added to the context,
      no resource) was removed and nothing added it back, so since CAMEL-25616
      the file with its templatedRoute was not loaded again either, and the
      templated route was missing after a reload of another file.
    - A route template in a watched file that is only used through kamelet:
      was not loaded again (a route from a Kamelet has no source of its own),
      so the route calling kamelet:<name> failed on a reload of another file,
      and the restore of the previous routes failed the same way.
    
    Now:
    
    - A new CamelContext.removeRouteTemplatesFromResources(pattern) removes
      only the route templates that were loaded from a resource. The reload and
      the restore of a failed reload use it, so a route template from Java is
      kept, and the file with the templated route is loaded again.
    - The file with the route template of a route created by a Kamelet is
      loaded again when it is a file (a Kamelet from the classpath is loaded
      again by the kamelet component when it is used).
    - The files with route templates are loaded before the other files, in the
      reload and in the restore, so the route template is there when a route
      that uses it through kamelet: is created, also when it is the changed
      file.
    
    Upgrade guide: the new CamelContext method.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../main/java/org/apache/camel/CamelContext.java   |  12 ++
 .../camel/impl/engine/SimpleCamelContext.java      |   5 +
 .../org/apache/camel/impl/DefaultCamelContext.java |  14 ++
 .../camel/support/RouteWatcherReloadStrategy.java  |  87 +++++++++--
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |   8 +
 ...ava => RouteReloadKameletTemplateFileTest.java} | 166 ++++++++++-----------
 .../yaml/RouteReloadTemplatedRouteSourceTest.java  |  74 +++++++--
 7 files changed, 253 insertions(+), 113 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/CamelContext.java 
b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java
index c6ca6d78cd11..3a929e06399c 100644
--- a/core/camel-api/src/main/java/org/apache/camel/CamelContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java
@@ -849,6 +849,18 @@ public interface CamelContext extends 
CamelContextLifecycle, RuntimeConfiguratio
      */
     void removeRouteTemplates(String pattern) throws Exception;
 
+    /**
+     * Removes the route templates matching the pattern that were loaded from 
a resource, such as a route file or a
+     * Kamelet, and keeps those without a resource, such as the route 
templates of a Java {@code RouteBuilder} added to
+     * the context. A route reload loads the resources again, so it removes 
their route templates with this: nothing
+     * would add a route template without a resource back.
+     *
+     * @param  pattern   pattern, such as * for all, or foo* to remove all foo 
templates
+     * @throws Exception is thrown if error during removing route templates
+     * @since            4.24
+     */
+    void removeRouteTemplatesFromResources(String pattern) throws Exception;
+
     /**
      * Adds the given route policy factory
      *
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
index 5eb8397859da..3c291d3ef3da 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/SimpleCamelContext.java
@@ -792,6 +792,11 @@ public class SimpleCamelContext extends 
AbstractCamelContext {
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public void removeRouteTemplatesFromResources(String pattern) throws 
Exception {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public List<NamedRoute> getNamedRouteDefinitions() {
         throw new UnsupportedOperationException();
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
index 6117f47a43f5..388310c8f09b 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
@@ -77,6 +77,7 @@ import org.apache.camel.spi.ValidatorKey;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.DefaultRegistry;
 import org.apache.camel.support.LocalBeanRegistry;
+import org.apache.camel.support.PatternHelper;
 import org.apache.camel.support.ResolverHelper;
 import org.apache.camel.support.SimpleUuidGenerator;
 import org.apache.camel.support.scan.AssignableToPackageScanFilter;
@@ -436,6 +437,19 @@ public class DefaultCamelContext extends 
SimpleCamelContext implements ModelCame
         }
     }
 
+    @Override
+    public void removeRouteTemplatesFromResources(String pattern) throws 
Exception {
+        if (!isLockModel()) {
+            List<RouteTemplateDefinition> remove = new ArrayList<>();
+            for (RouteTemplateDefinition def : new 
ArrayList<>(model.getRouteTemplateDefinitions())) {
+                if (def.getResource() != null && 
PatternHelper.matchPattern(def.getId(), pattern)) {
+                    remove.add(def);
+                }
+            }
+            model.removeRouteTemplateDefinitions(remove);
+        }
+    }
+
     @Override
     public List<NamedRoute> getNamedRouteDefinitions() {
         return new ArrayList<>(model.getRouteDefinitions());
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 460c99da95e9..397b58124d2e 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
@@ -78,6 +78,11 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
     private String pattern;
     private boolean removeAllRoutes = true;
     private final List<Resource> previousSources = new ArrayList<>();
+    /**
+     * The sources with the route templates that the routes of the last reload 
use, also through {@code kamelet:}: they
+     * are loaded before the other sources, so a route template is there when 
a route that uses it is created.
+     */
+    private final List<Resource> templateSources = new ArrayList<>();
     /**
      * The content of the sources as they last loaded, at startup and then at 
each successful reload, by location: what
      * a failed reload goes back to.
@@ -607,15 +612,19 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
                 // (the file of a route may have been removed since it loaded: 
on-demand reload only passes the files
                 // 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 -> 
addRouteSources(resources, sources, r));
+                // templated route, CAMEL-25598; a route from a Kamelet is 
created again by its parent route, and the
+                // file with its route template is loaded again when it is a 
file, CAMEL-25619)
+                templateSources.clear();
+                getCamelContext().getRoutes().forEach(r -> {
+                    addRouteSources(resources, sources, r);
+                    addTemplateSource(templateSources, r);
+                });
                 // first stop and remove all routes
                 getCamelContext().getRouteController().removeAllRoutes();
-                // remove left-over route templates and endpoints, so we can 
start on a fresh
-                getCamelContext().removeRouteTemplates("*");
+                // remove left-over route templates and endpoints, so we can 
start on a fresh: only the route templates
+                // that were loaded from a resource, as the reload loads those 
again; a route template added from Java
+                // has none, and nothing would add it back (CAMEL-25619)
+                getCamelContext().removeRouteTemplatesFromResources("*");
                 getCamelContext().getEndpointRegistry().clear();
             }
 
@@ -650,6 +659,10 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
                 }
             }
 
+            // the sources with route templates first, so a route template is 
there when a route that uses it is
+            // created (a route template used through kamelet:, CAMEL-25619)
+            templateSourcesFirst(sources);
+
             // just in case remember this set of sources as what was attempted 
previously to update
             // in case the update fails with an exception
             previousSources.clear();
@@ -861,10 +874,13 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
                 }
             }
         }
+        // the failed file goes back from memory at the end: it may have the 
route templates the others use
+        templateSourcesFirst(restore);
         try {
-            // a partial load may have left routes or endpoints behind
+            // a partial load may have left routes or endpoints behind (the 
route templates added from Java are kept,
+            // as in the reload, CAMEL-25619)
             getCamelContext().getRouteController().removeAllRoutes();
-            getCamelContext().removeRouteTemplates("*");
+            getCamelContext().removeRouteTemplatesFromResources("*");
             getCamelContext().getEndpointRegistry().clear();
             if (restore.isEmpty()) {
                 LOG.warn("Reload failed and there are no previous routes to 
restore: the application runs without"
@@ -887,16 +903,59 @@ public class RouteWatcherReloadStrategy extends 
FileWatcherResourceReloadStrateg
 
     /**
      * Adds the sources of a route to the sources to load again: the file it 
was loaded from and, for a route from a
-     * templated route, the file of the templated route. A route from a 
Kamelet has none of its own: its parent route
-     * creates it again.
+     * templated route, the file of the templated route. The route template of 
a templated route is either in a file,
+     * which is loaded again, or added without a resource (from Java), which a 
reload keeps (CAMEL-25619).
+     * <p/>
+     * A route from a Kamelet is created again by its parent route. Its source 
is the file with its route template: when
+     * that is a file, it is loaded again too, as a route template that is 
only used through {@code kamelet:} has no
+     * route of its own that would bring it back (CAMEL-25619). A Kamelet from 
the classpath, such as from a Kamelet
+     * catalog, is loaded again by the kamelet component when it is used.
      */
     private static void addRouteSources(Collection<Resource> resources, 
List<Resource> sources, Route route) {
-        if (!route.isCreatedByKamelet()) {
+        if (route.isCreatedByKamelet()) {
+            if (isFile(route.getSourceResource())) {
+                addRouteSource(resources, sources, route.getSourceResource());
+            }
+        } else {
             addRouteSource(resources, sources, route.getSourceResource());
-            if (route.getSourceResource() != null) {
-                addRouteSource(resources, sources, 
route.getTemplatedRouteResource());
+            addRouteSource(resources, sources, 
route.getTemplatedRouteResource());
+        }
+    }
+
+    /**
+     * Adds the source of a route created from a route template (a templated 
route, or a Kamelet) to the sources with
+     * route templates: the file with its route template.
+     */
+    private static void addTemplateSource(List<Resource> templateSources, 
Route route) {
+        Resource rs = route.getSourceResource();
+        if (route.getRouteTemplateId() != null && rs != null && 
!equalResourceLocation(templateSources, rs)) {
+            templateSources.add(rs);
+        }
+    }
+
+    /**
+     * Moves the sources with route templates before the other sources, 
keeping the order of each.
+     */
+    private void templateSourcesFirst(List<Resource> sources) {
+        if (templateSources.isEmpty() || sources.size() < 2) {
+            return;
+        }
+        List<Resource> first = new ArrayList<>();
+        List<Resource> rest = new ArrayList<>();
+        for (Resource rs : sources) {
+            if (equalResourceLocation(templateSources, rs)) {
+                first.add(rs);
+            } else {
+                rest.add(rs);
             }
         }
+        sources.clear();
+        sources.addAll(first);
+        sources.addAll(rest);
+    }
+
+    private static boolean isFile(Resource resource) {
+        return resource != null && resource.getLocation() != null && 
resource.getLocation().startsWith("file:");
     }
 
     /**
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 2b76f4e30d41..d9baa7d2cf37 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -323,6 +323,14 @@ returns the lock held while routes are added to or removed 
from the model. A cus
 implement it (classes that extend `DefaultCamelContext` need no change). The 
Kamelet component now creates the routes of
 Kamelets holding this lock.
 
+=== camel-core - CamelContext has a new removeRouteTemplatesFromResources 
method
+
+The `CamelContext` interface has a new method 
`removeRouteTemplatesFromResources(String pattern)`, which removes the
+route templates loaded from a resource (such as a route file or a Kamelet) and 
keeps those without one (such as the route
+templates of a Java `RouteBuilder` added to the context). A custom 
implementation of `CamelContext` must implement it
+(classes that extend `DefaultCamelContext` need no change). The route reload 
of dev mode uses it, so a route template
+added from Java is kept across a reload, and a route created from it with a 
templated route in a file is created again.
+
 === camel-core - Route.clearRouteModel() is deprecated
 
 The `Route.clearRouteModel()` method is deprecated and is now a no-op. It will 
be removed in a later release.
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/RouteReloadKameletTemplateFileTest.java
similarity index 50%
copy from 
dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadTemplatedRouteSourceTest.java
copy to 
dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/RouteReloadKameletTemplateFileTest.java
index 114d2cf48074..ffaacdd8fdd9 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/RouteReloadKameletTemplateFileTest.java
@@ -23,9 +23,7 @@ import java.nio.file.Path;
 import java.nio.file.SimpleFileVisitor;
 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;
@@ -34,10 +32,10 @@ import org.junit.jupiter.api.Test;
 import static org.assertj.core.api.Assertions.assertThat;
 
 /**
- * CAMEL-25598: a route created from a templated route records the file with 
its route template (as its source) and the
- * file with the templated route, so a reload of another file loads both again.
+ * CAMEL-25619: a route template in a watched file that a route uses only 
through {@code kamelet:} (no templated route)
+ * is loaded again on a reload, before the route that uses it.
  */
-class RouteReloadTemplatedRouteSourceTest extends YamlTestSupport {
+class RouteReloadKameletTemplateFileTest extends YamlTestSupport {
 
     /** Subclass to expose protected lifecycle methods for testing without a 
live file watcher. */
     static class TestableStrategy extends RouteWatcherReloadStrategy {
@@ -60,117 +58,113 @@ class RouteReloadTemplatedRouteSourceTest extends 
YamlTestSupport {
     private Path templates;
     private Path uses;
     private Path plain;
+    private TestableStrategy strategy;
 
     @Override
     public void doSetup() throws Exception {
-        dir = Files.createTempDirectory("camel-reload-templated-source");
+        dir = Files.createTempDirectory("camel-reload-kamelet-template");
         templates = dir.resolve("templates.camel.yaml");
         uses = dir.resolve("uses.camel.yaml");
         plain = dir.resolve("plain.camel.yaml");
-        Files.writeString(templates, """
-                - routeTemplate:
-                    id: "greeting"
-                    parameters:
-                      - name: "greeting"
+        Files.writeString(templates, template("{{greeting}}"));
+        Files.writeString(uses, """
+                - route:
+                    id: caller
                     from:
-                      uri: "direct:greet"
+                      uri: direct:start
                       steps:
-                        - setBody:
-                            constant: "{{greeting}}"
-                """);
-        Files.writeString(uses, """
-                - templatedRoute:
-                    routeTemplateRef: "greeting"
-                    routeId: "greet"
-                    parameters:
-                      - name: "greeting"
-                        value: "Hello"
+                        - to: "kamelet:greeting?greeting=Hello"
                 """);
         Files.writeString(plain, plainRoute("plain"));
         context.start();
         loadRoutes(ResourceHelper.resolveResource(context, "file:" + 
templates),
                 ResourceHelper.resolveResource(context, "file:" + uses),
                 ResourceHelper.resolveResource(context, "file:" + plain));
+        assertThat(greet()).isEqualTo("Hello");
+
+        strategy = new TestableStrategy(dir.toString());
+        strategy.setCamelContext(context);
+        strategy.setPattern("*.yaml");
+        // the strategy is not started (no file watcher in the test): its 
reload callback is driven by hand
+        strategy.doStart();
     }
 
     @Override
     public void doCleanup() throws Exception {
+        if (strategy != null) {
+            strategy.doStop();
+        }
         deleteRecursively(dir);
     }
 
     @Test
-    void routeRecordsItsSources() {
-        Route greet = context.getRoute("greet");
-        assertThat(greet.getRouteTemplateId()).isEqualTo("greeting");
-        assertThat(greet.getSourceResource().getLocation()).isEqualTo("file:" 
+ templates);
-        assertThat(greet.getSourceLocation()).startsWith("file:" + templates + 
":");
-        
assertThat(greet.getTemplatedRouteResource().getLocation()).isEqualTo("file:" + 
uses);
-
-        Route other = context.getRoute("plain");
-        assertThat(other.getRouteTemplateId()).isNull();
-        assertThat(other.getSourceResource().getLocation()).isEqualTo("file:" 
+ plain);
-        assertThat(other.getTemplatedRouteResource()).isNull();
+    void reloadOfAnotherFileKeepsTheRouteUsingTheKamelet() throws Exception {
+        Files.writeString(plain, plainRoute("plain changed"));
+        reload(plain);
+
+        assertThat(strategy.getFailedCounter()).isZero();
+        assertThat(strategy.getLastError()).isNull();
+        
assertThat(context.getRouteController().getRouteStatus("caller")).isEqualTo(ServiceStatus.Started);
+        assertThat(greet()).isEqualTo("Hello");
+        
assertThat(context.createFluentProducerTemplate().to("direct:plain").request(String.class))
+                .isEqualTo("plain changed");
     }
 
     @Test
-    void reloadOfAnotherFileKeepsTheTemplatedRoute() throws Exception {
-        TestableStrategy strategy = new TestableStrategy(dir.toString());
-        strategy.setCamelContext(context);
-        strategy.setPattern("*.yaml");
-        // the strategy is not started (no file watcher in the test): its 
reload callback is driven by hand
-        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.getRoutes()).hasSize(2);
-            
assertThat(context.getRouteController().getRouteStatus("greet")).isEqualTo(ServiceStatus.Started);
-            
assertThat(context.createFluentProducerTemplate().to("direct:greet").request(String.class))
-                    .isEqualTo("Hello");
-            
assertThat(context.createFluentProducerTemplate().to("direct:plain").request(String.class))
-                    .isEqualTo("plain changed");
-            
assertThat(context.getRoute("greet").getTemplatedRouteResource().getLocation()).isEqualTo("file:"
 + uses);
-        } finally {
-            strategy.doStop();
-        }
+    void reloadOfTheRouteTemplateFileIsUsedByTheRouteUsingTheKamelet() throws 
Exception {
+        // the file with the route template is loaded before the route that 
uses it, also when it is the changed file
+        Files.writeString(templates, template("{{greeting}} World"));
+        reload(templates);
+
+        assertThat(strategy.getFailedCounter()).isZero();
+        assertThat(strategy.getLastError()).isNull();
+        assertThat(greet()).isEqualTo("Hello World");
     }
 
     @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();
+    void failedReloadRestoresTheRouteUsingTheKamelet() throws Exception {
+        Files.writeString(plain, """
+                - route:
+                    id: plain
+                    from:
+                      uri: direct:plain
+                      steps:
+                        - noSuchEip: {}
+                """);
+        Exception failure = null;
         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();
+            reload(plain);
+        } catch (Exception e) {
+            failure = e;
         }
+
+        // the reload fails and the routes that ran before are restored 
(CAMEL-24860), the route using the Kamelet too
+        assertThat(failure).isNotNull();
+        
assertThat(context.getRouteController().getRouteStatus("caller")).isEqualTo(ServiceStatus.Started);
+        assertThat(greet()).isEqualTo("Hello");
+        
assertThat(context.createFluentProducerTemplate().to("direct:plain").request(String.class)).isEqualTo("plain");
+    }
+
+    private void reload(Path file) throws Exception {
+        strategy.getResourceReload().onReload(file.toString(), 
ResourceHelper.resolveResource(context, "file:" + file));
+    }
+
+    private String greet() {
+        return 
context.createFluentProducerTemplate().to("direct:start").request(String.class);
+    }
+
+    private static String template(String body) {
+        return """
+                - routeTemplate:
+                    id: "greeting"
+                    parameters:
+                      - name: "greeting"
+                    from:
+                      uri: "kamelet:source"
+                      steps:
+                        - setBody:
+                            simple: "%s"
+                """.formatted(body);
     }
 
     private static String plainRoute(String body) {
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 114d2cf48074..7787d0d52caf 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
@@ -141,19 +141,9 @@ 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();
+        // must not fail on the route template (CAMEL-25616), and keeps the 
route template, so the templated route is
+        // created again (CAMEL-25619)
+        useRouteTemplateFromJava();
 
         TestableStrategy strategy = new TestableStrategy(dir.toString());
         strategy.setCamelContext(context);
@@ -168,11 +158,69 @@ class RouteReloadTemplatedRouteSourceTest extends 
YamlTestSupport {
             assertThat(strategy.getLastError()).isNull();
             
assertThat(context.createFluentProducerTemplate().to("direct:plain").request(String.class))
                     .isEqualTo("plain changed");
+            
assertThat(context.getRouteController().getRouteStatus("greet")).isEqualTo(ServiceStatus.Started);
+            
assertThat(context.createFluentProducerTemplate().to("direct:greet").request(String.class))
+                    .isEqualTo("Hello");
+            
assertThat(context.getRoute("greet").getTemplatedRouteResource().getLocation()).isEqualTo("file:"
 + uses);
+        } finally {
+            strategy.doStop();
+        }
+    }
+
+    @Test
+    void failedReloadWithTheRouteTemplateFromJavaRestoresTheTemplatedRoute() 
throws Exception {
+        // the routes that ran before a failed reload are restored 
(CAMEL-24860): the templated route too, as the
+        // route template from Java is kept (CAMEL-25619)
+        useRouteTemplateFromJava();
+
+        TestableStrategy strategy = new TestableStrategy(dir.toString());
+        strategy.setCamelContext(context);
+        strategy.setPattern("*.yaml");
+        strategy.doStart();
+        try {
+            Files.writeString(plain, """
+                    - route:
+                        id: plain
+                        from:
+                          uri: direct:plain
+                          steps:
+                            - noSuchEip: {}
+                    """);
+            Exception failure = null;
+            try {
+                strategy.getResourceReload().onReload(plain.toString(),
+                        ResourceHelper.resolveResource(context, "file:" + 
plain));
+            } catch (Exception e) {
+                failure = e;
+            }
+
+            assertThat(failure).isNotNull();
+            
assertThat(context.getRouteController().getRouteStatus("greet")).isEqualTo(ServiceStatus.Started);
+            
assertThat(context.createFluentProducerTemplate().to("direct:greet").request(String.class))
+                    .isEqualTo("Hello");
+            
assertThat(context.createFluentProducerTemplate().to("direct:plain").request(String.class))
+                    .isEqualTo("plain");
         } finally {
             strategy.doStop();
         }
     }
 
+    private void useRouteTemplateFromJava() throws Exception {
+        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();
+        
assertThat(context.createFluentProducerTemplate().to("direct:greet").request(String.class)).isEqualTo("Hello");
+    }
+
     private static String plainRoute(String body) {
         return """
                 - route:

Reply via email to