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 08:37:15 UTC

[camel] 01/04: 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.

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

davsclaus pushed a commit to branch prefixId
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 6098712077423bedb8b5d21d7aaa3d9fb2516a27
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Dec 7 07:36:51 2022 +0100

    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 +
 .../main/java/org/apache/camel/CamelContext.java   |  35 ++++
 .../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  |  38 ++++
 .../camel/model/TemplatedRouteDefinition.java      |  20 +++
 .../camel/builder/RouteTemplatePrefixIdTest.java   | 194 +++++++++++++++++++++
 .../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 +
 22 files changed, 698 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..fe5d87f4f42 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 when assigning route and node IDs." },
     "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-api/src/main/java/org/apache/camel/CamelContext.java b/core/camel-api/src/main/java/org/apache/camel/CamelContext.java
index fda8bccb99a..be6a61d50f8 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
@@ -635,6 +635,24 @@ public interface CamelContext extends CamelContextLifecycle, RuntimeConfiguratio
      */
     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 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.
      *
@@ -650,6 +668,23 @@ public interface CamelContext extends CamelContextLifecycle, RuntimeConfiguratio
     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 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;
+
     /**
      * 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..fe5d87f4f42 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 when assigning route and node IDs." },
     "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..09cd5de0941 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 when assigning route and node IDs.
+     *
+     * @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..23846c0a94d 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();
     }
 
+    /**
+     * Prefix to use for node IDs (both set and auto-assigned IDs)
+     *
+     * @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..48fe5496bef 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;
     }
 
+    /**
+     * Prefix to use for node IDs (both set and auto-assigned IDs)
+     *
+     * @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 route and node IDs (also IDs that has been explicit set to a value)
+     */
+    public String getNodePrefixId() {
+        return nodePrefixId;
+    }
+
+    /**
+     * Prefix to use for route and node IDs (also IDs that has been explicit set to a value)
+     */
+    @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..2b07b5bb86a 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;
             }
@@ -739,6 +754,29 @@ public final class RouteDefinitionHelper {
         }
     }
 
+    /**
+     * For all custom assigned ids, then to avoid duplicate ids when creating new routes from route templates, then the
+     * custom assigned ids, must be prefixed.
+     *
+     * @param context   the camel context
+     * @param prefix    the prefix to set on custom assigned id
+     * @param processor the node
+     */
+    public static void prefixCustomAssignIds(CamelContext context, final String prefix, final ProcessorDefinition processor) {
+        if (processor.hasCustomIdAssigned()) {
+            String originalId = processor.getId();
+            String newId = prefix + originalId;
+            processor.setId(newId);
+        }
+
+        List<ProcessorDefinition<?>> children = processor.getOutputs();
+        if (children != null && !children.isEmpty()) {
+            for (ProcessorDefinition child : children) {
+                prefixCustomAssignIds(context, prefix, child);
+            }
+        }
+    }
+
     public static String getRouteMessage(String route) {
         // cut the route after 60 chars, so it won't be too big in the message
         // users just need to be able to identify the route, so they know where to look
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..5db5ae8942b 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 when assigning route and node IDs.
+     *
+     * @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..eadba72450a
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePrefixIdTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.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 {
+
+    // TODO: docs
+    // TODO: <xml> tests
+    // TODO: regen
+
+    @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();
+    }
+
+    @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();
+    }
+
+    @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();
+    }
+
+    @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();
+    }
+
+    @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;