This is an automated email from the ASF dual-hosted git repository. davsclaus 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 23ae746 CAMEL-17468: camel-core - Filter EIP - add option to turn on exchange property with filter status 23ae746 is described below commit 23ae7466c8acdca657041ef24aa1700f809f0a69 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Tue Jan 11 15:58:52 2022 +0100 CAMEL-17468: camel-core - Filter EIP - add option to turn on exchange property with filter status --- .../main/docs/modules/eips/pages/filter-eip.adoc | 32 ++++++++++++ .../resources/org/apache/camel/model/filter.json | 1 + .../org/apache/camel/model/FilterDefinition.java | 22 ++++++++ .../apache/camel/processor/FilterProcessor.java | 12 +++++ .../org/apache/camel/reifier/FilterReifier.java | 7 ++- .../camel/processor/FilterStatusPropertyTest.java | 61 ++++++++++++++++++++++ .../java/org/apache/camel/xml/in/ModelParser.java | 9 +++- .../dsl/yaml/deserializers/ModelDeserializers.java | 6 +++ .../src/generated/resources/camel-yaml-dsl.json | 3 ++ 9 files changed, 150 insertions(+), 3 deletions(-) diff --git a/core/camel-core-engine/src/main/docs/modules/eips/pages/filter-eip.adoc b/core/camel-core-engine/src/main/docs/modules/eips/pages/filter-eip.adoc index 9e05c29..15f2941 100644 --- a/core/camel-core-engine/src/main/docs/modules/eips/pages/filter-eip.adoc +++ b/core/camel-core-engine/src/main/docs/modules/eips/pages/filter-eip.adoc @@ -116,6 +116,38 @@ of the bean as shown: </route> ---- +=== Filtering with status property + +To know whether an `Exchange` was filtered or not, then you can choose to specify a name of a property +to store on the exchange with the result (boolean), using `statusPropertyName` as shown below: + +[source,java] +---- +from("direct:start") + .filter().method(MyBean.class, "isGoldCustomer").statusPropertyName("gold") + .to("mock:gold") + .end() + .to("mock:all"); +} +---- + +And in XML: + +[source,xml] +---- +<route> + <from uri="direct:start"/> + <filter statusPropertyName="gold"> + <method type="com.foo.MyBean" method="isGoldCustomer"/> + <to uri="mock:gold"/> + </filter> + <to uri="mock:all"/> +</route> +---- + +In the example above then Camel will store an exchange property with key `gold` with the result of the filtering, +whether it was `true` or `false`. + === Filtering and stopping When using the Message Filter EIP, then it only applies to its children. diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/filter.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/filter.json index 173a586..31cefd5 100644 --- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/filter.json +++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/filter.json @@ -13,6 +13,7 @@ }, "properties": { "expression": { "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "joor", "jsonpath", "language", "method", "mvel", "ognl", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret": false, "asPredicate": true, "descrip [...] + "statusPropertyName": { "kind": "attribute", "displayName": "Status Property Name", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of exchange property to use for storing the status of the filtering. Setting this allows to know if the filter predicate evaluated as true or false." }, "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/FilterDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/FilterDefinition.java index 8f770fc..244339d 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/FilterDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/FilterDefinition.java @@ -18,6 +18,7 @@ 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.Predicate; @@ -34,6 +35,9 @@ import org.apache.camel.spi.Metadata; @XmlAccessorType(XmlAccessType.FIELD) public class FilterDefinition extends OutputExpressionNode { + @XmlAttribute + private String statusPropertyName; + public FilterDefinition() { } @@ -60,6 +64,14 @@ public class FilterDefinition extends OutputExpressionNode { return "filter[" + getExpression() + "]"; } + public String getStatusPropertyName() { + return statusPropertyName; + } + + public void setStatusPropertyName(String statusPropertyName) { + this.statusPropertyName = statusPropertyName; + } + /** * Expression to determine if the message should be filtered or not. If the expression returns an empty value or * <tt>false</tt> then the message is filtered (dropped), otherwise the message is continued being routed. @@ -69,4 +81,14 @@ public class FilterDefinition extends OutputExpressionNode { // override to include javadoc what the expression is used for super.setExpression(expression); } + + /** + * Name of exchange property to use for storing the status of the filtering. + * + * Setting this allows to know if the filter predicate evaluated as true or false. + */ + public FilterDefinition statusPropertyName(String statusPropertyName) { + this.statusPropertyName = statusPropertyName; + return this; + } } diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/FilterProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/FilterProcessor.java index ae41e5b..5d66449 100644 --- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/FilterProcessor.java +++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/FilterProcessor.java @@ -42,6 +42,7 @@ public class FilterProcessor extends DelegateAsyncProcessor implements Traceable private String routeId; private final Predicate predicate; private transient long filtered; + private String statusPropertyName; public FilterProcessor(CamelContext context, Predicate predicate, Processor processor) { super(processor); @@ -49,6 +50,14 @@ public class FilterProcessor extends DelegateAsyncProcessor implements Traceable this.predicate = predicate; } + public String getStatusPropertyName() { + return statusPropertyName; + } + + public void setStatusPropertyName(String statusPropertyName) { + this.statusPropertyName = statusPropertyName; + } + @Override protected void doInit() throws Exception { super.doInit(); @@ -61,6 +70,9 @@ public class FilterProcessor extends DelegateAsyncProcessor implements Traceable try { matches = matches(exchange); + if (statusPropertyName != null) { + exchange.setProperty(statusPropertyName, matches); + } } catch (Exception e) { exchange.setException(e); } diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/FilterReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/FilterReifier.java index 239a82b..5436386 100644 --- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/FilterReifier.java +++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/FilterReifier.java @@ -35,9 +35,14 @@ public class FilterReifier extends ExpressionReifier<FilterDefinition> { @Override protected FilterProcessor createFilterProcessor() throws Exception { + String status = parseString(definition.getStatusPropertyName()); + // filter EIP should have child outputs Processor childProcessor = this.createChildProcessor(true); - return new FilterProcessor(camelContext, createPredicate(), childProcessor); + + FilterProcessor answer = new FilterProcessor(camelContext, createPredicate(), childProcessor); + answer.setStatusPropertyName(status); + return answer; } } diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/FilterStatusPropertyTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/FilterStatusPropertyTest.java new file mode 100644 index 0000000..9b1c054 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/processor/FilterStatusPropertyTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.processor; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +public class FilterStatusPropertyTest extends ContextTestSupport { + + @Test + public void testSendMatchingMessage() throws Exception { + getMockEndpoint("mock:bar").expectedMessageCount(1); + getMockEndpoint("mock:bar").message(0).exchangeProperty("myBar").isEqualTo(true); + getMockEndpoint("mock:result").expectedMessageCount(1); + getMockEndpoint("mock:result").message(0).exchangeProperty("myBar").isEqualTo(true); + + template.sendBodyAndHeader("direct:start", "<matched/>", "foo", "bar"); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testSendNotMatchingMessage() throws Exception { + getMockEndpoint("mock:bar").expectedMessageCount(0); + getMockEndpoint("mock:result").expectedMessageCount(1); + getMockEndpoint("mock:result").message(0).exchangeProperty("myBar").isEqualTo(false); + + template.sendBodyAndHeader("direct:start", "<notMatched/>", "foo", "notMatchedHeaderValue"); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:start") + .filter(header("foo").isEqualTo("bar")).statusPropertyName("myBar") + .to("mock:bar") + .end() + .to("mock:result"); + } + }; + } + +} 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 bf04ce6..49fbc89 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 @@ -403,8 +403,13 @@ public class ModelParser extends BaseParser { return doParse(new FaultToleranceConfigurationCommon(), faultToleranceConfigurationCommonAttributeHandler(), noElementHandler(), noValueHandler()); } protected FilterDefinition doParseFilterDefinition() throws IOException, XmlPullParserException { - return doParse(new FilterDefinition(), - processorDefinitionAttributeHandler(), outputExpressionNodeElementHandler(), noValueHandler()); + return doParse(new FilterDefinition(), (def, key, val) -> { + if ("statusPropertyName".equals(key)) { + def.setStatusPropertyName(val); + return true; + } + return processorDefinitionAttributeHandler().accept(def, key, val); + }, outputExpressionNodeElementHandler(), noValueHandler()); } protected <T extends OutputExpressionNode> ElementHandler<T> outputExpressionNodeElementHandler() { return (def, key) -> { diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java index a3b978e..962193f 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java @@ -5142,6 +5142,7 @@ public final class ModelDeserializers extends YamlDeserializerSupport { @YamlProperty(name = "__extends", type = "object:org.apache.camel.model.language.ExpressionDefinition"), @YamlProperty(name = "expression", type = "object:org.apache.camel.model.language.ExpressionDefinition"), @YamlProperty(name = "inherit-error-handler", type = "boolean"), + @YamlProperty(name = "status-property-name", type = "string"), @YamlProperty(name = "steps", type = "array:org.apache.camel.model.ProcessorDefinition") } ) @@ -5169,6 +5170,11 @@ public final class ModelDeserializers extends YamlDeserializerSupport { target.setInheritErrorHandler(java.lang.Boolean.valueOf(val)); break; } + case "status-property-name": { + String val = asText(node); + target.setStatusPropertyName(val); + break; + } case "id": { String val = asText(node); target.setId(val); 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 08075c1..67eead4 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 @@ -913,6 +913,9 @@ "inherit-error-handler" : { "type" : "boolean" }, + "status-property-name" : { + "type" : "string" + }, "steps" : { "type" : "array", "items" : {