You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/12/07 12:58:02 UTC

[camel] branch main updated: Node prefix ID (#8855)

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 3bcc047dcb5 Node prefix ID (#8855)
3bcc047dcb5 is described below

commit 3bcc047dcb54b064df5de3beacc4af57971a47f6
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Dec 7 13:57:54 2022 +0100

    Node prefix ID (#8855)
    
    CAMEL-18798/CAMEL-18771: Allow to configure nodePrefixId on route/routeTemplate to prefix all node IDs to make it easier to avoid clash with hardcoded IDs.
---
 .../camel/catalog/models/templatedRoute.json       |   1 +
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  12 ++
 .../SpringRouteNodePrefixIdDuplicateTest.java      |  30 +++
 .../processor/SpringRouteNodePrefixIdTest.java     |  30 +++
 .../SpringTemplatedRoutePrefixIdTest.java          |  54 ++++++
 .../SpringRouteNodePrefixIdDuplicateTest.xml       |  40 ++++
 .../processor/SpringRouteNodePrefixIdTest.xml      |  51 +++++
 .../SpringTemplatedRoutePrefixIdTest.xml           |  53 ++++++
 .../main/java/org/apache/camel/CamelContext.java   |  39 ++++
 .../org/apache/camel/RouteTemplateContext.java     |   8 +-
 .../camel/impl/engine/SimpleCamelContext.java      |  13 ++
 .../org/apache/camel/impl/DefaultCamelContext.java |  24 ++-
 .../java/org/apache/camel/impl/DefaultModel.java   |  28 ++-
 .../camel/impl/lw/LightweightCamelContext.java     |  13 ++
 .../impl/lw/LightweightRuntimeCamelContext.java    |  13 ++
 .../org/apache/camel/model/templatedRoute.json     |   1 +
 .../camel/builder/TemplatedRouteBuilder.java       |  13 +-
 .../main/java/org/apache/camel/model/Model.java    |  30 +++
 .../camel/model/OptionalIdentifiedDefinition.java  |  22 ++-
 .../apache/camel/model/ProcessorDefinition.java    |  17 ++
 .../org/apache/camel/model/RouteDefinition.java    |  29 +++
 .../apache/camel/model/RouteDefinitionHelper.java  |  15 ++
 .../camel/model/TemplatedRouteDefinition.java      |  20 ++
 .../camel/builder/RouteTemplatePrefixIdTest.java   | 205 +++++++++++++++++++++
 .../processor/RouteNodePrefixIdDuplicateTest.java  |  58 ++++++
 .../camel/processor/RouteNodePrefixIdTest.java     |  70 +++++++
 .../camel/main/MainTemplatedRoutePrefixIdTest.java |  77 ++++++++
 .../apache/camel/support/LocalBeanRegistry.java    |   6 +-
 .../java/org/apache/camel/xml/in/ModelParser.java  |   2 +
 .../modules/ROOT/pages/route-template.adoc         |  83 +++++++++
 .../deserializers/RouteDefinitionDeserializer.java |   5 +
 .../TemplatedRouteDefinitionDeserializer.java      |   6 +
 .../generated/resources/schema/camel-yaml-dsl.json |   6 +
 .../generated/resources/schema/camelYamlDsl.json   |   6 +
 .../apache/camel/dsl/yaml/RouteTemplateTest.groovy |  40 ++++
 .../org/apache/camel/dsl/yaml/RoutesTest.groovy    |  33 ++++
 36 files changed, 1139 insertions(+), 14 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/templatedRoute.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/templatedRoute.json
index cfa496c210f..70d516f20e0 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/templatedRoute.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/templatedRoute.json
@@ -14,6 +14,7 @@
   "properties": {
     "routeTemplateRef": { "kind": "attribute", "displayName": "Route Template Ref", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the id of the route template to use to build the route." },
     "routeId": { "kind": "attribute", "displayName": "Route Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the id of the route built from the route template." },
+    "prefixId": { "kind": "attribute", "displayName": "Prefix Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a prefix to use for all node ids (not route id)." },
     "parameter": { "kind": "element", "displayName": "Parameter", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.TemplatedRouteParameterDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Adds an input parameter of the template to build the route" },
     "bean": { "kind": "element", "displayName": "Bean", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.TemplatedRouteBeanDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Adds a local bean as input of the template to build the route" }
   }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index de64274293e..835018e66b9 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -16185,6 +16185,8 @@ Whether message history is enabled on this route. Default value: true
           </xs:annotation>
         </xs:attribute>
                 
+        <xs:attribute name="nodePrefixId" type="xs:string"/>
+                
         <xs:attribute name="precondition" type="xs:string"/>
                 
         <xs:attribute name="rest" type="xs:boolean"/>
@@ -17777,6 +17779,16 @@ Sets the id of the route built from the route template.
         </xs:documentation>
       </xs:annotation>
     </xs:attribute>
+        
+    <xs:attribute name="prefixId" type="xs:string">
+      <xs:annotation>
+        <xs:documentation xml:lang="en">
+          <![CDATA[
+Sets a prefix to use for all node ids (not route id).
+        ]]>
+        </xs:documentation>
+      </xs:annotation>
+    </xs:attribute>
       
   </xs:complexType>
     
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringRouteNodePrefixIdDuplicateTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringRouteNodePrefixIdDuplicateTest.java
new file mode 100644
index 00000000000..9194452c2c9
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringRouteNodePrefixIdDuplicateTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.RouteNodePrefixIdDuplicateTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringRouteNodePrefixIdDuplicateTest extends RouteNodePrefixIdDuplicateTest {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/SpringRouteNodePrefixIdDuplicateTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringRouteNodePrefixIdTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringRouteNodePrefixIdTest.java
new file mode 100644
index 00000000000..75ee283cfe9
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringRouteNodePrefixIdTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.RouteNodePrefixIdTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringRouteNodePrefixIdTest extends RouteNodePrefixIdTest {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/SpringRouteNodePrefixIdTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/SpringTemplatedRoutePrefixIdTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/SpringTemplatedRoutePrefixIdTest.java
new file mode 100644
index 00000000000..1b0da5abef3
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/routebuilder/SpringTemplatedRoutePrefixIdTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.spring.routebuilder;
+
+import org.apache.camel.Route;
+import org.apache.camel.spring.SpringTestSupport;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SpringTemplatedRoutePrefixIdTest extends SpringTestSupport {
+
+    @Override
+    protected AbstractXmlApplicationContext createApplicationContext() {
+        return new ClassPathXmlApplicationContext("org/apache/camel/spring/routebuilder/SpringTemplatedRoutePrefixIdTest.xml");
+    }
+
+    @Test
+    public void testPrefixId() throws Exception {
+        assertEquals(2, context.getRouteDefinitions().size());
+        assertEquals(2, context.getRoutes().size());
+        assertEquals("Started", context.getRouteController().getRouteStatus("first").name());
+        assertEquals("Started", context.getRouteController().getRouteStatus("second").name());
+        assertEquals("true", context.getRoute("first").getProperties().get(Route.TEMPLATE_PROPERTY));
+        assertEquals("true", context.getRoute("second").getProperties().get(Route.TEMPLATE_PROPERTY));
+
+        template.sendBody("direct:one", "Hello Cheese");
+        template.sendBody("direct:two", "Hello Cake");
+
+        assertMockEndpointsSatisfied();
+
+        // all nodes should include prefix
+        Assertions.assertEquals(3, context.getRoute("first").filter("aaa*").size());
+        Assertions.assertEquals(3, context.getRoute("second").filter("bbb*").size());
+    }
+
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringRouteNodePrefixIdDuplicateTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringRouteNodePrefixIdDuplicateTest.xml
new file mode 100644
index 00000000000..14499ff4aee
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringRouteNodePrefixIdDuplicateTest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <route id="foo" nodePrefixId="aaa">
+      <from uri="direct:foo"/>
+      <to uri="mock:foo" id="myMock"/>
+      <to uri="seda:foo"/>
+    </route>
+    <route id="bar" nodePrefixId="bbb">
+      <from uri="direct:bar"/>
+      <to uri="mock:bar" id="myMock"/>
+      <to uri="seda:bar"/>
+    </route>
+  </camelContext>
+
+</beans>
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringRouteNodePrefixIdTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringRouteNodePrefixIdTest.xml
new file mode 100644
index 00000000000..48ad6f5deaf
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/SpringRouteNodePrefixIdTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <route id="foo" nodePrefixId="aaa">
+      <from uri="direct:foo"/>
+      <to uri="mock:foo" id="myFoo"/>
+      <to uri="seda:foo"/>
+    </route>
+    <route id="bar" nodePrefixId="bbb">
+      <from uri="direct:bar"/>
+      <to uri="mock:bar" id="myBar"/>
+      <to uri="seda:bar"/>
+    </route>
+    <route nodePrefixId="ccc">
+      <from uri="direct:cheese"/>
+      <choice>
+        <when><header>cheese</header>
+          <to uri="mock:cheese" id="myCheese"/>
+        </when>
+        <otherwise>
+          <to uri="mock:gauda"/>
+        </otherwise>
+      </choice>
+    </route>
+  </camelContext>
+
+</beans>
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/routebuilder/SpringTemplatedRoutePrefixIdTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/routebuilder/SpringTemplatedRoutePrefixIdTest.xml
new file mode 100644
index 00000000000..76c86bd0c49
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/routebuilder/SpringTemplatedRoutePrefixIdTest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+            http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+    <camelContext id="foo" xmlns="http://camel.apache.org/schema/spring">
+        <routeTemplate id="myTemplate">
+            <templateParameter name="foo"/>
+            <templateParameter name="bar"/>
+            <route>
+                <from uri="direct:{{foo}}"/>
+                <choice>
+                    <when>
+                        <header>foo</header>
+                        <log message="${body}" id="myLog"/>
+                    </when>
+                    <otherwise>
+                        <to uri="mock:{{bar}}" id="end"/>
+                    </otherwise>
+                </choice>
+            </route>
+        </routeTemplate>
+        <templatedRoute routeTemplateRef="myTemplate" routeId="first" prefixId="aaa">
+            <parameter name="foo" value="one"/>
+            <parameter name="bar" value="cheese"/>
+        </templatedRoute>
+        <templatedRoute routeTemplateRef="myTemplate" routeId="second" prefixId="bbb">
+            <parameter name="foo" value="two"/>
+            <parameter name="bar" value="cake"/>
+        </templatedRoute>
+    </camelContext>
+
+</beans>
\ No newline at end of file
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 fda8bccb99a..a09faa5cb4c 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
@@ -632,9 +632,29 @@ public interface CamelContext extends CamelContextLifecycle, RuntimeConfiguratio
      * @param  parameters      parameters to use for the route template when creating the new route
      * @return                 the id of the route added (for example when an id was auto assigned)
      * @throws Exception       is thrown if error creating and adding the new route
+     * @deprecated use {@link #addRouteFromTemplate(String, String, String, Map)}
      */
+    @Deprecated
     String addRouteFromTemplate(String routeId, String routeTemplateId, Map<String, Object> parameters) throws Exception;
 
+    /**
+     * Adds a new route from a given route template.
+     *
+     * Camel end users should favour using {@link org.apache.camel.builder.TemplatedRouteBuilder} which is a fluent
+     * builder with more functionality than this API.
+     *
+     * @param  routeId         the id of the new route to add (optional)
+     * @param  routeTemplateId the id of the route template (mandatory)
+     * @param  prefixId        prefix to use for all node ids (not route id). Use null for no prefix. (optional)
+     * @param  parameters      parameters to use for the route template when creating the new route
+     * @return                 the id of the route added (for example when an id was auto assigned)
+     * @throws Exception       is thrown if error creating and adding the new route
+     */
+    String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId,
+            Map<String, Object> parameters)
+            throws Exception;
+
     /**
      * Adds a new route from a given route template.
      *
@@ -646,10 +666,29 @@ public interface CamelContext extends CamelContextLifecycle, RuntimeConfiguratio
      * @param  routeTemplateContext the route template context (mandatory)
      * @return                      the id of the route added (for example when an id was auto assigned)
      * @throws Exception            is thrown if error creating and adding the new route
+     * @deprecated use {@link #addRouteFromTemplate(String, String, String, RouteTemplateContext)}
      */
+    @Deprecated
     String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
             throws Exception;
 
+    /**
+     * Adds a new route from a given route template.
+     *
+     * Camel end users should favour using {@link org.apache.camel.builder.TemplatedRouteBuilder} which is a fluent
+     * builder with more functionality than this API.
+     *
+     * @param  routeId              the id of the new route to add (optional)
+     * @param  routeTemplateId      the id of the route template (mandatory)
+     * @param  prefixId             prefix to use for all node ids (not route id). Use null for no prefix. (optional)
+     * @param  routeTemplateContext the route template context (mandatory)
+     * @return                      the id of the route added (for example when an id was auto assigned)
+     * @throws Exception            is thrown if error creating and adding the new route
+     */
+    String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId, RouteTemplateContext routeTemplateContext)
+            throws Exception;
+
     /**
      * Removes the route templates matching the pattern
      *
diff --git a/core/camel-api/src/main/java/org/apache/camel/RouteTemplateContext.java b/core/camel-api/src/main/java/org/apache/camel/RouteTemplateContext.java
index fa2c77dc2e7..855353d44b9 100644
--- a/core/camel-api/src/main/java/org/apache/camel/RouteTemplateContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/RouteTemplateContext.java
@@ -39,7 +39,7 @@ public interface RouteTemplateContext extends HasCamelContext {
     /**
      * Binds the bean to the repository (if possible).
      *
-     * If the bean is {@link CamelContextAware} then the registry will automatic inject the context if possible.
+     * If the bean is {@link CamelContextAware} then the registry will automatically inject the context if possible.
      *
      * @param id   the id of the bean
      * @param bean the bean
@@ -53,7 +53,7 @@ public interface RouteTemplateContext extends HasCamelContext {
      * <p/>
      * Binding by id and type allows to bind multiple entries with the same id but with different type.
      *
-     * If the bean is {@link CamelContextAware} then the registry will automatic inject the context if possible.
+     * If the bean is {@link CamelContextAware} then the registry will automatically inject the context if possible.
      *
      * @param id   the id of the bean
      * @param type the type of the bean to associate the binding
@@ -69,7 +69,7 @@ public interface RouteTemplateContext extends HasCamelContext {
      * <p/>
      * Binding by id and type allows to bind multiple entries with the same id but with different type.
      *
-     * If the bean is {@link CamelContextAware} then the registry will automatic inject the context if possible.
+     * If the bean is {@link CamelContextAware} then the registry will automatically inject the context if possible.
      *
      * @param id   the id of the bean
      * @param type the type of the bean to associate the binding
@@ -84,7 +84,7 @@ public interface RouteTemplateContext extends HasCamelContext {
      * <p/>
      * Binding by id and type allows to bind multiple entries with the same id but with different type.
      *
-     * If the bean is {@link CamelContextAware} then the registry will automatic inject the context if possible.
+     * If the bean is {@link CamelContextAware} then the registry will automatically inject the context if possible.
      *
      * @param id   the id of the bean
      * @param type the type of the bean to associate the binding
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 690a87baced..16ddc3a347a 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
@@ -704,12 +704,25 @@ public class SimpleCamelContext extends AbstractCamelContext {
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public String addRouteFromTemplate(String routeId, String routeTemplateId, String prefixId, Map<String, Object> parameters)
+            throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
             throws Exception {
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId, RouteTemplateContext routeTemplateContext)
+            throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void removeRouteTemplates(String pattern) throws Exception {
         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 e9c2b30bc0e..54ab4d6ecdd 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
@@ -474,6 +474,15 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
         return model.addRouteFromTemplate(routeId, routeTemplateId, parameters);
     }
 
+    @Override
+    public String addRouteFromTemplate(String routeId, String routeTemplateId, String prefixId, Map<String, Object> parameters)
+            throws Exception {
+        if (model == null && isLightweight()) {
+            throw new IllegalStateException("Access to model not supported in lightweight mode");
+        }
+        return model.addRouteFromTemplate(routeId, routeTemplateId, prefixId, parameters);
+    }
+
     @Override
     public String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
             throws Exception {
@@ -483,6 +492,16 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
         return model.addRouteFromTemplate(routeId, routeTemplateId, routeTemplateContext);
     }
 
+    @Override
+    public String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId, RouteTemplateContext routeTemplateContext)
+            throws Exception {
+        if (model == null && isLightweight()) {
+            throw new IllegalStateException("Access to model not supported in lightweight mode");
+        }
+        return model.addRouteFromTemplate(routeId, routeTemplateId, prefixId, routeTemplateContext);
+    }
+
     @Override
     public void addRouteFromTemplatedRoute(TemplatedRouteDefinition templatedRouteDefinition)
             throws Exception {
@@ -820,7 +839,8 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
             List<RouteDefinition> routeDefinitionsToRemove = null;
             for (RouteDefinition routeDefinition : routeDefinitions) {
                 // assign ids to the routes and validate that the id's is all unique
-                String duplicate = RouteDefinitionHelper.validateUniqueIds(routeDefinition, routeDefinitions);
+                String duplicate = RouteDefinitionHelper.validateUniqueIds(routeDefinition, routeDefinitions,
+                        routeDefinition.getNodePrefixId());
                 if (duplicate != null) {
                     throw new FailedToStartRouteException(
                             routeDefinition.getId(),
@@ -900,6 +920,8 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
 
                     // need to reset auto assigned ids, so there is no clash when creating routes
                     ProcessorDefinitionHelper.resetAllAutoAssignedNodeIds(routeDefinition);
+                    // must re-init parent when created from a template
+                    RouteDefinitionHelper.initParent(routeDefinition);
                 }
                 // Check if the route is included
                 if (includedRoute(routeDefinition)) {
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 48d8847cbff..2cac4a0903e 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
@@ -364,12 +364,30 @@ public class DefaultModel implements Model {
         if (parameters != null) {
             parameters.forEach(rtc::setParameter);
         }
-        return addRouteFromTemplate(routeId, routeTemplateId, rtc);
+        return addRouteFromTemplate(routeId, routeTemplateId, null, rtc);
     }
 
     @Override
+    public String addRouteFromTemplate(String routeId, String routeTemplateId, String prefixId, Map<String, Object> parameters)
+            throws Exception {
+        RouteTemplateContext rtc = new DefaultRouteTemplateContext(camelContext);
+        if (parameters != null) {
+            parameters.forEach(rtc::setParameter);
+        }
+        return addRouteFromTemplate(routeId, routeTemplateId, prefixId, rtc);
+    }
+
     public String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
             throws Exception {
+        return addRouteFromTemplate(routeId, routeTemplateId, null, routeTemplateContext);
+    }
+
+    @Override
+    public String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId,
+            RouteTemplateContext routeTemplateContext)
+            throws Exception {
+
         RouteTemplateDefinition target = null;
         for (RouteTemplateDefinition def : routeTemplateDefinitions) {
             if (routeTemplateId.equals(def.getId())) {
@@ -459,6 +477,9 @@ public class DefaultModel implements Model {
         if (routeId != null) {
             def.setId(routeId);
         }
+        if (prefixId != null) {
+            def.setNodePrefixId(prefixId);
+        }
         def.setTemplateParameters(prop);
         def.setTemplateDefaultParameters(propDefaultValues);
         def.setRouteTemplateContext(routeTemplateContext);
@@ -473,7 +494,7 @@ public class DefaultModel implements Model {
         }
 
         // assign ids to the routes and validate that the id's are all unique
-        String duplicate = RouteDefinitionHelper.validateUniqueIds(def, routeDefinitions);
+        String duplicate = RouteDefinitionHelper.validateUniqueIds(def, routeDefinitions, prefixId);
         if (duplicate != null) {
             throw new FailedToCreateRouteFromTemplateException(
                     routeId, routeTemplateId,
@@ -740,6 +761,7 @@ public class DefaultModel implements Model {
     public void addRouteFromTemplatedRoute(TemplatedRouteDefinition templatedRouteDefinition)
             throws Exception {
         ObjectHelper.notNull(templatedRouteDefinition, "templatedRouteDefinition");
+
         final RouteTemplateContext routeTemplateContext = new DefaultRouteTemplateContext(camelContext);
         // Load the parameters into the context
         final List<TemplatedRouteParameterDefinition> parameters = templatedRouteDefinition.getParameters();
@@ -757,7 +779,7 @@ public class DefaultModel implements Model {
         }
         // Add the route
         addRouteFromTemplate(templatedRouteDefinition.getRouteId(), templatedRouteDefinition.getRouteTemplateRef(),
-                routeTemplateContext);
+                templatedRouteDefinition.getPrefixId(), routeTemplateContext);
     }
 
     @Override
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
index 0800e42775e..7fc6ace53ac 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java
@@ -1961,12 +1961,25 @@ public class LightweightCamelContext implements ExtendedCamelContext, CatalogCam
         return getModelCamelContext().addRouteFromTemplate(routeId, routeTemplateId, parameters);
     }
 
+    @Override
+    public String addRouteFromTemplate(String routeId, String routeTemplateId, String prefixId, Map<String, Object> parameters)
+            throws Exception {
+        return getModelCamelContext().addRouteFromTemplate(routeId, routeTemplateId, prefixId, parameters);
+    }
+
     @Override
     public String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
             throws Exception {
         return getModelCamelContext().addRouteFromTemplate(routeId, routeTemplateId, routeTemplateContext);
     }
 
+    @Override
+    public String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId, RouteTemplateContext routeTemplateContext)
+            throws Exception {
+        return getModelCamelContext().addRouteFromTemplate(routeId, routeTemplateId, prefixId, routeTemplateContext);
+    }
+
     @Override
     public void addRouteFromTemplatedRoute(TemplatedRouteDefinition templatedRouteDefinition)
             throws Exception {
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
index aec0eda6796..122a6237168 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightRuntimeCamelContext.java
@@ -2109,12 +2109,25 @@ public class LightweightRuntimeCamelContext implements ExtendedCamelContext, Cat
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public String addRouteFromTemplate(String routeId, String routeTemplateId, String prefixId, Map<String, Object> parameters)
+            throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
             throws Exception {
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId, RouteTemplateContext routeTemplateContext)
+            throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void removeRouteTemplates(String pattern) throws Exception {
         throw new UnsupportedOperationException();
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/templatedRoute.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/templatedRoute.json
index cfa496c210f..70d516f20e0 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/templatedRoute.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/templatedRoute.json
@@ -14,6 +14,7 @@
   "properties": {
     "routeTemplateRef": { "kind": "attribute", "displayName": "Route Template Ref", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the id of the route template to use to build the route." },
     "routeId": { "kind": "attribute", "displayName": "Route Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the id of the route built from the route template." },
+    "prefixId": { "kind": "attribute", "displayName": "Prefix Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a prefix to use for all node ids (not route id)." },
     "parameter": { "kind": "element", "displayName": "Parameter", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.TemplatedRouteParameterDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Adds an input parameter of the template to build the route" },
     "bean": { "kind": "element", "displayName": "Bean", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.TemplatedRouteBeanDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Adds a local bean as input of the template to build the route" }
   }
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 9f5bd6a484f..f988b135160 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
@@ -36,6 +36,7 @@ public final class TemplatedRouteBuilder {
     private final String routeTemplateId;
     private final RouteTemplateContext routeTemplateContext;
     private String routeId;
+    private String prefixId;
     private Consumer<RouteTemplateDefinition> handler;
     private Consumer<RouteTemplateContext> configurer;
 
@@ -67,6 +68,16 @@ public final class TemplatedRouteBuilder {
         return this;
     }
 
+    /**
+     * Sets a prefix to use for all node ids (not route id).
+     *
+     * @param prefixId the prefix id
+     */
+    public TemplatedRouteBuilder prefixId(String prefixId) {
+        this.prefixId = prefixId;
+        return this;
+    }
+
     /**
      * Adds a parameter the route template will use when creating the route.
      *
@@ -165,7 +176,7 @@ public final class TemplatedRouteBuilder {
             if (configurer != null) {
                 routeTemplateContext.setConfigurer(configurer);
             }
-            return camelContext.addRouteFromTemplate(routeId, routeTemplateId, routeTemplateContext);
+            return camelContext.addRouteFromTemplate(routeId, routeTemplateId, prefixId, routeTemplateContext);
         } catch (Exception e) {
             throw RuntimeCamelException.wrapRuntimeException(e);
         }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/Model.java b/core/camel-core-model/src/main/java/org/apache/camel/model/Model.java
index ae3528bd3de..a3148f46153 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/Model.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/Model.java
@@ -223,6 +223,21 @@ public interface Model {
      */
     String addRouteFromTemplate(String routeId, String routeTemplateId, Map<String, Object> parameters) throws Exception;
 
+    /**
+     * Adds a new route from a given route template
+     *
+     * @param  routeId         the id of the new route to add (optional)
+     * @param  routeTemplateId the id of the route template (mandatory)
+     * @param  prefixId        prefix to use when assigning route and node IDs (optional)
+     * @param  parameters      parameters to use for the route template when creating the new route
+     * @return                 the id of the route added (for example when an id was auto assigned)
+     * @throws Exception       is thrown if error creating and adding the new route
+     */
+    String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId,
+            Map<String, Object> parameters)
+            throws Exception;
+
     /**
      * Adds a new route from a given route template
      *
@@ -235,6 +250,21 @@ public interface Model {
     String addRouteFromTemplate(String routeId, String routeTemplateId, RouteTemplateContext routeTemplateContext)
             throws Exception;
 
+    /**
+     * Adds a new route from a given route template
+     *
+     * @param  routeId              the id of the new route to add (optional)
+     * @param  routeTemplateId      the id of the route template (mandatory)
+     * @param  prefixId             prefix to use when assigning route and node IDs (optional)
+     * @param  routeTemplateContext the route template context (mandatory)
+     * @return                      the id of the route added (for example when an id was auto assigned)
+     * @throws Exception            is thrown if error creating and adding the new route
+     */
+    String addRouteFromTemplate(
+            String routeId, String routeTemplateId, String prefixId,
+            RouteTemplateContext routeTemplateContext)
+            throws Exception;
+
     /**
      * Adds a new route from a given templated route definition
      *
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/OptionalIdentifiedDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/OptionalIdentifiedDefinition.java
index f1df499f8b0..7cc89556f6d 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/OptionalIdentifiedDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/OptionalIdentifiedDefinition.java
@@ -43,8 +43,8 @@ public abstract class OptionalIdentifiedDefinition<T extends OptionalIdentifiedD
     private String id;
     private Boolean customId;
     private DescriptionDefinition description;
-    private transient int lineNumber = -1;
-    private transient String location;
+    private int lineNumber = -1;
+    private String location;
 
     @Override
     public CamelContext getCamelContext() {
@@ -188,10 +188,26 @@ public abstract class OptionalIdentifiedDefinition<T extends OptionalIdentifiedD
      * Gets the node id, creating one if not already set.
      */
     public String idOrCreate(NodeIdFactory factory) {
+        // prefix is only for nodes in the route (not the route id)
+        String prefix = null;
+        boolean iAmRoute = this instanceof RouteDefinition;
+        boolean allowPrefix = !iAmRoute && this instanceof ProcessorDefinition;
+        if (allowPrefix) {
+            RouteDefinition route = ProcessorDefinitionHelper.getRoute(this);
+            if (route != null) {
+                prefix = route.getNodePrefixId();
+            }
+        }
         if (id == null) {
             setGeneratedId(factory.createId(this));
         }
-        return id;
+
+        // return with prefix
+        if (prefix != null) {
+            return prefix + id;
+        } else {
+            return id;
+        }
     }
 
     public Boolean getCustomId() {
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
index 0f9adf65346..b7592696ca5 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
@@ -865,6 +865,23 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
         return asType();
     }
 
+    /**
+     * Sets a prefix to use for all node ids (not route id).
+     *
+     * @param  prefixId the prefix
+     * @return          the builder
+     */
+    public Type nodePrefixId(String prefixId) {
+        ProcessorDefinition<?> def = this;
+
+        RouteDefinition route = ProcessorDefinitionHelper.getRoute(def);
+        if (route != null) {
+            route.setNodePrefixId(prefixId);
+        }
+
+        return asType();
+    }
+
     /**
      * Disables this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later
      * at runtime.
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
index 9f591c6205a..a16dd9709a6 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
@@ -66,6 +66,7 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition>
     private String routeConfigurationId;
     private transient Set<String> appliedRouteConfigurationIds;
     private String group;
+    private String nodePrefixId;
     private String streamCache;
     private String trace;
     private String messageHistory;
@@ -280,6 +281,18 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition>
         return this;
     }
 
+    /**
+     * Sets a prefix to use for all node ids (not route id).
+     *
+     * @param  prefixId the prefix
+     * @return          the builder
+     */
+    @Override
+    public RouteDefinition nodePrefixId(String prefixId) {
+        setNodePrefixId(prefixId);
+        return this;
+    }
+
     /**
      * Disable stream caching for this route.
      *
@@ -855,6 +868,22 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition>
         this.group = group;
     }
 
+    /**
+     * Prefix to use for all node ids (not route id).
+     */
+    public String getNodePrefixId() {
+        return nodePrefixId;
+    }
+
+    /**
+     * Sets a prefix to use for all node ids (not route id).
+     */
+    @XmlAttribute
+    @Metadata(label = "advanced")
+    public void setNodePrefixId(String nodePrefixId) {
+        this.nodePrefixId = nodePrefixId;
+    }
+
     /**
      * Whether stream caching is enabled on this route.
      */
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
index 0e27dc6af8a..f85ddaf10b4 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
@@ -245,6 +245,18 @@ public final class RouteDefinitionHelper {
      * @return        <tt>null</tt> if no duplicate id's detected, otherwise the first found duplicate id is returned.
      */
     public static String validateUniqueIds(RouteDefinition target, List<RouteDefinition> routes) {
+        return validateUniqueIds(target, routes, null);
+    }
+
+    /**
+     * Validates that the target route has no duplicate id's from any of the existing routes.
+     *
+     * @param  target   the target route
+     * @param  routes   the existing routes
+     * @param  prefixId optional prefix to use in duplicate id detection
+     * @return          <tt>null</tt> if no duplicate id's detected, otherwise the first found duplicate id is returned.
+     */
+    public static String validateUniqueIds(RouteDefinition target, List<RouteDefinition> routes, String prefixId) {
         Set<String> routesIds = new LinkedHashSet<>();
         // gather all ids for the existing route, but only include custom ids,
         // and no abstract ids
@@ -267,6 +279,9 @@ public final class RouteDefinitionHelper {
 
         // now check for clash with the target route
         for (String id : targetIds) {
+            if (prefixId != null) {
+                id = prefixId + id;
+            }
             if (routesIds.contains(id)) {
                 return id;
             }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/TemplatedRouteDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/TemplatedRouteDefinition.java
index 98da50a8c9b..fda2fa2572a 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/TemplatedRouteDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/TemplatedRouteDefinition.java
@@ -50,6 +50,8 @@ public class TemplatedRouteDefinition implements CamelContextAware {
     private String routeTemplateRef;
     @XmlAttribute
     private String routeId;
+    @XmlAttribute
+    private String prefixId;
     @XmlElement(name = "parameter")
     @Metadata(description = "Adds an input parameter of the template to build the route")
     private List<TemplatedRouteParameterDefinition> parameters;
@@ -89,6 +91,14 @@ public class TemplatedRouteDefinition implements CamelContextAware {
         this.routeId = routeId;
     }
 
+    public String getPrefixId() {
+        return prefixId;
+    }
+
+    public void setPrefixId(String prefixId) {
+        this.prefixId = prefixId;
+    }
+
     @Override
     public CamelContext getCamelContext() {
         return camelContext;
@@ -260,6 +270,16 @@ public class TemplatedRouteDefinition implements CamelContextAware {
         return def;
     }
 
+    /**
+     * Sets a prefix to use for all node ids (not route id).
+     *
+     * @param id the prefix id
+     */
+    public TemplatedRouteDefinition prefixId(String id) {
+        setPrefixId(id);
+        return this;
+    }
+
     /**
      * Sets the id of the route built from the route template.
      *
diff --git a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePrefixIdTest.java b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePrefixIdTest.java
new file mode 100644
index 00000000000..f7fd2012780
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePrefixIdTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Route;
+import org.apache.camel.model.RouteTemplateDefinition;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class RouteTemplatePrefixIdTest extends ContextTestSupport {
+
+    @Test
+    public void testDefineRouteTemplate() throws Exception {
+        assertEquals(1, context.getRouteTemplateDefinitions().size());
+
+        RouteTemplateDefinition routeTemplate = context.getRouteTemplateDefinition("myTemplate");
+        assertEquals("foo", routeTemplate.getTemplateParameters().get(0).getName());
+        assertEquals("bar", routeTemplate.getTemplateParameters().get(1).getName());
+    }
+
+    @Test
+    public void testCreateRouteFromRouteTemplate() throws Exception {
+        assertEquals(1, context.getRouteTemplateDefinitions().size());
+
+        RouteTemplateDefinition routeTemplate = context.getRouteTemplateDefinition("myTemplate");
+        assertEquals("foo", routeTemplate.getTemplateParameters().get(0).getName());
+        assertEquals("bar", routeTemplate.getTemplateParameters().get(1).getName());
+
+        getMockEndpoint("mock:cheese").expectedBodiesReceived("Hello Cheese");
+        getMockEndpoint("mock:cake").expectedBodiesReceived("Hello Cake");
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("foo", "one");
+        parameters.put("bar", "cheese");
+        context.addRouteFromTemplate("first", "myTemplate", "aaa", parameters);
+
+        parameters.put("foo", "two");
+        parameters.put("bar", "cake");
+        context.addRouteFromTemplate("second", "myTemplate", "bbb", parameters);
+
+        assertEquals(2, context.getRouteDefinitions().size());
+        assertEquals(2, context.getRoutes().size());
+        assertEquals("Started", context.getRouteController().getRouteStatus("first").name());
+        assertEquals("Started", context.getRouteController().getRouteStatus("second").name());
+        assertEquals("true", context.getRoute("first").getProperties().get(Route.TEMPLATE_PROPERTY));
+        assertEquals("true", context.getRoute("second").getProperties().get(Route.TEMPLATE_PROPERTY));
+
+        template.sendBody("direct:one", "Hello Cheese");
+        template.sendBody("direct:two", "Hello Cake");
+
+        assertMockEndpointsSatisfied();
+
+        // all nodes should include prefix
+        Assertions.assertEquals(3, context.getRoute("first").filter("aaa*").size());
+        Assertions.assertEquals(3, context.getRoute("second").filter("bbb*").size());
+    }
+
+    @Test
+    public void testCreateRouteFromRouteTemplateFluent() throws Exception {
+        assertEquals(1, context.getRouteTemplateDefinitions().size());
+
+        RouteTemplateDefinition routeTemplate = context.getRouteTemplateDefinition("myTemplate");
+        assertEquals("foo", routeTemplate.getTemplateParameters().get(0).getName());
+        assertEquals("bar", routeTemplate.getTemplateParameters().get(1).getName());
+
+        getMockEndpoint("mock:cheese").expectedBodiesReceived("Hello Cheese");
+        getMockEndpoint("mock:cake").expectedBodiesReceived("Hello Cake");
+
+        TemplatedRouteBuilder.builder(context, "myTemplate")
+                .routeId("first")
+                .prefixId("aaa")
+                .parameter("foo", "one")
+                .parameter("bar", "cheese")
+                .add();
+
+        TemplatedRouteBuilder.builder(context, "myTemplate")
+                .routeId("second")
+                .prefixId("bbb")
+                .parameter("foo", "two")
+                .parameter("bar", "cake")
+                .add();
+
+        assertEquals(2, context.getRouteDefinitions().size());
+        assertEquals(2, context.getRoutes().size());
+        assertEquals("Started", context.getRouteController().getRouteStatus("first").name());
+        assertEquals("Started", context.getRouteController().getRouteStatus("second").name());
+        assertEquals("true", context.getRoute("first").getProperties().get(Route.TEMPLATE_PROPERTY));
+        assertEquals("true", context.getRoute("second").getProperties().get(Route.TEMPLATE_PROPERTY));
+
+        template.sendBody("direct:one", "Hello Cheese");
+        template.sendBody("direct:two", "Hello Cake");
+
+        assertMockEndpointsSatisfied();
+
+        // all nodes should include prefix
+        Assertions.assertEquals(3, context.getRoute("first").filter("aaa*").size());
+        Assertions.assertEquals(3, context.getRoute("second").filter("bbb*").size());
+    }
+
+    @Test
+    public void testCreateRouteFromRouteTemplateAutoAssignedRouteId() throws Exception {
+        assertEquals(1, context.getRouteTemplateDefinitions().size());
+
+        RouteTemplateDefinition routeTemplate = context.getRouteTemplateDefinition("myTemplate");
+        assertEquals("foo", routeTemplate.getTemplateParameters().get(0).getName());
+        assertEquals("bar", routeTemplate.getTemplateParameters().get(1).getName());
+
+        getMockEndpoint("mock:cheese").expectedBodiesReceived("Hello Cheese");
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("foo", "one");
+        parameters.put("bar", "cheese");
+        String routeId = context.addRouteFromTemplate(null, "myTemplate", "aaa", parameters);
+
+        assertNotNull(routeId);
+        assertEquals(1, context.getRouteDefinitions().size());
+        assertEquals(1, context.getRoutes().size());
+        assertEquals("Started", context.getRouteController().getRouteStatus(routeId).name());
+        assertEquals("true", context.getRoute(routeId).getProperties().get(Route.TEMPLATE_PROPERTY));
+
+        template.sendBody("direct:one", "Hello Cheese");
+
+        assertMockEndpointsSatisfied();
+
+        // all nodes should include prefix
+        Assertions.assertEquals(3, context.getRoute(routeId).filter("aaa*").size());
+    }
+
+    @Test
+    public void testCreateRouteFromRouteTemplateAutoAssignedRouteIdClash() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // use a route id that can clash with auto assigned
+                from("direct:hello").to("mock:hello").routeId("route1");
+            }
+        });
+
+        assertEquals(1, context.getRouteDefinitions().size());
+        assertEquals(1, context.getRouteTemplateDefinitions().size());
+
+        RouteTemplateDefinition routeTemplate = context.getRouteTemplateDefinition("myTemplate");
+        assertEquals("foo", routeTemplate.getTemplateParameters().get(0).getName());
+        assertEquals("bar", routeTemplate.getTemplateParameters().get(1).getName());
+
+        getMockEndpoint("mock:cheese").expectedBodiesReceived("Hello Cheese");
+
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("foo", "one");
+        parameters.put("bar", "cheese");
+        String routeId = context.addRouteFromTemplate(null, "myTemplate", "aaa", parameters);
+
+        assertNotNull(routeId);
+        assertNotEquals("route1", routeId, "Should not be named route1");
+        assertEquals(2, context.getRouteDefinitions().size());
+        assertEquals(2, context.getRoutes().size());
+        assertEquals("Started", context.getRouteController().getRouteStatus(routeId).name());
+        assertEquals("true", context.getRoute(routeId).getProperties().get(Route.TEMPLATE_PROPERTY));
+
+        template.sendBody("direct:one", "Hello Cheese");
+
+        assertMockEndpointsSatisfied();
+
+        // all nodes should include prefix
+        Assertions.assertEquals(3, context.getRoute(routeId).filter("aaa*").size());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                routeTemplate("myTemplate").templateParameter("foo").templateParameter("bar")
+                        .from("direct:{{foo}}")
+                        .choice()
+                        .when(header("foo"))
+                        .log("${body}").id("myLog")
+                        .otherwise()
+                        .to("mock:{{bar}}").id("end");
+            }
+        };
+    }
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/RouteNodePrefixIdDuplicateTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/RouteNodePrefixIdDuplicateTest.java
new file mode 100644
index 00000000000..28796d46c7d
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/RouteNodePrefixIdDuplicateTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.processor;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class RouteNodePrefixIdDuplicateTest extends ContextTestSupport {
+
+    @Test
+    public void testRoutePrefixId() throws Exception {
+        Assertions.assertEquals(2, context.getRoutes().size());
+
+        // ID should be prefixed
+        SendProcessor send = context.getProcessor("myMock", SendProcessor.class);
+        Assertions.assertNull(send);
+        send = context.getProcessor("aaamyMock", SendProcessor.class);
+        Assertions.assertNotNull(send);
+        send = context.getProcessor("bbbmyMock", SendProcessor.class);
+        Assertions.assertNotNull(send);
+
+        // all nodes should include prefix
+        Assertions.assertEquals(2, context.getRoute("foo").filter("aaa*").size());
+        Assertions.assertEquals(2, context.getRoute("bar").filter("bbb*").size());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo").routeId("foo").nodePrefixId("aaa")
+                        .to("mock:foo").id("myMock")
+                        .to("seda:foo");
+
+                from("direct:bar").nodePrefixId("bbb").routeId("bar")
+                        .to("mock:bar").id("myMock")
+                        .to("seda:bar");
+            }
+        };
+    }
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/RouteNodePrefixIdTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/RouteNodePrefixIdTest.java
new file mode 100644
index 00000000000..61e8c5216a3
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/RouteNodePrefixIdTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.processor;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteNodePrefixIdTest extends ContextTestSupport {
+
+    @Test
+    public void testRoutePrefixId() throws Exception {
+        Assertions.assertEquals(3, context.getRoutes().size());
+
+        // ID should be prefixed
+        SendProcessor send = context.getProcessor("aaamyFoo", SendProcessor.class);
+        Assertions.assertNotNull(send);
+        send = context.getProcessor("bbbmyBar", SendProcessor.class);
+        Assertions.assertNotNull(send);
+        send = context.getProcessor("cccmyCheese", SendProcessor.class);
+        Assertions.assertNotNull(send);
+
+        // all nodes should include prefix
+        Assertions.assertEquals(2, context.getRoute("foo").filter("aaa*").size());
+        Assertions.assertEquals(2, context.getRoute("bar").filter("bbb*").size());
+        Assertions.assertEquals(3, context.getRoutes().get(2).filter("ccc*").size());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo").routeId("foo").nodePrefixId("aaa")
+                        .to("mock:foo").id("myFoo")
+                        .to("seda:foo");
+
+                from("direct:bar").nodePrefixId("bbb").routeId("bar")
+                        .to("mock:bar").id("myBar")
+                        .to("seda:bar");
+
+                from("direct:cheese")
+                        .nodePrefixId("ccc")
+                        .choice()
+                            .when(header("cheese"))
+                                .to("mock:cheese").id("myCheese")
+                            .otherwise()
+                                .to("mock:gauda")
+                            .end();
+            }
+        };
+    }
+}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainTemplatedRoutePrefixIdTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainTemplatedRoutePrefixIdTest.java
new file mode 100644
index 00000000000..1d5e9e8686b
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainTemplatedRoutePrefixIdTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.main;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.processor.LogProcessor;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class MainTemplatedRoutePrefixIdTest {
+
+    @Test
+    void testMain() throws Exception {
+        Main main = new Main();
+        main.configure().addRoutesBuilder(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                routeTemplate("myTemplate")
+                        .templateParameter("foo")
+                        .templateParameter("bar")
+                        .from("direct:{{foo}}")
+                        .choice()
+                        .when(header("foo"))
+                        .log("${body}").id("myLog")
+                        .otherwise()
+                        .to("mock:{{bar}}").id("end");
+
+                templatedRoute("myTemplate")
+                        .routeId("my-route")
+                        .prefixId("aaa")
+                        .parameter("foo", "fooVal")
+                        .parameter("bar", "barVal");
+
+                templatedRoute("myTemplate")
+                        .routeId("my-route2")
+                        .prefixId("bbb")
+                        .parameter("foo", "fooVal2")
+                        .parameter("bar", "barVal2");
+            }
+        });
+
+        main.start();
+
+        CamelContext context = main.getCamelContext();
+        assertEquals(2, context.getRoutes().size());
+
+        // ID should be prefixed
+        LogProcessor log = context.getProcessor("aaamyLog", LogProcessor.class);
+        Assertions.assertNotNull(log);
+        log = context.getProcessor("bbbmyLog", LogProcessor.class);
+        Assertions.assertNotNull(log);
+
+        // all nodes should include prefix
+        Assertions.assertEquals(3, context.getRoute("my-route").filter("aaa*").size());
+        Assertions.assertEquals(3, context.getRoute("my-route2").filter("bbb*").size());
+
+        main.stop();
+    }
+
+}
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/LocalBeanRegistry.java b/core/camel-support/src/main/java/org/apache/camel/support/LocalBeanRegistry.java
index 08426c6ca9f..d26845f611a 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/LocalBeanRegistry.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/LocalBeanRegistry.java
@@ -48,9 +48,11 @@ public final class LocalBeanRegistry extends SupplierRegistry {
      * registry or endpoint registry in Camel. Then there is a check that validates for clashes and then re-assign key
      * names.
      *
-     * @param oldKey the old key name
-     * @param newKey the new key name
+     * @param      oldKey the old key name
+     * @param      newKey the new key name
+     * @deprecated        not in use
      */
+    @Deprecated
     public void swapKey(String oldKey, String newKey) {
         Map<Class<?>, Object> value = remove(oldKey);
         if (value != null) {
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index f4aaa921674..297e9f235fe 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -1043,6 +1043,7 @@ public class ModelParser extends BaseParser {
                 case "group": def.setGroup(val); break;
                 case "logMask": def.setLogMask(val); break;
                 case "messageHistory": def.setMessageHistory(val); break;
+                case "nodePrefixId": def.setNodePrefixId(val); break;
                 case "precondition": def.setPrecondition(val); break;
                 case "routeConfigurationId": def.setRouteConfigurationId(val); break;
                 case "routePolicyRef": def.setRoutePolicyRef(val); break;
@@ -1363,6 +1364,7 @@ public class ModelParser extends BaseParser {
     protected TemplatedRouteDefinition doParseTemplatedRouteDefinition() throws IOException, XmlPullParserException {
         return doParse(new TemplatedRouteDefinition(), (def, key, val) -> {
             switch (key) {
+                case "prefixId": def.setPrefixId(val); break;
                 case "routeId": def.setRouteId(val); break;
                 case "routeTemplateRef": def.setRouteTemplateRef(val); break;
                 default: return false;
diff --git a/docs/user-manual/modules/ROOT/pages/route-template.adoc b/docs/user-manual/modules/ROOT/pages/route-template.adoc
index ccc86207b49..8b08497f9a6 100644
--- a/docs/user-manual/modules/ROOT/pages/route-template.adoc
+++ b/docs/user-manual/modules/ROOT/pages/route-template.adoc
@@ -254,6 +254,89 @@ And in YAML DSL
         value: "5s"
 ----
 
+=== Using hardcoded node IDs in route templates
+
+If route templates contains hardcoded node IDs then routes created from templates will use the same IDs
+and therefore if 2 or more routes are created from the same template, you will have _duplicate id detected_ error.
+
+Given the route template below, then it has hardcoded ID (_new-order_) in node calling the http services.
+
+[source,java]
+----
+public class MyRouteTemplates extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        routeTemplate("orderTemplate")
+            .templateParameter("queue")
+            .from("jms:{{queue}}")
+                .to("http:orderserver.acme.com/neworder").id("new-order")
+                .log("Processing order");
+    }
+}
+----
+
+When creating routes from templates, you can then provide a _prefix_ which are used for all node IDs.
+This allows to create 2 or more routes without _duplicate id_ errors.
+
+For example in the following we create a new route _myCoolRoute_ from the _myTemplate_ template, and
+use a prefix of _web_.
+
+And in Java DSL
+
+[source,java]
+----
+templatedRoute("orderTemplate")
+        .routeId("webOrder")
+        .prefixId("web")
+        .parameter("queue", "order.web");
+----
+
+Then we can create a 2nd route:
+
+[source,java]
+----
+templatedRoute("orderTemplate")
+        .routeId("ftpOrder")
+        .prefixId("ftp")
+        .parameter("queue", "order.ftp");
+----
+
+And in Spring XML DSL
+
+[source,xml]
+----
+<camelContext>
+  <templatedRoute routeTemplateRef="orderTemplate" routeId="webOrder" prefixId="web">
+    <parameter name="queue" value="web"/>
+  </templatedRoute>
+</camelContext>
+----
+
+And in XML DSL
+
+[source,xml]
+----
+<templatedRoutes xmlns="http://camel.apache.org/schema/spring">
+  <templatedRoute routeTemplateRef="orderTemplate" routeId="webOrder" prefixId="web">
+    <parameter name="queue" value="web"/>
+  </templatedRoute>
+</templatedRoutes>
+----
+
+And in YAML DSL
+
+[source,yaml]
+----
+- templated-route:
+    route-template-ref: "orderTemplate"
+    route-id: "webOrder"
+    prefix-id: "web"
+    parameters:
+      - name: "queue"
+        value: "web"
+----
+
 == Binding beans to route template
 
 The route template allows to bind beans which is local scoped and only used as part of creating routes from the template.
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
index eaec780238d..08072f756f3 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
@@ -39,6 +39,7 @@ import org.snakeyaml.engine.v2.nodes.NodeTuple;
                   @YamlProperty(name = "id", type = "string"),
                   @YamlProperty(name = "description", type = "string"),
                   @YamlProperty(name = "group", type = "string"),
+                  @YamlProperty(name = "node-prefix-id", type = "string"),
                   @YamlProperty(name = "precondition", type = "string"),
                   @YamlProperty(name = "route-configuration-id", type = "string"),
                   @YamlProperty(name = "auto-startup", type = "boolean"),
@@ -84,6 +85,10 @@ public class RouteDefinitionDeserializer extends YamlDeserializerBase<RouteDefin
                 case "group":
                     target.setGroup(asText(val));
                     break;
+                case "nodePrefixId":
+                case "node-prefix-id":
+                    target.setNodePrefixId(asText(val));
+                    break;
                 case "routeConfigurationId":
                 case "route-configuration-id":
                     target.setRouteConfigurationId(asText(val));
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/TemplatedRouteDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/TemplatedRouteDefinitionDeserializer.java
index e088c47c451..fd9f8747ffe 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/TemplatedRouteDefinitionDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/TemplatedRouteDefinitionDeserializer.java
@@ -35,6 +35,8 @@ import org.snakeyaml.engine.v2.nodes.Node;
           properties = {
                   @YamlProperty(name = "route-id",
                                 type = "string"),
+                  @YamlProperty(name = "prefix-id",
+                                type = "string"),
                   @YamlProperty(name = "route-template-ref",
                                 type = "string",
                                 required = true),
@@ -63,6 +65,10 @@ public class TemplatedRouteDefinitionDeserializer extends YamlDeserializerBase<T
                 target.setRouteId(asText(node));
                 break;
             }
+            case "prefixId":
+            case "prefix-id":
+                target.setPrefixId(asText(node));
+                break;
             case "routeTemplateRef":
             case "route-template-ref": {
                 target.setRouteTemplateRef(asText(node));
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camel-yaml-dsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camel-yaml-dsl.json
index 960fa3c8b7f..68cc7020f88 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camel-yaml-dsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camel-yaml-dsl.json
@@ -2565,6 +2565,9 @@
           "message-history" : {
             "type" : "boolean"
           },
+          "node-prefix-id" : {
+            "type" : "string"
+          },
           "precondition" : {
             "type" : "string"
           },
@@ -3097,6 +3100,9 @@
               "$ref" : "#/items/definitions/org.apache.camel.model.TemplatedRouteParameterDefinition"
             }
           },
+          "prefix-id" : {
+            "type" : "string"
+          },
           "route-id" : {
             "type" : "string"
           },
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index d98ccef879d..13b4e3c94a1 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -2469,6 +2469,9 @@
           "messageHistory" : {
             "type" : "boolean"
           },
+          "nodePrefixId" : {
+            "type" : "string"
+          },
           "precondition" : {
             "type" : "string"
           },
@@ -3001,6 +3004,9 @@
               "$ref" : "#/items/definitions/org.apache.camel.model.TemplatedRouteParameterDefinition"
             }
           },
+          "prefixId" : {
+            "type" : "string"
+          },
           "routeId" : {
             "type" : "string"
           },
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteTemplateTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteTemplateTest.groovy
index 362eb48a614..057c51499b4 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteTemplateTest.groovy
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteTemplateTest.groovy
@@ -484,4 +484,44 @@ class RouteTemplateTest extends YamlTestSupport {
             }
     }
 
+    def "create route-template with prefix"() {
+        setup:
+        loadRoutes """
+                - route-template:
+                    id: "myTemplate"
+                    parameters:
+                      - name: "foo"
+                      - name: "bar"
+                    from:
+                      uri: "direct:{{foo}}"
+                      steps:
+                      - choice:  
+                          when:
+                            - header: "foo"
+                              steps:
+                                - log:
+                                    id: "myLog"
+                                    message: "Hello World"
+                          otherwise:
+                            steps:
+                              - to:
+                                  uri: "mock:{{bar}}"
+                                  id: "end"
+            """
+        when:
+        Map<String, Object> parameters = new HashMap<>();
+        parameters.put("foo", "one");
+        parameters.put("bar", "cheese");
+        context.addRouteFromTemplate("first", "myTemplate", "aaa", parameters);
+
+        parameters.put("foo", "two");
+        parameters.put("bar", "cake");
+        context.addRouteFromTemplate("second", "myTemplate", "bbb", parameters);
+        context.start()
+
+        then:
+        Assertions.assertEquals(3, context.getRoute("first").filter("aaa*").size());
+        Assertions.assertEquals(3, context.getRoute("second").filter("bbb*").size());
+    }
+
 }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
index b11cabc5afe..32038c0a122 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.yaml
 import org.apache.camel.dsl.yaml.support.YamlTestSupport
 import org.apache.camel.model.LogDefinition
 import org.apache.camel.model.RouteDefinition
+import org.junit.jupiter.api.Assertions
 
 class RoutesTest extends YamlTestSupport {
 
@@ -307,4 +308,36 @@ class RoutesTest extends YamlTestSupport {
         }
     }
 
+    def "load route with node-prefix-id"() {
+        when:
+        loadRoutes '''
+                - route:
+                    id: foo
+                    node-prefix-id: aaa
+                    from:
+                      uri: "direct:foo"
+                      steps:
+                        - to:
+                            id: "myFoo"
+                            uri: "mock:foo"
+                        - to: "seda:foo"
+                - route:
+                    id: bar
+                    node-prefix-id: bbb
+                    from:
+                      uri: "direct:bar"
+                      steps:
+                        - to:
+                            id: "myBar"
+                            uri: "mock:bar"
+                        - to: "seda:bar"
+            '''
+        then:
+        context.routeDefinitions.size() == 2
+        context.start()
+
+        Assertions.assertEquals(2, context.getRoute("foo").filter("aaa*").size());
+        Assertions.assertEquals(2, context.getRoute("bar").filter("bbb*").size());
+    }
+
 }