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

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


The following commit(s) were added to refs/heads/main by this push:
     new 7acfb27  CAMEL-17555: Flag to include/exclude routes in route 
templates (#7091)
7acfb27 is described below

commit 7acfb273005770b01fae31a394ee44b13c2a8036
Author: Nicolas Filotto <essob...@users.noreply.github.com>
AuthorDate: Thu Mar 3 11:31:15 2022 +0100

    CAMEL-17555: Flag to include/exclude routes in route templates (#7091)
    
    ## Motivation
    
    To make route templates (aka kamelets) more flexible it can benefit that we 
allow to declare routes whether they should be included or excluded. This 
allows to parameterize these routes and therefore only include them if the 
template has a parameter included for this.
    
    ## Modifications
    
    * Add a new parameter called `precondition` allowing to provide a predicate 
in Simple language to evaluate in order to know if a given route should be 
excluded or not.
    * Remove the definition of the route if the `precondition` fails
---
 .../apache/camel/catalog/schemas/camel-spring.xsd  |   1 +
 .../apache/camel/spring/RoutePreconditionTest.java |  53 +++++++++++
 .../camel/spring/RoutePreconditionTest.properties  |  18 ++++
 .../apache/camel/spring/RoutePreconditionTest.xml  |  51 ++++++++++
 .../org/apache/camel/impl/DefaultCamelContext.java |  68 ++++++++++---
 .../org/apache/camel/model/RouteDefinition.java    |  31 ++++++
 .../camel/model/RouteTemplateDefinition.java       |   2 +-
 .../builder/RouteTemplatePreconditionTest.java     |  92 ++++++++++++++++++
 .../camel/processor/RoutePreconditionTest.java     | 106 +++++++++++++++++++++
 .../java/org/apache/camel/xml/in/ModelParser.java  |   1 +
 docs/user-manual/modules/ROOT/pages/routes.adoc    |  38 ++++++++
 .../deserializers/RouteDefinitionDeserializer.java |   4 +
 .../src/generated/resources/camel-yaml-dsl.json    |   3 +
 .../src/generated/resources/camelYamlDsl.json      |   3 +
 .../org/apache/camel/dsl/yaml/RoutesTest.groovy    |  27 ++++++
 15 files changed, 484 insertions(+), 14 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index b600499..c1742f1 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -10762,6 +10762,7 @@ Whether message history is enabled on this route. 
Default value: true
             ]]></xs:documentation>
           </xs:annotation>
         </xs:attribute>
+        <xs:attribute name="precondition" type="xs:string"/>
         <xs:attribute name="rest" type="xs:boolean"/>
         <xs:attribute name="routeConfigurationId" type="xs:string"/>
         <xs:attribute name="routePolicyRef" type="xs:string">
diff --git 
a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/RoutePreconditionTest.java
 
b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/RoutePreconditionTest.java
new file mode 100644
index 0000000..9d910bb
--- /dev/null
+++ 
b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/RoutePreconditionTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The test ensuring that the precondition set on a rule determines if the 
route is included or not.
+ */
+class RoutePreconditionTest extends SpringTestSupport {
+
+    @Override
+    protected AbstractXmlApplicationContext createApplicationContext() {
+        return new 
ClassPathXmlApplicationContext("org/apache/camel/spring/RoutePreconditionTest.xml");
+    }
+
+    @Test
+    void testRoutesAreIncludedOrExcludedAsExpected() throws Exception {
+        assertCollectionSize(context.getRouteDefinitions(), 2);
+        assertCollectionSize(context.getRoutes(), 2);
+        assertNotNull(context.getRoute("templatedRouteIncluded"));
+        assertNotNull(context.getRoute("routeIncluded"));
+        assertNull(context.getRoute("templatedRouteExcluded"));
+        assertNull(context.getRoute("routeExcluded"));
+
+        getMockEndpoint("mock:out").expectedMessageCount(1);
+        getMockEndpoint("mock:outT").expectedMessageCount(1);
+
+        template.sendBody("direct:in", "Hello Included Route");
+        template.sendBody("direct:inT", "Hello Included Templated Route");
+
+        assertMockEndpointsSatisfied();
+    }
+}
diff --git 
a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RoutePreconditionTest.properties
 
b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RoutePreconditionTest.properties
new file mode 100644
index 0000000..2148b1e
--- /dev/null
+++ 
b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RoutePreconditionTest.properties
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+activate=true
diff --git 
a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RoutePreconditionTest.xml
 
b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RoutePreconditionTest.xml
new file mode 100644
index 0000000..03e6a66
--- /dev/null
+++ 
b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RoutePreconditionTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+            http://camel.apache.org/schema/spring 
http://camel.apache.org/schema/spring/camel-spring.xsd
+            http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd";>
+
+    <camelContext xmlns="http://camel.apache.org/schema/spring";>
+        <propertyPlaceholder id="properties" 
location="classpath:org/apache/camel/spring/RoutePreconditionTest.properties"/>
+        <routeTemplate id="myTemplate">
+            <templateParameter name="activateT"/>
+            <route precondition="{{activateT}}">
+                <from uri="direct:inT"/>
+                <to uri="mock:outT"/>
+            </route>
+        </routeTemplate>
+        <templatedRoute routeTemplateRef="myTemplate" 
routeId="templatedRouteIncluded">
+            <parameter name="activateT" value="true"/>
+        </templatedRoute>
+        <templatedRoute routeTemplateRef="myTemplate" 
routeId="templatedRouteExcluded">
+            <parameter name="activateT" value="false"/>
+        </templatedRoute>
+        <route precondition="{{activate}}" id="routeIncluded">
+            <from uri="direct:in"/>
+            <to uri="mock:out"/>
+        </route>
+        <route precondition="{{!activate}}" id="routeExcluded">
+            <from uri="direct:in"/>
+            <to uri="mock:out"/>
+        </route>
+    </camelContext>
+
+</beans>
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 dbe23bf..8a25e37 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
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.impl;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -64,6 +65,7 @@ import org.apache.camel.model.RoutesDefinition;
 import org.apache.camel.model.TemplatedRouteDefinition;
 import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
 import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.model.language.SimpleExpression;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.rest.RestsDefinition;
 import org.apache.camel.model.transformer.TransformerDefinition;
@@ -83,6 +85,7 @@ import org.apache.camel.spi.Transformer;
 import org.apache.camel.spi.UuidGenerator;
 import org.apache.camel.spi.Validator;
 import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.DefaultExchange;
 import org.apache.camel.support.DefaultRegistry;
 import org.apache.camel.support.LocalBeanRegistry;
 import org.apache.camel.support.SimpleUuidGenerator;
@@ -796,6 +799,7 @@ public class DefaultCamelContext extends SimpleCamelContext 
implements ModelCame
         }
         try {
             RouteDefinitionHelper.forceAssignIds(getCamelContextReference(), 
routeDefinitions);
+            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);
@@ -871,28 +875,40 @@ public class DefaultCamelContext extends 
SimpleCamelContext implements ModelCame
                     // need to reset auto assigned ids, so there is no clash 
when creating routes
                     
ProcessorDefinitionHelper.resetAllAutoAssignedNodeIds(routeDefinition);
                 }
+                // Check if the route is included
+                if (includedRoute(routeDefinition)) {
+                    // must ensure route is prepared, before we can start it
+                    if (!routeDefinition.isPrepared()) {
+                        
RouteDefinitionHelper.prepareRoute(getCamelContextReference(), routeDefinition);
+                        routeDefinition.markPrepared();
+                    }
 
-                // must ensure route is prepared, before we can start it
-                if (!routeDefinition.isPrepared()) {
-                    
RouteDefinitionHelper.prepareRoute(getCamelContextReference(), routeDefinition);
-                    routeDefinition.markPrepared();
+                    StartupStepRecorder recorder
+                            = 
getCamelContextReference().adapt(ExtendedCamelContext.class).getStartupStepRecorder();
+                    StartupStep step = recorder.beginStep(Route.class, 
routeDefinition.getRouteId(), "Create Route");
+                    Route route = 
model.getModelReifierFactory().createRoute(this, routeDefinition);
+                    recorder.endStep(step);
+
+                    RouteService routeService = new RouteService(route);
+                    startRouteService(routeService, true);
+                } else {
+                    // Add the definition to the list of definitions to remove 
as the route is excluded
+                    if (routeDefinitionsToRemove == null) {
+                        routeDefinitionsToRemove = new 
ArrayList<>(routeDefinitions.size());
+                    }
+                    routeDefinitionsToRemove.add(routeDefinition);
                 }
 
-                StartupStepRecorder recorder
-                        = 
getCamelContextReference().adapt(ExtendedCamelContext.class).getStartupStepRecorder();
-                StartupStep step = recorder.beginStep(Route.class, 
routeDefinition.getRouteId(), "Create Route");
-                Route route = model.getModelReifierFactory().createRoute(this, 
routeDefinition);
-                recorder.endStep(step);
-
-                RouteService routeService = new RouteService(route);
-                startRouteService(routeService, true);
-
                 // clear local after the route is created via the reifier
                 pc.setLocalProperties(null);
                 if (localBeans != null) {
                     localBeans.setLocalBeanRepository(null);
                 }
             }
+            if (routeDefinitionsToRemove != null) {
+                // Remove all the excluded routes
+                model.removeRouteDefinitions(routeDefinitionsToRemove);
+            }
         } finally {
             if (!alreadyStartingRoutes) {
                 setStartingRoutes(false);
@@ -975,6 +991,32 @@ public class DefaultCamelContext extends 
SimpleCamelContext implements ModelCame
         return removed;
     }
 
+    /**
+     * Indicates whether the route should be included according to the 
precondition.
+     *
+     * @param  definition the definition of the route to check.
+     * @return            {@code true} if the route should be included, {@code 
false} otherwise.
+     */
+    private boolean includedRoute(RouteDefinition definition) {
+        final String precondition = definition.getPrecondition();
+        if (precondition == null) {
+            LOG.trace("No precondition found, the route is included by 
default");
+            return true;
+        }
+        final ExpressionDefinition expression = new 
SimpleExpression(precondition);
+        expression.initPredicate(this);
+
+        Predicate predicate = expression.getPredicate();
+        predicate.initPredicate(this);
+
+        boolean matches = predicate.matches(new DefaultExchange(this));
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("The precondition has been evaluated to {}, consequently 
the route is {}", matches,
+                    matches ? "included" : "excluded");
+        }
+        return matches;
+    }
+
     private static ValueHolder<String> 
createTransformerKey(TransformerDefinition def) {
         return ObjectHelper.isNotEmpty(def.getScheme())
                 ? new TransformerKey(def.getScheme())
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 2444d35..c639ce2 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
@@ -90,6 +90,7 @@ public class RouteDefinition extends 
OutputDefinition<RouteDefinition> implement
     private Map<String, Object> templateParameters;
     private RouteTemplateContext routeTemplateContext;
     private Resource resource;
+    private String precondition;
 
     public RouteDefinition() {
     }
@@ -457,6 +458,18 @@ public class RouteDefinition extends 
OutputDefinition<RouteDefinition> implement
     }
 
     /**
+     * Sets the predicate of the precondition in simple language to evaluate 
in order to determine if this route should
+     * be included or not.
+     *
+     * @param  precondition the predicate corresponding to the test to 
evaluate.
+     * @return              the builder
+     */
+    public RouteDefinition precondition(String precondition) {
+        setPrecondition(precondition);
+        return this;
+    }
+
+    /**
      * Configures the startup order for this route
      * <p/>
      * Camel will reorder routes and star them ordered by 0..N where 0 is the 
lowest number and N the highest number.
@@ -913,6 +926,24 @@ public class RouteDefinition extends 
OutputDefinition<RouteDefinition> implement
     }
 
     /**
+     * The predicate of the precondition in simple language to evaluate in 
order to determine if this given route should
+     * be included or not.
+     */
+    public String getPrecondition() {
+        return precondition;
+    }
+
+    /**
+     * The predicate of the precondition in simple language to evaluate in 
order to determine if this route should be
+     * included or not.
+     */
+    @XmlAttribute
+    @Metadata(label = "advanced")
+    public void setPrecondition(String precondition) {
+        this.precondition = precondition;
+    }
+
+    /**
      * To configure the ordering of the routes being started
      */
     public Integer getStartupOrder() {
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
index 8e69ebd..f700c69 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
@@ -417,7 +417,7 @@ public class RouteTemplateDefinition extends 
OptionalIdentifiedDefinition {
         } else {
             copy.setDescription(getDescription());
         }
-
+        copy.setPrecondition(route.getPrecondition());
         return copy;
     }
 
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePreconditionTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePreconditionTest.java
new file mode 100644
index 0000000..ba35b01
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePreconditionTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The test ensuring that the precondition set on a rule determines if the 
route is included or not
+ */
+class RouteTemplatePreconditionTest extends ContextTestSupport {
+
+    @Test
+    void testRouteIncluded() throws Exception {
+        TemplatedRouteBuilder.builder(context, "myTemplateWithPrecondition")
+                .parameter("protocol", "json")
+                .routeId("myRoute")
+                .add();
+
+        assertCollectionSize(context.getRouteDefinitions(), 1);
+        assertCollectionSize(context.getRoutes(), 1);
+        assertNotNull(context.getRoute("myRoute"));
+
+        getMockEndpoint("mock:out").expectedMessageCount(1);
+
+        template.sendBody("direct:in", "Hello Included Route");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    void testRouteExcluded() {
+        TemplatedRouteBuilder.builder(context, "myTemplateWithPrecondition")
+                .parameter("protocol", "avro")
+                .routeId("myRoute")
+                .add();
+
+        assertCollectionSize(context.getRouteDefinitions(), 0);
+        assertCollectionSize(context.getRoutes(), 0);
+        assertNull(context.getRoute("myRoute"));
+    }
+
+    @Test
+    void testRouteIncludedByDefault() throws Exception {
+        TemplatedRouteBuilder.builder(context, "myTemplateWithoutPrecondition")
+                .routeId("myRoute")
+                .add();
+
+        assertCollectionSize(context.getRouteDefinitions(), 1);
+        assertCollectionSize(context.getRoutes(), 1);
+        assertNotNull(context.getRoute("myRoute"));
+
+        getMockEndpoint("mock:out").expectedMessageCount(1);
+
+        template.sendBody("direct:in", "Hello Included Route");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                routeTemplate("myTemplateWithPrecondition")
+                        .templateParameter("protocol")
+                        .from("direct:in").precondition("'{{protocol}}' == 
'json'")
+                        .to("mock:out");
+                routeTemplate("myTemplateWithoutPrecondition")
+                        .from("direct:in")
+                        .to("mock:out");
+            }
+        };
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/RoutePreconditionTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/RoutePreconditionTest.java
new file mode 100644
index 0000000..508fa3c
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/RoutePreconditionTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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 java.util.Properties;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The test ensuring that the precondition set on a rule determines if the 
route is included or not
+ */
+class RoutePreconditionTest extends ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    void testRouteIncluded() throws Exception {
+        Properties init = new Properties();
+        init.setProperty("protocol", "json");
+        context.getPropertiesComponent().setInitialProperties(init);
+
+        context.addRoutes(createRouteBuilder());
+        context.start();
+
+        assertCollectionSize(context.getRouteDefinitions(), 2);
+        assertCollectionSize(context.getRoutes(), 2);
+        assertNotNull(context.getRoute("myRoute"));
+        assertNotNull(context.getRoute("myRouteNP"));
+
+        getMockEndpoint("mock:out").expectedMessageCount(1);
+
+        template.sendBody("direct:in", "Hello Included Route");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    void testRouteExcluded() throws Exception {
+        Properties init = new Properties();
+        init.setProperty("protocol", "avro");
+        context.getPropertiesComponent().setInitialProperties(init);
+
+        context.addRoutes(createRouteBuilder());
+        context.start();
+
+        assertCollectionSize(context.getRouteDefinitions(), 1);
+        assertCollectionSize(context.getRoutes(), 1);
+        assertNull(context.getRoute("myRoute"));
+        assertNotNull(context.getRoute("myRouteNP"));
+    }
+
+    @Test
+    void testRouteIncludedByDefault() throws Exception {
+        Properties init = new Properties();
+        init.setProperty("protocol", "foo");
+        context.getPropertiesComponent().setInitialProperties(init);
+
+        context.addRoutes(createRouteBuilder());
+        context.start();
+
+        assertCollectionSize(context.getRouteDefinitions(), 1);
+        assertCollectionSize(context.getRoutes(), 1);
+        assertNotNull(context.getRoute("myRouteNP"));
+
+        getMockEndpoint("mock:outNP").expectedMessageCount(1);
+
+        template.sendBody("direct:inNP", "Hello Included Route");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                
from("direct:in").routeId("myRoute").precondition("'{{protocol}}' == 'json'")
+                        .to("mock:out");
+                from("direct:inNP").routeId("myRouteNP")
+                        .to("mock:outNP");
+            }
+        };
+    }
+
+}
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 5b9047b..b6d537ef 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
@@ -1048,6 +1048,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 "precondition": def.setPrecondition(val); break;
                 case "routeConfigurationId": def.setRouteConfigurationId(val); 
break;
                 case "routePolicyRef": def.setRoutePolicyRef(val); break;
                 case "shutdownRoute": def.setShutdownRoute(val); break;
diff --git a/docs/user-manual/modules/ROOT/pages/routes.adoc 
b/docs/user-manual/modules/ROOT/pages/routes.adoc
index 5aca179..6adf101 100644
--- a/docs/user-manual/modules/ROOT/pages/routes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/routes.adoc
@@ -54,6 +54,44 @@ rb -> rb.from("kafka:cheese").to("jms:queue:foo");
 There is a bit more to this as the lambda route must be coded in a Java method 
that returns an instance of `LambdaRouteBuilder`.
 See more at the xref:lambda-route-builder.adoc[LambdaRouteBuilder] 
documentation.
 
+== Route Precondition
+
+The routes can be included or not according to the result of a test expressed 
in simple language that is evaluated only once during the initialization phase.
+
+In the next example, the route is only included if the parameter `format` has 
been set to `xml`.
+
+[source,java]
+----
+from("direct:in").precondition("'{{format}}' == 'xml'")
+   .unmarshal().jaxb()
+   .to("direct:out");
+----
+
+And the same example using XML DSL:
+
+[source,xml]
+----
+<route precondition="'{{format}}' == 'xml'">
+  <from uri="direct:in"/>
+  <unmarshal><jaxb/></unmarshal>
+  <to uri="direct:out"/>
+</route>
+----
+
+And in YAML DSL:
+
+[source,yaml]
+----
+- route:
+    precondition: "'{{format}}' == 'xml'"
+    from:
+      uri: "direct:in"
+      steps:
+        - unmarshal:
+            jaxb: {}
+        - to: "direct:out"
+----
+
 == More Information
 
 See xref:route-builder.adoc[RouteBuilder] and xref:dsl.adoc[DSL] for a list of 
supported languages you can use for coding Camel routes.
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
index 18fc980..8e49bb1 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
@@ -38,6 +38,7 @@ import org.snakeyaml.engine.v2.nodes.NodeTuple;
           properties = {
                   @YamlProperty(name = "id", type = "string"),
                   @YamlProperty(name = "description", type = "string"),
+                  @YamlProperty(name = "precondition", type = "string"),
                   @YamlProperty(name = "group", type = "string"),
                   @YamlProperty(name = "route-configuration-id", type = 
"string"),
                   @YamlProperty(name = "from", type = 
"object:org.apache.camel.model.FromDefinition", required = true)
@@ -70,6 +71,9 @@ public class RouteDefinitionDeserializer extends 
YamlDeserializerBase<RouteDefin
                 case "description":
                     target.setDescription(new 
DescriptionDefinition(asText(val)));
                     break;
+                case "precondition":
+                    target.setPrecondition(asText(val));
+                    break;
                 case "group":
                     target.setGroup(asText(val));
                     break;
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
index ac4fdc1..9ca79c9 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
@@ -2514,6 +2514,9 @@
           "id" : {
             "type" : "string"
           },
+          "precondition" : {
+            "type" : "string"
+          },
           "route-configuration-id" : {
             "type" : "string"
           }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
index cf4c823..f67dbf4 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
@@ -2415,6 +2415,9 @@
           "id" : {
             "type" : "string"
           },
+          "precondition" : {
+            "type" : "string"
+          },
           "routeConfigurationId" : {
             "type" : "string"
           }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
index c2d7abf..baeb051e 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
@@ -186,4 +186,31 @@ class RoutesTest extends YamlTestSupport {
             }
         }
     }
+
+    def "load route description with precondition"() {
+        when:
+        loadRoutes '''
+                - route:
+                    id: demo-route
+                    description: something cool
+                    precondition: "{{?red}}"
+                    from:
+                      uri: "direct:info"
+                      steps:
+                        - log: "message"
+            '''
+        then:
+        context.routeDefinitions.size() == 1
+
+        with(context.routeDefinitions[0], RouteDefinition) {
+            routeId == 'demo-route'
+            description.text == 'something cool'
+            input.endpointUri == 'direct:info'
+            precondition == '{{?red}}'
+
+            with (outputs[0], LogDefinition) {
+                message == 'message'
+            }
+        }
+    }
 }

Reply via email to