[camel] branch CAMEL-14963 created (now 7406e84)

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

[camel] branch CAMEL-14963 created (now 7406e84)

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

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


      at 7406e84  CAMEL-14963: Route Template. WIP

This branch includes the following new commits:

     new 7406e84  CAMEL-14963: Route Template. WIP

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Reply | Threaded
Open this post in threaded view
|

[camel] 01/01: CAMEL-14963: Route Template. WIP

davsclaus-2
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 <[hidden email]>
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();