This is an automated email from the ASF dual-hosted git repository. nfilotto pushed a commit to branch CAMEL-17754/route-configuration-precondition in repository https://gitbox.apache.org/repos/asf/camel.git
commit 1c4279b0d9d1b670f36607a3a386d6448012cc16 Author: Nicolas Filotto <nfilo...@talend.com> AuthorDate: Mon Mar 7 18:50:19 2022 +0100 CAMEL-17754: Precondition to hard exclude route configurations --- .../camel/catalog/models/routeConfiguration.json | 1 + .../apache/camel/catalog/schemas/camel-spring.xsd | 8 +++ ...ava => RouteConfigurationPreconditionTest.java} | 25 +++----- .../apache/camel/spring/RoutePreconditionTest.java | 2 +- ...Test.properties => PreconditionTest.properties} | 0 .../spring/RouteConfigurationPreconditionTest.xml | 75 ++++++++++++++++++++++ .../apache/camel/spring/RoutePreconditionTest.xml | 2 +- .../org/apache/camel/impl/DefaultCamelContext.java | 20 +----- .../java/org/apache/camel/impl/DefaultModel.java | 12 +++- .../org/apache/camel/impl/PreconditionHelper.java | 66 +++++++++++++++++++ .../org/apache/camel/model/routeConfiguration.json | 1 + .../apache/camel/model/PreconditionContainer.java | 37 +++++++++++ .../camel/model/RouteConfigurationDefinition.java | 37 ++++++++++- .../org/apache/camel/model/RouteDefinition.java | 11 ++-- ...ava => RouteConfigurationPreconditionTest.java} | 71 +++++++++----------- .../camel/processor/RoutePreconditionTest.java | 2 +- .../java/org/apache/camel/xml/in/ModelParser.java | 9 ++- .../modules/ROOT/pages/route-configuration.adoc | 45 +++++++++++++ .../RouteConfigurationDefinitionDeserializer.java | 4 ++ .../src/generated/resources/camel-yaml-dsl.json | 3 + .../src/generated/resources/camelYamlDsl.json | 3 + .../camel/dsl/yaml/RouteConfigurationTest.groovy | 53 ++++++++++++++- 22 files changed, 400 insertions(+), 87 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routeConfiguration.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routeConfiguration.json index 29af0d6..a1b45b6 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routeConfiguration.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routeConfiguration.json @@ -17,6 +17,7 @@ "interceptSendToEndpoint": { "kind": "element", "displayName": "Intercept Send To Endpoint", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.InterceptSendToEndpointDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Applies a route for an interceptor if an exchange is send to the given endpoint" }, "onException": { "kind": "element", "displayName": "On Exception", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.OnExceptionDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Exception clause for catching certain exceptions and handling them." }, "onCompletion": { "kind": "element", "displayName": "On Completion", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.OnCompletionDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "On completion callback for doing custom routing when the org.apache.camel.Exchange is complete." }, + "precondition": { "kind": "attribute", "displayName": "Precondition", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The predicate of the precondition in simple language to evaluate in order to determine if this route configuration should be included or not." }, "id": { "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": 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, "autowired": false, "secret": false, "description": "Sets the description of this node" } } 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 6dd00d4..d06f7df 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 @@ -10612,6 +10612,14 @@ Reference to the route templates in the xml dsl. <xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:onException"/> <xs:element maxOccurs="unbounded" minOccurs="0" ref="tns:onCompletion"/> </xs:sequence> + <xs:attribute name="precondition" type="xs:string"> + <xs:annotation> + <xs:documentation xml:lang="en"><![CDATA[ +The predicate of the precondition in simple language to evaluate in order to +determine if this route configuration should be included or not. + ]]></xs:documentation> + </xs:annotation> + </xs:attribute> </xs:extension> </xs:complexContent> </xs:complexType> 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/RouteConfigurationPreconditionTest.java similarity index 60% copy from components/camel-spring-xml/src/test/java/org/apache/camel/spring/RoutePreconditionTest.java copy to components/camel-spring-xml/src/test/java/org/apache/camel/spring/RouteConfigurationPreconditionTest.java index 9d910bb..9e3e45b 100644 --- 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/RouteConfigurationPreconditionTest.java @@ -20,33 +20,28 @@ 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. + * The test ensuring that the precondition set on a route configuration determines if the route configuration is + * included or not */ -class RoutePreconditionTest extends SpringTestSupport { +class RouteConfigurationPreconditionTest extends SpringTestSupport { @Override protected AbstractXmlApplicationContext createApplicationContext() { - return new ClassPathXmlApplicationContext("org/apache/camel/spring/RoutePreconditionTest.xml"); + return new ClassPathXmlApplicationContext("org/apache/camel/spring/RouteConfigurationPreconditionTest.xml"); } @Test - void testRoutesAreIncludedOrExcludedAsExpected() throws Exception { + void testRouteConfigurationAreIncludedOrExcludedAsExpected() throws Exception { + assertCollectionSize(context.getRouteConfigurationDefinitions(), 2); 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); + getMockEndpoint("mock:error").expectedMessageCount(2); + getMockEndpoint("mock:error").expectedBodiesReceived("Activated", "Default"); - template.sendBody("direct:in", "Hello Included Route"); - template.sendBody("direct:inT", "Hello Included Templated Route"); + template.sendBody("direct:start", "Hello Activated Route Config"); + template.sendBody("direct:start2", "Hello Not Activated Route Config"); assertMockEndpointsSatisfied(); } 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 index 9d910bb..f02994e 100644 --- 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 @@ -24,7 +24,7 @@ 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. + * The test ensuring that the precondition set on a route determines if the route is included or not. */ class RoutePreconditionTest extends SpringTestSupport { 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/PreconditionTest.properties similarity index 100% rename from components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RoutePreconditionTest.properties rename to components/camel-spring-xml/src/test/resources/org/apache/camel/spring/PreconditionTest.properties diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RouteConfigurationPreconditionTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RouteConfigurationPreconditionTest.xml new file mode 100644 index 0000000..4b46716 --- /dev/null +++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/RouteConfigurationPreconditionTest.xml @@ -0,0 +1,75 @@ +<?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/PreconditionTest.properties"/> + <routeConfiguration> + <onException> + <exception>java.lang.Exception</exception> + <handled> + <constant>true</constant> + </handled> + <transform> + <constant>Default</constant> + </transform> + <to uri="mock:error"/> + </onException> + </routeConfiguration> + <routeConfiguration precondition="{{activate}}"> + <onException> + <exception>java.lang.IllegalArgumentException</exception> + <handled> + <constant>true</constant> + </handled> + <transform> + <constant>Activated</constant> + </transform> + <to uri="mock:error"/> + </onException> + </routeConfiguration> + <routeConfiguration precondition="{{!activate}}"> + <onException> + <exception>java.lang.IllegalArgumentException</exception> + <handled> + <constant>true</constant> + </handled> + <transform> + <constant>Not Activated</constant> + </transform> + <to uri="mock:error"/> + </onException> + </routeConfiguration> + + <route> + <from uri="direct:start"/> + <throwException exceptionType="java.lang.IllegalArgumentException" message="Foo"/> + </route> + <route> + <from uri="direct:start2"/> + <throwException exceptionType="java.lang.RuntimeException" message="Foo"/> + </route> + </camelContext> + +</beans> 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 index 03e6a66..45694d7 100644 --- 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 @@ -24,7 +24,7 @@ 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"/> + <propertyPlaceholder id="properties" location="classpath:org/apache/camel/spring/PreconditionTest.properties"/> <routeTemplate id="myTemplate"> <templateParameter name="activateT"/> <route precondition="{{activateT}}"> 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 8a25e37..d2b4dcb 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 @@ -65,7 +65,6 @@ 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; @@ -85,7 +84,6 @@ 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; @@ -998,23 +996,7 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame * @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; + return PreconditionHelper.included(definition, this); } private static ValueHolder<String> createTransformerKey(TransformerDefinition def) { 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 fcb834f..179cc44 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 @@ -122,7 +122,8 @@ public class DefaultModel implements Model { @Override public void addRouteConfiguration(RouteConfigurationDefinition routesConfiguration) { - if (routesConfiguration == null) { + // Ensure that the route configuration should be included + if (routesConfiguration == null || !includedRouteConfiguration(routesConfiguration)) { return; } // only add if not already exists (route-loader may let Java DSL add route configuration twice @@ -925,4 +926,13 @@ public class DefaultModel implements Model { } } + /** + * Indicates whether the route configuration should be included according to the precondition. + * + * @param definition the definition of the route configuration to check. + * @return {@code true} if the route configuration should be included, {@code false} otherwise. + */ + private boolean includedRouteConfiguration(RouteConfigurationDefinition definition) { + return PreconditionHelper.included(definition, camelContext); + } } diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/PreconditionHelper.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/PreconditionHelper.java new file mode 100644 index 0000000..a3d3ebb --- /dev/null +++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/PreconditionHelper.java @@ -0,0 +1,66 @@ +/* + * 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.impl; + +import org.apache.camel.CamelContext; +import org.apache.camel.Predicate; +import org.apache.camel.model.PreconditionContainer; +import org.apache.camel.model.language.ExpressionDefinition; +import org.apache.camel.model.language.SimpleExpression; +import org.apache.camel.support.DefaultExchange; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Helper class for preconditions. + */ +final class PreconditionHelper { + + /** + * The logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(PreconditionHelper.class); + + private PreconditionHelper() { + } + + /** + * Indicates whether the given {@link PreconditionContainer} should be included according to the precondition. + * + * @param container the container to check. + * @return {@code true} if the {@link PreconditionContainer} should be included, {@code false} otherwise. + */ + static boolean included(PreconditionContainer container, CamelContext context) { + final String precondition = container.getPrecondition(); + if (precondition == null) { + LOG.trace("No precondition found, the {} is included by default", container.getLabel()); + return true; + } + final ExpressionDefinition expression = new SimpleExpression(precondition); + expression.initPredicate(context); + + Predicate predicate = expression.getPredicate(); + predicate.initPredicate(context); + + boolean matches = predicate.matches(new DefaultExchange(context)); + if (LOG.isTraceEnabled()) { + LOG.trace("The precondition has been evaluated to {}, consequently the {} is {}", matches, container.getLabel(), + matches ? "included" : "excluded"); + } + return matches; + } +} diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfiguration.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfiguration.json index 29af0d6..a1b45b6 100644 --- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfiguration.json +++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/routeConfiguration.json @@ -17,6 +17,7 @@ "interceptSendToEndpoint": { "kind": "element", "displayName": "Intercept Send To Endpoint", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.InterceptSendToEndpointDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Applies a route for an interceptor if an exchange is send to the given endpoint" }, "onException": { "kind": "element", "displayName": "On Exception", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.OnExceptionDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "Exception clause for catching certain exceptions and handling them." }, "onCompletion": { "kind": "element", "displayName": "On Completion", "required": false, "type": "array", "javaType": "java.util.List<org.apache.camel.model.OnCompletionDefinition>", "deprecated": false, "autowired": false, "secret": false, "description": "On completion callback for doing custom routing when the org.apache.camel.Exchange is complete." }, + "precondition": { "kind": "attribute", "displayName": "Precondition", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The predicate of the precondition in simple language to evaluate in order to determine if this route configuration should be included or not." }, "id": { "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": 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, "autowired": false, "secret": false, "description": "Sets the description of this node" } } diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/PreconditionContainer.java b/core/camel-core-model/src/main/java/org/apache/camel/model/PreconditionContainer.java new file mode 100644 index 0000000..078d1a6 --- /dev/null +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/PreconditionContainer.java @@ -0,0 +1,37 @@ +/* + * 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 org.apache.camel.NamedNode; + +/** + * Container to hold a precondition to determine if it should be included or not. + */ +public interface PreconditionContainer extends NamedNode { + + /** + * The predicate of the precondition in simple language to evaluate in order to determine if it should be included + * or not. + */ + String getPrecondition(); + + /** + * The predicate of the precondition in simple language to evaluate in order to determine if it should be included + * or not. + */ + void setPrecondition(String precondition); +} diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationDefinition.java index 56df20c..1a7b65f 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteConfigurationDefinition.java @@ -22,6 +22,7 @@ import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -33,7 +34,8 @@ import org.apache.camel.spi.Metadata; @Metadata(label = "configuration") @XmlRootElement(name = "routeConfiguration") @XmlAccessorType(XmlAccessType.FIELD) -public class RouteConfigurationDefinition extends OptionalIdentifiedDefinition<RouteConfigurationDefinition> { +public class RouteConfigurationDefinition extends OptionalIdentifiedDefinition<RouteConfigurationDefinition> + implements PreconditionContainer { // TODO: Model for ErrorHandler (requires to move error handler model from spring-xml, blueprint to core) @@ -47,6 +49,9 @@ public class RouteConfigurationDefinition extends OptionalIdentifiedDefinition<R private List<OnExceptionDefinition> onExceptions = new ArrayList<>(); @XmlElement(name = "onCompletion") private List<OnCompletionDefinition> onCompletions = new ArrayList<>(); + @XmlAttribute + @Metadata(label = "advanced") + private String precondition; public RouteConfigurationDefinition() { } @@ -106,10 +111,40 @@ public class RouteConfigurationDefinition extends OptionalIdentifiedDefinition<R this.interceptSendTos = interceptSendTos; } + /** + * The predicate of the precondition in simple language to evaluate in order to determine if this route + * configuration should be included or not. + */ + @Override + public String getPrecondition() { + return precondition; + } + + /** + * The predicate of the precondition in simple language to evaluate in order to determine if this route + * configuration should be included or not. + */ + @Override + public void setPrecondition(String precondition) { + this.precondition = precondition; + } + // Fluent API // ------------------------------------------------------------------------- /** + * Sets the predicate of the precondition in simple language to evaluate in order to determine if this route + * configuration should be included or not. + * + * @param precondition the predicate corresponding to the test to evaluate. + * @return the builder + */ + public RouteConfigurationDefinition precondition(String precondition) { + setPrecondition(precondition); + return this; + } + + /** * <a href="http://camel.apache.org/exception-clause.html">Exception clause</a> for catching certain exceptions and * handling them. * 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 c639ce2..c7e2d3e 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 @@ -34,6 +34,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; +import org.apache.camel.CamelContext; import org.apache.camel.Endpoint; import org.apache.camel.ErrorHandlerFactory; import org.apache.camel.NamedRoute; @@ -57,7 +58,7 @@ import org.apache.camel.spi.RoutePolicy; @XmlType(propOrder = { "input", "inputType", "outputType", "outputs", "routeProperties" }) @XmlAccessorType(XmlAccessType.PROPERTY) // must use XmlAccessType.PROPERTY as there is some custom logic needed to be executed in the setter methods -public class RouteDefinition extends OutputDefinition<RouteDefinition> implements NamedRoute { +public class RouteDefinition extends OutputDefinition<RouteDefinition> implements NamedRoute, PreconditionContainer { private final AtomicBoolean prepared = new AtomicBoolean(); private FromDefinition input; private String routeConfigurationId; @@ -117,7 +118,7 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition> implement * Check if the route has been prepared * * @return whether the route has been prepared or not - * @see RouteDefinitionHelper#prepareRoute(ModelCamelContext, RouteDefinition) + * @see RouteDefinitionHelper#prepareRoute(CamelContext, RouteDefinition) */ public boolean isPrepared() { return prepared.get(); @@ -926,9 +927,10 @@ 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. + * The predicate of the precondition in simple language to evaluate in order to determine if this route should be + * included or not. */ + @Override public String getPrecondition() { return precondition; } @@ -939,6 +941,7 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition> implement */ @XmlAttribute @Metadata(label = "advanced") + @Override public void setPrecondition(String precondition) { this.precondition = precondition; } 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/RouteConfigurationPreconditionTest.java similarity index 51% copy from core/camel-core/src/test/java/org/apache/camel/processor/RoutePreconditionTest.java copy to core/camel-core/src/test/java/org/apache/camel/processor/RouteConfigurationPreconditionTest.java index 508fa3c..bb11a29 100644 --- a/core/camel-core/src/test/java/org/apache/camel/processor/RoutePreconditionTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/processor/RouteConfigurationPreconditionTest.java @@ -20,15 +20,14 @@ import java.util.Properties; import org.apache.camel.ContextTestSupport; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.builder.RouteConfigurationBuilder; 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 + * The test ensuring that the precondition set on a route configuration determines if the route configuration is + * included or not */ -class RoutePreconditionTest extends ContextTestSupport { +class RouteConfigurationPreconditionTest extends ContextTestSupport { @Override public boolean isUseRouteBuilder() { @@ -36,71 +35,61 @@ class RoutePreconditionTest extends ContextTestSupport { } @Test - void testRouteIncluded() throws Exception { + void testRouteConfigurationIncluded() throws Exception { Properties init = new Properties(); - init.setProperty("protocol", "json"); + init.setProperty("activate", "true"); 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")); + assertCollectionSize(context.getRouteConfigurationDefinitions(), 2); + assertCollectionSize(context.getRouteDefinitions(), 1); + assertCollectionSize(context.getRoutes(), 1); - getMockEndpoint("mock:out").expectedMessageCount(1); + getMockEndpoint("mock:error").expectedMessageCount(1); + getMockEndpoint("mock:error").expectedBodiesReceived("Activated"); - template.sendBody("direct:in", "Hello Included Route"); + template.sendBody("direct:start", "Hello Activated Route Config"); assertMockEndpointsSatisfied(); } @Test - void testRouteExcluded() throws Exception { + void testRouteConfigurationExcluded() throws Exception { Properties init = new Properties(); - init.setProperty("protocol", "avro"); + init.setProperty("activate", "false"); context.getPropertiesComponent().setInitialProperties(init); context.addRoutes(createRouteBuilder()); context.start(); + assertCollectionSize(context.getRouteConfigurationDefinitions(), 1); 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); + getMockEndpoint("mock:error").expectedMessageCount(1); + getMockEndpoint("mock:error").expectedBodiesReceived("Default"); - 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"); + template.sendBody("direct:start", "Hello Not Activated Route Config"); 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"); + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteConfigurationBuilder() { + @Override + public void configuration() { + from("direct:start") + .throwException(new IllegalArgumentException("Foo")); + routeConfiguration().precondition("{{activate}}").onException(IllegalArgumentException.class).handled(true) + .transform(constant("Activated")) + .to("mock:error"); + routeConfiguration().onException(Exception.class).handled(true) + .transform(constant("Default")) + .to("mock:error"); } }; } - } 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 index 508fa3c..f295238 100644 --- 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 @@ -26,7 +26,7 @@ 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 + * The test ensuring that the precondition set on a route determines if the route is included or not */ class RoutePreconditionTest extends ContextTestSupport { 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 bd2d248..4aaca5f 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 @@ -984,8 +984,13 @@ public class ModelParser extends BaseParser { }, noElementHandler(), noValueHandler()); } protected RouteConfigurationDefinition doParseRouteConfigurationDefinition() throws IOException, XmlPullParserException { - return doParse(new RouteConfigurationDefinition(), - optionalIdentifiedDefinitionAttributeHandler(), (def, key) -> { + return doParse(new RouteConfigurationDefinition(), (def, key, val) -> { + if ("precondition".equals(key)) { + def.setPrecondition(val); + return true; + } + return optionalIdentifiedDefinitionAttributeHandler().accept(def, key, val); + }, (def, key) -> { switch (key) { case "interceptFrom": doAdd(doParseInterceptFromDefinition(), def.getInterceptFroms(), def::setInterceptFroms); break; case "interceptSendToEndpoint": doAdd(doParseInterceptSendToEndpointDefinition(), def.getInterceptSendTos(), def::setInterceptSendTos); break; diff --git a/docs/user-manual/modules/ROOT/pages/route-configuration.adoc b/docs/user-manual/modules/ROOT/pages/route-configuration.adoc index de16d2d..2215ffb 100644 --- a/docs/user-manual/modules/ROOT/pages/route-configuration.adoc +++ b/docs/user-manual/modules/ROOT/pages/route-configuration.adoc @@ -323,6 +323,51 @@ And in Camel Main / Quarkus: camel.main.startup-summary-level = verbose ---- +== Route Precondition + +The route configurations 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 configuration is only included if the parameter `activate` has been set to `true`. + +[source,java] +---- +routeConfiguration().precondition("{{activate}}") + .onException(IllegalArgumentException.class) + .handled(true) + .log("WARN: ${exception.message}"); +---- + +And the same example using XML DSL: + +[source,xml] +---- +<routeConfiguration precondition="{{activate}}"> + <onException> + <exception>java.lang.IllegalArgumentException</exception> + <handled> + <constant>true</constant> + </handled> + <log message="XML WARN: ${exception.message}"/> + </onException> +</routeConfiguration> +---- + +And in YAML DSL: + +[source,yaml] +---- +- route-configuration: + - precondition: "{{activate}}" + - on-exception: + exception: + - "java.lang.IllegalArgumentException" + handled: + constant: "true" + steps: + - log: + message: "YAML WARN ${exception.message}" +---- + == More Information See these examples: diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteConfigurationDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteConfigurationDefinitionDeserializer.java index 1fcb898..5b99d26 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteConfigurationDefinitionDeserializer.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteConfigurationDefinitionDeserializer.java @@ -40,6 +40,7 @@ import org.snakeyaml.engine.v2.nodes.SequenceNode; nodes = { "route-configuration", "routeConfiguration" }, properties = { @YamlProperty(name = "id", type = "string"), + @YamlProperty(name = "precondition", type = "string"), @YamlProperty(name = "intercept", type = "array:org.apache.camel.model.InterceptDefinition"), @YamlProperty(name = "intercept-from", type = "array:org.apache.camel.model.InterceptFromDefinition"), @YamlProperty(name = "intercept-send-to-endpoint", @@ -75,6 +76,9 @@ public class RouteConfigurationDefinitionDeserializer extends YamlDeserializerBa target.setId(asText(val)); break; } + case "precondition": + target.setPrecondition(asText(val)); + break; case "on-exception": setDeserializationContext(val, dc); OnExceptionDefinition oed = asType(val, OnExceptionDefinition.class); 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 b96eb2e..e7ac7f4 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 @@ -2482,6 +2482,9 @@ "items" : { "$ref" : "#/items/definitions/org.apache.camel.model.OnExceptionDefinition" } + }, + "precondition" : { + "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 a7c11d0..7179ecf 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 @@ -2383,6 +2383,9 @@ "items" : { "$ref" : "#/items/definitions/org.apache.camel.model.OnExceptionDefinition" } + }, + "precondition" : { + "type" : "string" } } } ] diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteConfigurationTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteConfigurationTest.groovy index ba7e9a8..dbaabb0 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteConfigurationTest.groovy +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteConfigurationTest.groovy @@ -22,7 +22,8 @@ import org.apache.camel.dsl.yaml.support.YamlTestSupport import org.apache.camel.dsl.yaml.support.model.MyException import org.apache.camel.dsl.yaml.support.model.MyFailingProcessor import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Disabled + +import static org.apache.camel.util.PropertiesHelper.asProperties class RouteConfigurationTest extends YamlTestSupport { def "route-configuration"() { @@ -62,6 +63,56 @@ class RouteConfigurationTest extends YamlTestSupport { MockEndpoint.assertIsSatisfied(context) } + def "route-configuration-precondition"() { + setup: + context.getPropertiesComponent().setInitialProperties(asProperties("activate", "true")) + loadRoutes """ + - beans: + - name: myFailingProcessor + type: ${MyFailingProcessor.name} + - route-configuration: + - precondition: "{{!activate}}" + - on-exception: + handled: + constant: "true" + exception: + - ${MyException.name} + steps: + - transform: + constant: "Not Activated" + - to: "mock:on-exception" + - route-configuration: + - precondition: "{{activate}}" + - on-exception: + handled: + constant: "true" + exception: + - ${MyException.name} + steps: + - transform: + constant: "Activated" + - to: "mock:on-exception" + - from: + uri: "direct:start" + steps: + - process: + ref: "myFailingProcessor" + """ + + withMock('mock:on-exception') { + expectedBodiesReceived 'Activated' + } + + when: + context.start() + + withTemplate { + to('direct:start').withBody('hello').send() + } + then: + MockEndpoint.assertIsSatisfied(context) + } + def "route-configuration-separate"() { setup: // global configurations