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

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

commit 7406e84d68bc01bbb01bc145762a07a25d33e375
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Tue Jun 30 10:46:01 2020 +0200

    CAMEL-14963: Route Template. WIP
---
 .../services/org/apache/camel/model.properties     |   2 +
 .../resources/org/apache/camel/model/jaxb.index    |   2 +
 .../org/apache/camel/model/routeTemplate.json      |  31 +++++
 .../org/apache/camel/model/routeTemplates.json     |  17 +++
 .../org/apache/camel/builder/RouteBuilder.java     |  37 ++++++
 .../org/apache/camel/impl/DefaultCamelContext.java |  31 +++++
 .../java/org/apache/camel/impl/DefaultModel.java   |  40 +++++++
 .../camel/impl/lw/LightweightCamelContext.java     |  31 +++++
 .../main/java/org/apache/camel/model/Model.java    |  69 +++++++++++
 .../org/apache/camel/model/RouteDefinition.java    |   2 +-
 .../apache/camel/model/RouteTemplateContainer.java |  42 +++++++
 .../camel/model/RouteTemplateDefinition.java       |  72 +++++++++++
 .../camel/model/RouteTemplatesDefinition.java      | 133 +++++++++++++++++++++
 .../apache/camel/builder/RouteTemplateTest.java    |  46 +++++++
 .../java/org/apache/camel/xml/in/ModelParser.java  |  43 +++++--
 15 files changed, 589 insertions(+), 9 deletions(-)

diff --git 
a/core/camel-core-engine/src/generated/resources/META-INF/services/org/apache/camel/model.properties
 
b/core/camel-core-engine/src/generated/resources/META-INF/services/org/apache/camel/model.properties
index 9259476..1815052 100644
--- 
a/core/camel-core-engine/src/generated/resources/META-INF/services/org/apache/camel/model.properties
+++ 
b/core/camel-core-engine/src/generated/resources/META-INF/services/org/apache/camel/model.properties
@@ -131,6 +131,8 @@ roundRobin
 route
 routeBuilder
 routeContextRef
+routeTemplate
+routeTemplates
 routes
 routingSlip
 rss
diff --git 
a/core/camel-core-engine/src/generated/resources/org/apache/camel/model/jaxb.index
 
b/core/camel-core-engine/src/generated/resources/org/apache/camel/model/jaxb.index
index d6c82d7..b57f01f 100644
--- 
a/core/camel-core-engine/src/generated/resources/org/apache/camel/model/jaxb.index
+++ 
b/core/camel-core-engine/src/generated/resources/org/apache/camel/model/jaxb.index
@@ -64,6 +64,8 @@ RollbackDefinition
 RouteBuilderDefinition
 RouteContextRefDefinition
 RouteDefinition
+RouteTemplateDefinition
+RouteTemplatesDefinition
 RoutesDefinition
 RoutingSlipDefinition
 SagaCompletionMode
diff --git 
a/core/camel-core-engine/src/generated/resources/org/apache/camel/model/routeTemplate.json
 
b/core/camel-core-engine/src/generated/resources/org/apache/camel/model/routeTemplate.json
new file mode 100644
index 0000000..2a48fa2
--- /dev/null
+++ 
b/core/camel-core-engine/src/generated/resources/org/apache/camel/model/routeTemplate.json
@@ -0,0 +1,31 @@
+{
+  "model": {
+    "kind": "model",
+    "name": "routeTemplate",
+    "title": "Route Template",
+    "description": "Defines a route template (parameterized routes)",
+    "deprecated": false,
+    "label": "configuration",
+    "javaType": "org.apache.camel.model.RouteTemplateDefinition",
+    "input": true,
+    "output": true
+  },
+  "properties": {
+    "properties": { "kind": "attribute", "displayName": "Properties", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "secret": false, "description": "Route properties. 
Multiple keys can be separated by comma." },
+    "group": { "kind": "attribute", "displayName": "Group", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"secret": false, "description": "The group that this route belongs to; could be 
the name of the RouteBuilder class or be explicitly configured in the XML. May 
be null." },
+    "streamCache": { "kind": "attribute", "displayName": "Stream Cache", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "secret": false, "description": "Whether stream caching is 
enabled on this route." },
+    "trace": { "kind": "attribute", "displayName": "Trace", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"secret": false, "description": "Whether tracing is enabled on this route." },
+    "messageHistory": { "kind": "attribute", "displayName": "Message History", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "secret": false, "defaultValue": "true", "description": 
"Whether message history is enabled on this route." },
+    "logMask": { "kind": "attribute", "displayName": "Log Mask", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"secret": false, "defaultValue": "false", "description": "Whether security mask 
for Logging is enabled on this route." },
+    "delayer": { "kind": "attribute", "displayName": "Delayer", "required": 
false, "type": "duration", "javaType": "java.lang.String", "deprecated": false, 
"secret": false, "description": "Whether to slow down processing messages by a 
given delay in msec." },
+    "autoStartup": { "kind": "attribute", "displayName": "Auto Startup", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "secret": false, "defaultValue": "true", "description": 
"Whether to auto start this route" },
+    "startupOrder": { "kind": "attribute", "displayName": "Startup Order", 
"required": false, "type": "integer", "javaType": "java.lang.Integer", 
"deprecated": false, "secret": false, "description": "To configure the ordering 
of the routes being started" },
+    "errorHandlerRef": { "kind": "attribute", "displayName": "Error Handler", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "secret": false, "description": "Sets the bean ref name of 
the error handler builder to use on this route" },
+    "routePolicyRef": { "kind": "attribute", "displayName": "Route Policy", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "secret": false, "description": "Reference to custom 
org.apache.camel.spi.RoutePolicy to use by the route. Multiple policies can be 
configured by separating values using comma." },
+    "shutdownRoute": { "kind": "attribute", "displayName": "Shutdown Route", 
"required": false, "type": "enum", "javaType": 
"org.apache.camel.ShutdownRoute", "enum": [ "Default", "Defer" ], "deprecated": 
false, "secret": false, "description": "To control how to shutdown the route." 
},
+    "shutdownRunningTask": { "kind": "attribute", "displayName": "Shutdown 
Running Task", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ShutdownRunningTask", "enum": [ "CompleteCurrentTaskOnly", 
"CompleteAllTasks" ], "deprecated": false, "secret": false, "description": "To 
control how to shutdown the route." },
+    "input": { "kind": "element", "displayName": "Input", "required": true, 
"type": "object", "javaType": "org.apache.camel.model.FromDefinition", "oneOf": 
[ "from" ], "deprecated": false, "secret": false, "description": "Input to the 
route." },
+    "id": { "kind": "attribute", "displayName": "Id", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"secret": false, "description": "Sets the id of this node" },
+    "description": { "kind": "element", "displayName": "Description", 
"required": false, "type": "object", "javaType": 
"org.apache.camel.model.DescriptionDefinition", "deprecated": false, "secret": 
false, "description": "Sets the description of this node" }
+  }
+}
diff --git 
a/core/camel-core-engine/src/generated/resources/org/apache/camel/model/routeTemplates.json
 
b/core/camel-core-engine/src/generated/resources/org/apache/camel/model/routeTemplates.json
new file mode 100644
index 0000000..199e76c
--- /dev/null
+++ 
b/core/camel-core-engine/src/generated/resources/org/apache/camel/model/routeTemplates.json
@@ -0,0 +1,17 @@
+{
+  "model": {
+    "kind": "model",
+    "name": "routeTemplates",
+    "title": "Route Templates",
+    "description": "A series of route templates",
+    "deprecated": false,
+    "label": "routeTemplates",
+    "javaType": "org.apache.camel.model.RouteTemplatesDefinition",
+    "input": false,
+    "output": false
+  },
+  "properties": {
+    "id": { "kind": "attribute", "displayName": "Id", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"secret": false, "description": "Sets the id of this node" },
+    "description": { "kind": "element", "displayName": "Description", 
"required": false, "type": "object", "javaType": 
"org.apache.camel.model.DescriptionDefinition", "deprecated": false, "secret": 
false, "description": "Sets the description of this node" }
+  }
+}
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/builder/RouteBuilder.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/builder/RouteBuilder.java
index dd66c78..9393748 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/builder/RouteBuilder.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/builder/RouteBuilder.java
@@ -35,6 +35,8 @@ import org.apache.camel.model.Model;
 import org.apache.camel.model.OnCompletionDefinition;
 import org.apache.camel.model.OnExceptionDefinition;
 import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.RouteTemplateDefinition;
+import org.apache.camel.model.RouteTemplatesDefinition;
 import org.apache.camel.model.RoutesDefinition;
 import org.apache.camel.model.rest.RestConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
@@ -61,6 +63,7 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
     private List<TransformerBuilder> transformerBuilders = new ArrayList<>();
     private List<ValidatorBuilder> validatorBuilders = new ArrayList<>();
     private RoutesDefinition routeCollection = new RoutesDefinition();
+    private RouteTemplatesDefinition routeTemplateCollection = new 
RouteTemplatesDefinition();
     private final List<RouteBuilderLifecycleStrategy> lifecycleInterceptors = 
new ArrayList<>();
 
     public RouteBuilder() {
@@ -158,6 +161,18 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
     }
 
     /**
+     * Creates a new route template
+     *
+     * @return the builder
+     */
+    public RouteTemplateDefinition routeTemplate(String id, String... 
properties) {
+        getRouteTemplateCollection().setCamelContext(getContext());
+        RouteTemplateDefinition answer = 
getRouteTemplateCollection().routeTemplate(id, properties);
+        configureRouteTemplate(answer);
+        return answer;
+    }
+
+    /**
      * Creates a new REST service
      *
      * @return the builder
@@ -408,6 +423,7 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         populateRests();
         populateTransformers();
         populateValidators();
+        populateRouteTemplates();
         populateRoutes();
 
         if (this instanceof OnCamelContextEvent) {
@@ -489,6 +505,15 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         }
     }
 
+    protected void populateRouteTemplates() throws Exception {
+        CamelContext camelContext = getContext();
+        if (camelContext == null) {
+            throw new IllegalArgumentException("CamelContext has not been 
injected!");
+        }
+        getRouteTemplateCollection().setCamelContext(camelContext);
+        
camelContext.getExtension(Model.class).addRouteTemplateDefinitions(getRouteTemplateCollection().getRouteTemplates());
+    }
+
     protected void populateRoutes() throws Exception {
         CamelContext camelContext = getContext();
         if (camelContext == null) {
@@ -582,6 +607,14 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         return this.routeCollection;
     }
 
+    public RouteTemplatesDefinition getRouteTemplateCollection() {
+        return routeTemplateCollection;
+    }
+
+    public void setRouteTemplateCollection(RouteTemplatesDefinition 
routeTemplateCollection) {
+        this.routeTemplateCollection = routeTemplateCollection;
+    }
+
     protected void configureRest(RestDefinition rest) {
         // noop
     }
@@ -590,4 +623,8 @@ public abstract class RouteBuilder extends BuilderSupport 
implements RoutesBuild
         // noop
     }
 
+    protected void configureRouteTemplate(RouteTemplateDefinition 
routeTemplate) {
+        // noop
+    }
+
 }
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 67e34f7..6002917 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
@@ -43,6 +43,7 @@ import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RouteDefinitionHelper;
+import org.apache.camel.model.RouteTemplateDefinition;
 import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
 import org.apache.camel.model.language.ExpressionDefinition;
 import org.apache.camel.model.rest.RestDefinition;
@@ -143,6 +144,36 @@ public class DefaultCamelContext extends 
SimpleCamelContext implements ModelCame
     }
 
     @Override
+    public List<RouteTemplateDefinition> getRouteTemplateDefinitions() {
+        return model.getRouteTemplateDefinitions();
+    }
+
+    @Override
+    public RouteTemplateDefinition getRouteTemplateDefinition(String id) {
+        return model.getRouteTemplateDefinition(id);
+    }
+
+    @Override
+    public void 
addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> 
routeTemplateDefinitions) throws Exception {
+        model.addRouteTemplateDefinitions(routeTemplateDefinitions);
+    }
+
+    @Override
+    public void addRouteTemplateDefinition(RouteTemplateDefinition 
routeTemplateDefinition) throws Exception {
+        model.addRouteTemplateDefinition(routeTemplateDefinition);
+    }
+
+    @Override
+    public void 
removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> 
routeTemplateDefinitions) throws Exception {
+        model.removeRouteTemplateDefinitions(routeTemplateDefinitions);
+    }
+
+    @Override
+    public void removeRouteTemplateDefinition(RouteTemplateDefinition 
routeTemplateDefinition) throws Exception {
+        model.removeRouteTemplateDefinition(routeTemplateDefinition);
+    }
+
+    @Override
     public List<RestDefinition> getRestDefinitions() {
         return model.getRestDefinitions();
     }
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 3145fdc..2a0160a 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
@@ -38,6 +38,7 @@ import org.apache.camel.model.ProcessorDefinitionHelper;
 import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.RouteFilters;
+import org.apache.camel.model.RouteTemplateDefinition;
 import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.transformer.TransformerDefinition;
@@ -48,6 +49,7 @@ public class DefaultModel implements Model {
     private final CamelContext camelContext;
 
     private final List<RouteDefinition> routeDefinitions = new ArrayList<>();
+    private final List<RouteTemplateDefinition> routeTemplateDefinitions = new 
ArrayList<>();
     private final List<RestDefinition> restDefinitions = new ArrayList<>();
     private Map<String, DataFormatDefinition> dataFormats = new HashMap<>();
     private List<TransformerDefinition> transformers = new ArrayList<>();
@@ -126,6 +128,44 @@ public class DefaultModel implements Model {
     }
 
     @Override
+    public List<RouteTemplateDefinition> getRouteTemplateDefinitions() {
+        return routeTemplateDefinitions;
+    }
+
+    @Override
+    public RouteTemplateDefinition getRouteTemplateDefinition(String id) {
+        for (RouteTemplateDefinition route : routeTemplateDefinitions) {
+            if 
(route.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory()).equals(id))
 {
+                return route;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void 
addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> 
routeTemplateDefinitions) throws Exception {
+        if (routeTemplateDefinitions == null || 
routeTemplateDefinitions.isEmpty()) {
+            return;
+        }
+        this.routeTemplateDefinitions.addAll(routeTemplateDefinitions);
+    }
+
+    @Override
+    public void addRouteTemplateDefinition(RouteTemplateDefinition 
routeTemplateDefinition) throws Exception {
+        
addRouteTemplateDefinitions(Collections.singletonList(routeTemplateDefinition));
+    }
+
+    @Override
+    public void 
removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> 
routeTemplateDefinitions) throws Exception {
+        routeTemplateDefinitions.removeAll(routeTemplateDefinitions);
+    }
+
+    @Override
+    public void removeRouteTemplateDefinition(RouteTemplateDefinition 
routeTemplateDefinition) throws Exception {
+        routeTemplateDefinitions.remove(routeTemplateDefinition);
+    }
+
+    @Override
     public synchronized List<RestDefinition> getRestDefinitions() {
         return restDefinitions;
     }
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 8dd1887..b30db89 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
@@ -62,6 +62,7 @@ import org.apache.camel.model.ModelCamelContext;
 import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.model.Resilience4jConfigurationDefinition;
 import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.RouteTemplateDefinition;
 import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
 import org.apache.camel.model.language.ExpressionDefinition;
 import org.apache.camel.model.rest.RestDefinition;
@@ -1498,6 +1499,36 @@ public class LightweightCamelContext implements 
ExtendedCamelContext, CatalogCam
     }
 
     @Override
+    public List<RouteTemplateDefinition> getRouteTemplateDefinitions() {
+        return getModelCamelContext().getRouteTemplateDefinitions();
+    }
+
+    @Override
+    public RouteTemplateDefinition getRouteTemplateDefinition(String id) {
+        return getModelCamelContext().getRouteTemplateDefinition(id);
+    }
+
+    @Override
+    public void 
addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> 
routeTemplateDefinitions) throws Exception {
+        
getModelCamelContext().addRouteTemplateDefinitions(routeTemplateDefinitions);
+    }
+
+    @Override
+    public void addRouteTemplateDefinition(RouteTemplateDefinition 
routeTemplateDefinition) throws Exception {
+        
getModelCamelContext().addRouteTemplateDefinition(routeTemplateDefinition);
+    }
+
+    @Override
+    public void 
removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> 
routeTemplateDefinitions) throws Exception {
+        
getModelCamelContext().removeRouteTemplateDefinitions(routeTemplateDefinitions);
+    }
+
+    @Override
+    public void removeRouteTemplateDefinition(RouteTemplateDefinition 
routeTemplateDefinition) throws Exception {
+        
getModelCamelContext().removeRouteTemplateDefinition(routeTemplateDefinition);
+    }
+
+    @Override
     public List<RestDefinition> getRestDefinitions() {
         return getModelCamelContext().getRestDefinitions();
     }
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/model/Model.java 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/Model.java
index 5f90bc7..5c0d652 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/model/Model.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/model/Model.java
@@ -98,6 +98,75 @@ public interface Model {
      */
     void removeRouteDefinition(RouteDefinition routeDefinition) throws 
Exception;
 
+    // TODO: update javadoc
+
+    /**
+     * Returns a list of the current route definitions
+     *
+     * @return list of the current route definitions
+     */
+    List<RouteTemplateDefinition> getRouteTemplateDefinitions();
+
+    /**
+     * Gets the route definition with the given id
+     *
+     * @param id id of the route
+     * @return the route definition or <tt>null</tt> if not found
+     */
+    RouteTemplateDefinition getRouteTemplateDefinition(String id);
+
+    /**
+     * Adds a collection of route definitions to the context
+     * <p/>
+     * <b>Important: </b> Each route in the same {@link CamelContext} must have
+     * an <b>unique</b> route id. If you use the API from {@link CamelContext}
+     * or {@link Model} to add routes, then any new routes which has a route id
+     * that matches an old route, then the old route is replaced by the new
+     * route.
+     *
+     * @param routeTemplateDefinitions the route(s) definition to add
+     * @throws Exception if the route definitions could not be added for
+     *             whatever reason
+     */
+    void addRouteTemplateDefinitions(Collection<RouteTemplateDefinition> 
routeTemplateDefinitions) throws Exception;
+
+    /**
+     * Add a route definition to the context
+     * <p/>
+     * <b>Important: </b> Each route in the same {@link CamelContext} must have
+     * an <b>unique</b> route id. If you use the API from {@link CamelContext}
+     * or {@link Model} to add routes, then any new routes which has a route id
+     * that matches an old route, then the old route is replaced by the new
+     * route.
+     *
+     * @param routeTemplateDefinition the route definition to add
+     * @throws Exception if the route definition could not be added for 
whatever
+     *             reason
+     */
+    void addRouteTemplateDefinition(RouteTemplateDefinition 
routeTemplateDefinition) throws Exception;
+
+    /**
+     * Removes a collection of route definitions from the context - stopping 
any
+     * previously running routes if any of them are actively running
+     *
+     * @param routeTemplateDefinitions route(s) definitions to remove
+     * @throws Exception if the route definitions could not be removed for
+     *             whatever reason
+     */
+    void removeRouteTemplateDefinitions(Collection<RouteTemplateDefinition> 
routeTemplateDefinitions) throws Exception;
+
+    /**
+     * Removes a route definition from the context - stopping any previously
+     * running routes if any of them are actively running
+     *
+     * @param routeTemplateDefinition route definition to remove
+     * @throws Exception if the route definition could not be removed for
+     *             whatever reason
+     */
+    void removeRouteTemplateDefinition(RouteTemplateDefinition 
routeTemplateDefinition) throws Exception;
+
+
+
     /**
      * Returns a list of the current REST definitions
      *
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteDefinition.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteDefinition.java
index 9fd44a1..828060a 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteDefinition.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteDefinition.java
@@ -102,7 +102,7 @@ public class RouteDefinition extends 
OutputDefinition<RouteDefinition> implement
     /**
      * Check if the route has been prepared
      *
-     * @return wether the route has been prepared or not
+     * @return whether the route has been prepared or not
      * @see RouteDefinitionHelper#prepareRoute(ModelCamelContext,
      *      RouteDefinition)
      */
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateContainer.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateContainer.java
new file mode 100644
index 0000000..ae4e8c8
--- /dev/null
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateContainer.java
@@ -0,0 +1,42 @@
+/*
+ * 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.model;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElementRef;
+
+/**
+ * Container to hold {@link RouteTemplateDefinition Route}.
+ */
+public interface RouteTemplateContainer {
+
+    /**
+     * Returns the route templates
+     *
+     * @return the route templates
+     */
+    @XmlElementRef
+    List<RouteTemplateDefinition> getRouteTemplates();
+
+    /**
+     * Sets the route templates to use
+     *
+     * @param routes the route templates
+     */
+    void setRouteTemplates(List<RouteTemplateDefinition> routes);
+}
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
new file mode 100644
index 0000000..b64cc39
--- /dev/null
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
@@ -0,0 +1,72 @@
+/*
+ * 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.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.camel.spi.Metadata;
+
+/**
+ * Defines a route template (parameterized routes)
+ */
+@Metadata(label = "configuration")
+@XmlRootElement(name = "routeTemplate")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RouteTemplateDefinition extends RouteDefinition {
+
+    @XmlAttribute
+    private String properties;
+
+    public String getProperties() {
+        return properties;
+    }
+
+    /**
+     * Route properties. Multiple keys can be separated by comma.
+     */
+    public void setProperties(String properties) {
+        if (this.properties == null) {
+            this.properties = properties;
+        } else {
+            this.properties += "," + properties;
+        }
+    }
+
+    // Fluent API
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Route properties. Multiple keys can be separated by comma.
+     */
+    public RouteTemplateDefinition properties(String properties) {
+        setProperties(properties);
+        return this;
+    }
+
+    @Override
+    public String getShortName() {
+        return "routeTemplate";
+    }
+
+    @Override
+    public String getLabel() {
+        return "RouteTemplate[" + getInput().getLabel() + "]";
+    }
+}
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplatesDefinition.java
 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplatesDefinition.java
new file mode 100644
index 0000000..4cceff1
--- /dev/null
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/model/RouteTemplatesDefinition.java
@@ -0,0 +1,133 @@
+/*
+ * 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.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.Metadata;
+
+/**
+ * A series of route templates
+ */
+@Metadata(label = "routeTemplates")
+@XmlRootElement(name = "routeTemplates")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RouteTemplatesDefinition extends 
OptionalIdentifiedDefinition<RouteTemplatesDefinition> implements 
RouteTemplateContainer {
+    @XmlElementRef
+    private List<RouteTemplateDefinition> routeTemplates = new ArrayList<>();
+    @XmlTransient
+    private CamelContext camelContext;
+
+    public RouteTemplatesDefinition() {
+    }
+
+    @Override
+    public String toString() {
+        return "RouteTemplates: " + routeTemplates;
+    }
+
+    @Override
+    public String getShortName() {
+        return "routeTemplates";
+    }
+
+    @Override
+    public String getLabel() {
+        return "RouteTemplate " + getId();
+    }
+
+    // Properties
+    // -----------------------------------------------------------------------
+
+    @Override
+    public List<RouteTemplateDefinition> getRouteTemplates() {
+        return routeTemplates;
+    }
+
+    /**
+     * The rest services
+     */
+    public void setRouteTemplates(List<RouteTemplateDefinition> 
routeTemplates) {
+        this.routeTemplates = routeTemplates;
+    }
+
+    public CamelContext getCamelContext() {
+        return camelContext;
+    }
+
+    public void setCamelContext(CamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    // Fluent API
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Creates a route template
+     *
+     * @param id the id of the route template
+     * @param properties the properties of the route template, multiple 
properties can be separated by comma
+     */
+    public RouteTemplateDefinition routeTemplate(String id, String properties) 
{
+        RouteTemplateDefinition routeTemplate = createRouteTemplate();
+        routeTemplate.id(id);
+        routeTemplate.properties(properties);
+        return routeTemplate(routeTemplate);
+    }
+
+    /**
+     * Creates a route template
+     *
+     * @param id the id of the route template
+     * @param properties the properties of the route template
+     */
+    public RouteTemplateDefinition routeTemplate(String id, String... 
properties) {
+        RouteTemplateDefinition routeTemplate = createRouteTemplate();
+        routeTemplate.id(id);
+        if (properties != null) {
+            for (String p : properties) {
+                routeTemplate.properties(p);
+            }
+        }
+        return routeTemplate(routeTemplate);
+    }
+
+    /**
+     * Adds the {@link RouteTemplatesDefinition}
+     */
+    public RouteTemplateDefinition routeTemplate(RouteTemplateDefinition rest) 
{
+        getRouteTemplates().add(rest);
+        return rest;
+    }
+
+    // Implementation methods
+    // 
-------------------------------------------------------------------------
+
+    protected RouteTemplateDefinition createRouteTemplate() {
+        RouteTemplateDefinition template = new RouteTemplateDefinition();
+        return template;
+    }
+
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateTest.java 
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateTest.java
new file mode 100644
index 0000000..b95915f
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplateTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.camel.ContextTestSupport;
+import org.apache.camel.model.RouteTemplateDefinition;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteTemplateTest extends ContextTestSupport {
+
+    @Test
+    public void testRouteTemplate() throws Exception {
+        assertEquals(1, context.getRouteTemplateDefinitions().size());
+
+        RouteTemplateDefinition template = 
context.getRouteTemplateDefinition("myTemplate");
+        assertEquals("foo,bar", template.getProperties());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                routeTemplate("myTemplate", "foo", "bar")
+                    .from("direct:{{foo}}")
+                    .to("mock:{{bar}}");
+            }
+        };
+    }
+}
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 b73f673..888b8ad 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
@@ -937,8 +937,8 @@ public class ModelParser extends BaseParser {
             return false;
         }, noElementHandler(), noValueHandler());
     }
-    protected RouteDefinition doParseRouteDefinition() throws IOException, 
XmlPullParserException {
-        return doParse(new RouteDefinition(), (def, key, val) -> {
+    protected <T extends RouteDefinition> AttributeHandler<T> 
routeDefinitionAttributeHandler() {
+        return (def, key, val) -> {
             switch (key) {
                 case "autoStartup": def.setAutoStartup(val); break;
                 case "delayer": def.setDelayer(val); break;
@@ -955,7 +955,10 @@ public class ModelParser extends BaseParser {
                 default: return 
processorDefinitionAttributeHandler().accept(def, key, val);
             }
             return true;
-        }, (def, key) -> {
+        };
+    }
+    protected <T extends RouteDefinition> ElementHandler<T> 
routeDefinitionElementHandler() {
+        return (def, key) -> {
             switch (key) {
                 case "from": def.setInput(doParseFromDefinition()); break;
                 case "inputType": 
def.setInputType(doParseInputTypeDefinition()); break;
@@ -964,7 +967,10 @@ public class ModelParser extends BaseParser {
                 default: return outputDefinitionElementHandler().accept(def, 
key);
             }
             return true;
-        }, noValueHandler());
+        };
+    }
+    protected RouteDefinition doParseRouteDefinition() throws IOException, 
XmlPullParserException {
+        return doParse(new RouteDefinition(), 
routeDefinitionAttributeHandler(), routeDefinitionElementHandler(), 
noValueHandler());
     }
     protected RestDefinition doParseRestDefinition() throws IOException, 
XmlPullParserException {
         return doParse(new RestDefinition(), (def, key, val) -> {
@@ -1013,6 +1019,25 @@ public class ModelParser extends BaseParser {
             return true;
         }, optionalIdentifiedDefinitionElementHandler(), noValueHandler());
     }
+    protected RouteTemplateDefinition doParseRouteTemplateDefinition() throws 
IOException, XmlPullParserException {
+        return doParse(new RouteTemplateDefinition(), (def, key, val) -> {
+            if ("properties".equals(key)) {
+                def.setProperties(val);
+                return true;
+            }
+            return routeDefinitionAttributeHandler().accept(def, key, val);
+        }, routeDefinitionElementHandler(), noValueHandler());
+    }
+    protected RouteTemplatesDefinition doParseRouteTemplatesDefinition() 
throws IOException, XmlPullParserException {
+        return doParse(new RouteTemplatesDefinition(),
+            optionalIdentifiedDefinitionAttributeHandler(), (def, key) -> {
+            if ("routeTemplate".equals(key)) {
+                doAdd(doParseRouteTemplateDefinition(), 
def.getRouteTemplates(), def::setRouteTemplates);
+                return true;
+            }
+            return optionalIdentifiedDefinitionElementHandler().accept(def, 
key);
+        }, noValueHandler());
+    }
     public RoutesDefinition parseRoutesDefinition()
             throws IOException, XmlPullParserException {
         expectTag("routes");
@@ -1021,11 +1046,12 @@ public class ModelParser extends BaseParser {
     protected RoutesDefinition doParseRoutesDefinition() throws IOException, 
XmlPullParserException {
         return doParse(new RoutesDefinition(),
             optionalIdentifiedDefinitionAttributeHandler(), (def, key) -> {
-            if ("route".equals(key)) {
-                doAdd(doParseRouteDefinition(), def.getRoutes(), 
def::setRoutes);
-                return true;
+            switch (key) {
+                case "route": doAdd(doParseRouteDefinition(), def.getRoutes(), 
def::setRoutes); break;
+                case "routeTemplate": doAdd(doParseRouteTemplateDefinition(), 
def.getRoutes(), def::setRoutes); break;
+                default: return 
optionalIdentifiedDefinitionElementHandler().accept(def, key);
             }
-            return optionalIdentifiedDefinitionElementHandler().accept(def, 
key);
+            return true;
         }, noValueHandler());
     }
     protected RoutingSlipDefinition doParseRoutingSlipDefinition() throws 
IOException, XmlPullParserException {
@@ -2876,6 +2902,7 @@ public class ModelParser extends BaseParser {
             case "resequence": return doParseResequenceDefinition();
             case "rollback": return doParseRollbackDefinition();
             case "route": return doParseRouteDefinition();
+            case "routeTemplate": return doParseRouteTemplateDefinition();
             case "routingSlip": return doParseRoutingSlipDefinition();
             case "saga": return doParseSagaDefinition();
             case "sample": return doParseSamplingDefinition();

Reply via email to