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 45b7d77b15c7 CAMEL-25068: camel-core - Route templates: the caller's
bean wins over a template bean of the same name (item 1) (#27466)
45b7d77b15c7 is described below
commit 45b7d77b15c7bb21e20192efda1807497404d6bc
Author: allthingssecurity <[email protected]>
AuthorDate: Wed Oct 7 15:56:48 2026 +0530
CAMEL-25068: camel-core - Route templates: the caller's bean wins over a
template bean of the same name (item 1) (#27466)
When the caller binds a bean with the same name as a templateBean, which
bean the route got depended on how it was bound and looked up. The caller's
bean now consistently wins over a template bean of the same name (CAMEL-25068
item 1).
Closes #27466
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../java/org/apache/camel/impl/DefaultModel.java | 12 +++
.../camel/builder/TemplatedRouteBuilder.java | 9 +-
.../camel/builder/RouteTemplateCallerBeanTest.java | 104 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 6 ++
.../modules/ROOT/pages/route-template.adoc | 6 ++
5 files changed, 134 insertions(+), 3 deletions(-)
diff --git
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
index 0bf095fdb152..0d740ce89a6c 100644
---
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
+++
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
@@ -20,8 +20,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
@@ -64,6 +66,7 @@ import org.apache.camel.spi.NodeIdFactory;
import org.apache.camel.spi.RouteTemplateLoaderListener;
import org.apache.camel.spi.RouteTemplateParameterSource;
import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.LocalBeanRegistry;
import org.apache.camel.support.PatternHelper;
import org.apache.camel.support.RouteTemplateHelper;
import org.apache.camel.util.AntPathMatcher;
@@ -708,7 +711,16 @@ public class DefaultModel implements Model {
private static void addTemplateBeans(RouteTemplateContext
routeTemplateContext, RouteTemplateDefinition target)
throws Exception {
+ // a bean the caller has bound (TemplatedRouteBuilder or templated
route) takes precedence over the template
+ // bean with the same name, as a parameter takes precedence over the
default value of the template
+ Set<String> callerBeans = Collections.emptySet();
+ if (routeTemplateContext.getLocalBeanRepository() instanceof
LocalBeanRegistry local) {
+ callerBeans = new HashSet<>(local.keys());
+ }
for (BeanFactoryDefinition b : target.getTemplateBeans()) {
+ if (callerBeans.contains(b.getName())) {
+ continue;
+ }
// route template beans do not directly support property
placeholders
// but need to use rtc.property API calls
b.setScriptPropertyPlaceholders("false");
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/builder/TemplatedRouteBuilder.java
b/core/camel-core-model/src/main/java/org/apache/camel/builder/TemplatedRouteBuilder.java
index f1f0109c4f00..41b16790589f 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/builder/TemplatedRouteBuilder.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/builder/TemplatedRouteBuilder.java
@@ -100,7 +100,8 @@ public final class TemplatedRouteBuilder {
}
/**
- * Binds the bean to the template local repository (takes precedence over
global beans)
+ * Binds the bean to the template local repository (takes precedence over
global beans, and over a template bean
+ * with the same name)
*
* @param id the id of the bean
* @param bean the bean
@@ -111,7 +112,8 @@ public final class TemplatedRouteBuilder {
}
/**
- * Binds the bean to the template local repository (takes precedence over
global beans)
+ * Binds the bean to the template local repository (takes precedence over
global beans, and over a template bean
+ * with the same name)
*
* @param id the id of the bean
* @param type the type of the bean to associate the binding
@@ -123,7 +125,8 @@ public final class TemplatedRouteBuilder {
}
/**
- * Binds the bean (via a supplier) to the template local repository (takes
precedence over global beans)
+ * Binds the bean (via a supplier) to the template local repository (takes
precedence over global beans, and over a
+ * template bean with the same name)
*
* @param id the id of the bean
* @param type the type of the bean to associate the binding
diff --git
a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateCallerBeanTest.java
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateCallerBeanTest.java
new file mode 100644
index 000000000000..336e07095832
--- /dev/null
+++
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateCallerBeanTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.builder;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Processor;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.TemplatedRouteDefinition;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A bean that the caller binds with the same name as a template bean takes
precedence over the template bean.
+ */
+class RouteTemplateCallerBeanTest extends ContextTestSupport {
+
+ private final AtomicInteger templateGreetings = new AtomicInteger();
+
+ @Override
+ public boolean isUseRouteBuilder() {
+ return false;
+ }
+
+ @BeforeEach
+ void addTemplate() throws Exception {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ routeTemplate("myTemplate")
+ .templateParameter("foo")
+ .templateBean("greeting", Processor.class, rtc -> {
+ templateGreetings.incrementAndGet();
+ return (Processor) e ->
e.getMessage().setBody("template");
+ })
+ .templateBean("suffix", Processor.class,
+ rtc -> (Processor) e ->
e.getMessage().setBody(e.getMessage().getBody() + "!"))
+ .from("direct:{{foo}}")
+ .process("greeting")
+ .process("suffix");
+ }
+ });
+ context.start();
+ }
+
+ @Test
+ void builderBeanTakesPrecedence() {
+ TemplatedRouteBuilder.builder(context, "myTemplate")
+ .parameter("foo", "one")
+ .bean("greeting", mine())
+ .add();
+ TemplatedRouteBuilder.builder(context, "myTemplate")
+ .parameter("foo", "two")
+ .bean("greeting", Processor.class, mine())
+ .add();
+
+ assertThat(template.requestBody("direct:one",
"World")).isEqualTo("caller!");
+ assertThat(template.requestBody("direct:two",
"World")).isEqualTo("caller!");
+ // the template bean with the same name is not created
+ assertThat(templateGreetings.get()).isZero();
+ }
+
+ @Test
+ void templatedRouteBeanTakesPrecedence() throws Exception {
+ TemplatedRouteDefinition def = new TemplatedRouteDefinition();
+ def.setRouteTemplateRef("myTemplate");
+ def.parameter("foo", "one").bean("greeting", mine());
+ ((ModelCamelContext) context).addRouteFromTemplatedRoute(def);
+
+ assertThat(template.requestBody("direct:one",
"World")).isEqualTo("caller!");
+ assertThat(templateGreetings.get()).isZero();
+ }
+
+ @Test
+ void configurerBeanTakesPrecedence() {
+ TemplatedRouteBuilder.builder(context, "myTemplate")
+ .parameter("foo", "one")
+ .configure(rtc -> rtc.bind("greeting", Processor.class,
mine()))
+ .add();
+
+ assertThat(template.requestBody("direct:one",
"World")).isEqualTo("caller!");
+ }
+
+ private static Processor mine() {
+ return e -> e.getMessage().setBody("caller");
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 44bcffc25a7a..2efde4c330fa 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -500,6 +500,12 @@ The
`org.apache.camel.processor.InterceptSendToEndpointCallback` class is deprec
id. Prior to Camel 4.23 they were accepted, and both processors had the same
id at runtime.
- A hardcoded node id in a route template no longer clashes with the same id
in a regular route, as the ids of
the route created from the template are prefixed.
+- A bean bound by `TemplatedRouteBuilder.bean` or in a `templatedRoute` is now
used instead of a `templateBean` with
+ the same name, which is no longer created. Prior to Camel 4.23 the template
bean replaced it when both had the same
+ type, and was used when the route looked the bean up by the type of the
template bean (such as `process("myBean")`).
+ When the same `RouteTemplateContext` is reused for several
`addRouteFromTemplate` calls, the template beans bound for
+ the first route are now kept as beans of the caller, so the later routes
share these instances instead of each
+ getting new ones. Use a new `RouteTemplateContext` per route to get new
template beans for each route.
=== camel-aws2-ddb
diff --git a/docs/user-manual/modules/ROOT/pages/route-template.adoc
b/docs/user-manual/modules/ROOT/pages/route-template.adoc
index a89dcad18011..cb9e8cc28fcb 100644
--- a/docs/user-manual/modules/ROOT/pages/route-template.adoc
+++ b/docs/user-manual/modules/ROOT/pages/route-template.adoc
@@ -652,6 +652,12 @@ This is an advanced feature.
See xref:route-template-bean-binding.adoc[Route Template Bean Binding] for
details.
+A bean that is bound when creating the route (`TemplatedRouteBuilder.bean` or
the beans of a `templatedRoute`)
+takes precedence over a `templateBean` with the same name, which is then not
created. This allows replacing a
+template bean, for example with a mock in a test. The template beans are bound
to the `RouteTemplateContext` too, so
+when the same `RouteTemplateContext` is reused for several
`addRouteFromTemplate` calls, the routes after the first
+share the template beans created for the first route; use a new
`RouteTemplateContext` per route to get new ones.
+
== Configuring route templates when creating route (advanced)
There may be some special situations where you want to be able to do some
custom configuration/code when