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 {