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 051b2de  CAMEL-16630: camel-core - Allow to use joor script for 
aggregation strategy pojo. CAMEL-16630: camel-core - Allow to use BiFunction 
for aggregation strategy pojo.
051b2de is described below

commit 051b2de1b243503c691eb2e831bf936bf6a355d8
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Jun 30 09:56:20 2021 +0200

    CAMEL-16630: camel-core - Allow to use joor script for aggregation strategy 
pojo.
    CAMEL-16630: camel-core - Allow to use BiFunction for aggregation strategy 
pojo.
---
 .../camel-joor/src/main/docs/joor-language.adoc    |  75 +++++---
 .../apache/camel/language/joor/JoorCompiler.java   |  12 ++
 .../camel/language/joor/JoorScriptingCompiler.java |  12 ++
 .../kamelet/KameletEipAggregateJoorTest.java       |  74 ++++++++
 .../AggregationStrategyBiFunctionAdapter.java      |  86 +++++++++
 .../org/apache/camel/reifier/AggregateReifier.java |  10 +
 .../apache/camel/reifier/ClaimCheckReifier.java    |   5 +
 .../org/apache/camel/reifier/EnrichReifier.java    |  11 ++
 .../org/apache/camel/reifier/MulticastReifier.java |  10 +
 .../apache/camel/reifier/PollEnrichReifier.java    |  11 ++
 .../apache/camel/reifier/RecipientListReifier.java |  10 +
 .../org/apache/camel/reifier/SplitReifier.java     |  10 +
 .../aggregator/AggregateBiFunctionTest.java        |  54 ++++++
 .../processor/aggregator/AggregateDslTest.java     |   5 +-
 .../processor/enricher/EnricherBiFunctionTest.java |  73 ++++++++
 .../apache/camel/util/function/TriConsumer.java    |  10 +-
 dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml          |   5 +
 .../apache/camel/dsl/yaml/RouteTemplateTest.groovy | 205 +++++++++++++--------
 18 files changed, 568 insertions(+), 110 deletions(-)

diff --git a/components/camel-joor/src/main/docs/joor-language.adoc 
b/components/camel-joor/src/main/docs/joor-language.adoc
index 4b1e30e..413ca62 100644
--- a/components/camel-joor/src/main/docs/joor-language.adoc
+++ b/components/camel-joor/src/main/docs/joor-language.adoc
@@ -12,7 +12,8 @@ 
include::{cq-version}@camel-quarkus:ROOT:partial$reference/languages/joor.adoc[o
 The jOOR language allows to use Java code in your Camel expression, with some 
limitations.
 The jOOR library integrates with the Java compiler and performs runtime 
compilation of Java code.
 
-NOTE: Java 8 is not supported. Java 11 or 14 is required.
+NOTE: Java 8 is not supported.
+Java 11 or 14 is required.
 
 
 == jOOR Options
@@ -22,14 +23,14 @@ NOTE: Java 8 is not supported. Java 11 or 14 is required.
 // language options: START
 The jOOR language supports 4 options, which are listed below.
 
-
-
 [width="100%",cols="2,1m,1m,6",options="header"]
 |===
 | Name | Default | Java Type | Description
-| preCompile | true | Boolean | Whether the expression should be pre compiled 
once during initialization phase. If this is turned off, then the expression is 
reloaded and compiled on each evaluation.
+| preCompile | true | Boolean | Whether the expression should be pre compiled 
once during initialization phase.
+If this is turned off, then the expression is reloaded and compiled on each 
evaluation.
 | resultType |  | String | Sets the class name of the result type (type from 
output)
-| singleQuotes | true | Boolean | Whether single quotes can be used as 
replacement for double quotes. This is convenient when you need to work with 
strings inside strings.
+| singleQuotes | true | Boolean | Whether single quotes can be used as 
replacement for double quotes.
+This is convenient when you need to work with strings inside strings.
 | trim | true | Boolean | Whether to trim the value to remove leading and 
trailing whitespaces and line breaks
 |===
 // language options: END
@@ -57,9 +58,11 @@ The jOOR language allows the following functions to be used 
in the script:
 | Function | Description
 | bodyAs(type) | To convert the body to the given type.
 | headerAs(name, type) | To convert the header with the name to the given type.
-| headerAs(name, defaultValue, type) | To convert the header with the name to 
the given type. If no header exists then use the given default value.
+| headerAs(name, defaultValue, type) | To convert the header with the name to 
the given type.
+If no header exists then use the given default value.
 | exchangePropertyAs(name, type) | To convert the exchange property with the 
name to the given type.
-| exchangePropertyAs(name, defaultValue, type) | To convert the exchange 
property with the name to the given type. If no exchange property exists then 
use the given default value.
+| exchangePropertyAs(name, defaultValue, type) | To convert the exchange 
property with the name to the given type.
+If no exchange property exists then use the given default value.
 | optionalBodyAs(type) | To convert the body to the given type returned 
wrapped in `java.util.Optional`.
 | optionalHeaderAs(name, type) | To convert the header with the name to the 
given type returned wrapped in `java.util.Optional`.
 | optionalExchangePropertyAs(name, type) | To convert the exchange property 
with the name to the given type returned wrapped in `java.util.Optional`.
@@ -68,19 +71,21 @@ The jOOR language allows the following functions to be used 
in the script:
 These functions are convenient for getting the message body, header or 
exchange properties as a specific Java type.
 
 Here we want to get the message body as a `com.foo.MyUser` type we can do as 
follows:
+
 [source,java]
 ----
 var user = bodyAs(com.foo.MyUser.class);
 ----
 
 You can omit _.class_ to make the function a little bit smaller:
+
 [source,java]
 ----
 var user = bodyAs(com.foo.MyUser);
 ----
 
-The type must be a fully qualified class type, but that can be inconvenient to 
type all the time. In such a situation, you can configure an import
-in the `camel-joor.properties` file as shown below:
+The type must be a fully qualified class type, but that can be inconvenient to 
type all the time.
+In such a situation, you can configure an import in the 
`camel-joor.properties` file as shown below:
 
 [source,properties]
 ----
@@ -88,6 +93,7 @@ import com.foo.MyUser;
 ----
 
 And then the function can be shortened:
+
 [source,java]
 ----
 var user = bodyAs(MyUser);
@@ -128,8 +134,7 @@ from("direct:start")
     .to("mock:result");
 ----
 
-Now this code may seem a bit magically, but what happens is that the `myEcho` 
bean is injected via a constructor,
-and then called directly in the script so its as fast as possible.
+Now this code may seem a bit magically, but what happens is that the `myEcho` 
bean is injected via a constructor, and then called directly in the script so 
its as fast as possible.
 
 Under the hod Camel jOOR generates the following source code that is compiled 
once:
 
@@ -159,8 +164,8 @@ from("direct:start")
     .to("mock:result");
 ----
 
-Notice how we declare the bean as if its a local variable via `var bean = 
#bean:myEcho`. When doing this we must use
-a different name as `myEcho` is the variable used by the dependency injection 
and therefore we use _bean_ as name in the script.
+Notice how we declare the bean as if its a local variable via `var bean = 
#bean:myEcho`.
+When doing this we must use a different name as `myEcho` is the variable used 
by the dependency injection and therefore we use _bean_ as name in the script.
 
 == Auto imports
 
@@ -240,6 +245,7 @@ from("seda:orders")
 ----
 
 And in XML DSL:
+
 [source,xml]
 ----
 <route>
@@ -253,7 +259,8 @@ And in XML DSL:
 
 == Multi statements
 
-It is possible to include multiple statements. The code below shows an example 
where the `user` header is retrieved in a first statement.
+It is possible to include multiple statements.
+The code below shows an example where the `user` header is retrieved in a 
first statement.
 And then, in a second statement we return a value whether the user is `null` 
or not.
 
 [source,java]
@@ -287,8 +294,7 @@ from("jms:incoming")
     .to("jms:orders");
 ----
 
-Here the jOOR script is externalized into the file 
`src/main/resources/orders.joor` which allows you to edit this source
-file while running the Camel application and try the changes with 
hot-reloading.
+Here the jOOR script is externalized into the file 
`src/main/resources/orders.joor` which allows you to edit this source file 
while running the Camel application and try the changes with hot-reloading.
 
 In XML DSL it's easier because you can turn off pre-compilation in the 
`<joor>` XML element:
 
@@ -303,6 +309,33 @@ In XML DSL it's easier because you can turn off 
pre-compilation in the `<joor>`
 </route>
 ----
 
+== Lambda based AggregationStrategy
+
+The jOOR language have special support for defining an 
`org.apache.camel.AggregationStrategy` as a lambda expression.
+This is useful when using EIP patterns that uses aggregation such as the 
Aggregator, Splitter, Recipient List, Enrich, and others.
+
+To use this then the jOOR language script must be in the following syntax:
+
+----
+(e1, e2) -> { }
+----
+
+Where `e1` and `e2` are the _old_ Exchange and _new_ Exchange from the 
`aggregate` method in the `AggregationStrategy`.
+The returned value is used as the aggregated message body, or use `null` to 
skip this.
+
+The lambda syntax is representing a Java util `BiFunction<Exchange, Exchange, 
Object>` type.
+
+For example to aggregate message bodies together we can do this as shown:
+
+[source,java]
+----
+(e1, e2) -> {
+  String b1 = e1.getMessage().getBody(String.class);
+  String b2 = e2.getMessage().getBody(String.class);
+  return b1 + ',' + b2;
+}
+----
+
 == Limitations
 
 The jOOR Camel language is only supported as a block of Java code that gets 
compiled into a Java class with a single method.
@@ -311,17 +344,13 @@ The code that you can write is therefore limited to a 
number of Java statements.
 The supported runtime is intended for Java standalone, Spring Boot, Camel 
Quarkus and other microservices runtimes.
 It is not supported in OSGi, Camel Karaf or any kind of Java Application 
Server runtime.
 
-jOOR does not support runtime compilation with Spring Boot using _fat jar_ 
packaging (https://github.com/jOOQ/jOOR/issues/69),
-it works with exploded classpath.
+jOOR does not support runtime compilation with Spring Boot using _fat jar_ 
packaging (https://github.com/jOOQ/jOOR/issues/69), it works with exploded 
classpath.
 
 == Dependencies
 
-To use scripting languages in your camel routes you need to add a
-dependency on *camel-joor*.
+To use scripting languages in your camel routes you need to add a dependency 
on *camel-joor*.
 
-If you use Maven you could just add the following to your `pom.xml`,
-substituting the version number for the latest and greatest release (see
-the download page for the latest versions).
+If you use Maven you could just add the following to your `pom.xml`, 
substituting the version number for the latest and greatest release (see the 
download page for the latest versions).
 
 [source,xml]
 ---------------------------------------
diff --git 
a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
 
b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
index 9d8418c..9361bfe 100644
--- 
a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
+++ 
b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorCompiler.java
@@ -124,6 +124,8 @@ public class JoorCompiler extends ServiceSupport implements 
StaticService {
 
         // trim text
         script = script.trim();
+        // special for evaluating aggregation strategy via a BiFunction
+        boolean biFunction = script.startsWith("(e1, e2) ->");
 
         script = staticHelper(script);
         script = alias(script);
@@ -137,6 +139,7 @@ public class JoorCompiler extends ServiceSupport implements 
StaticService {
         sb.append("\n");
         sb.append("import java.util.*;\n");
         sb.append("import java.util.concurrent.*;\n");
+        sb.append("import java.util.function.*;\n");
         sb.append("import java.util.stream.*;\n");
         sb.append("\n");
         sb.append("import org.apache.camel.*;\n");
@@ -182,6 +185,12 @@ public class JoorCompiler extends ServiceSupport 
implements StaticService {
         if (!script.contains("return ")) {
             sb.append("return ");
         }
+        if (biFunction) {
+            if (!sb.toString().endsWith("return ")) {
+                sb.append("return ");
+            }
+            sb.append("(BiFunction<Exchange, Exchange, Object>) ");
+        }
 
         if (singleQuotes) {
             // single quotes instead of double quotes, as its very annoying 
for string in strings
@@ -193,6 +202,9 @@ public class JoorCompiler extends ServiceSupport implements 
StaticService {
         if (!script.endsWith("}") && !script.endsWith(";")) {
             sb.append(";");
         }
+        if (biFunction && !script.endsWith(";")) {
+            sb.append(";");
+        }
         sb.append("\n");
         sb.append("    }\n");
         sb.append("}\n");
diff --git 
a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorScriptingCompiler.java
 
b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorScriptingCompiler.java
index e5c3106..4890bdf 100644
--- 
a/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorScriptingCompiler.java
+++ 
b/components/camel-joor/src/main/java/org/apache/camel/language/joor/JoorScriptingCompiler.java
@@ -106,6 +106,8 @@ public class JoorScriptingCompiler extends ServiceSupport 
implements StaticServi
 
         // trim text
         script = script.trim();
+        // special for evaluating aggregation strategy via a BiFunction
+        boolean biFunction = script.startsWith("(e1, e2) ->");
 
         Set<String> scriptImports = new LinkedHashSet<>();
         Map<String, Class> scriptBeans = new HashMap<>();
@@ -117,6 +119,7 @@ public class JoorScriptingCompiler extends ServiceSupport 
implements StaticServi
         sb.append("\n");
         sb.append("import java.util.*;\n");
         sb.append("import java.util.concurrent.*;\n");
+        sb.append("import java.util.function.*;\n");
         sb.append("import java.util.stream.*;\n");
         sb.append("\n");
         sb.append("import org.apache.camel.*;\n");
@@ -172,6 +175,12 @@ public class JoorScriptingCompiler extends ServiceSupport 
implements StaticServi
         if (!script.contains("return ")) {
             sb.append("return ");
         }
+        if (biFunction) {
+            if (!sb.toString().endsWith("return ")) {
+                sb.append("return ");
+            }
+            sb.append("(BiFunction<Exchange, Exchange, Object>) ");
+        }
 
         if (singleQuotes) {
             // single quotes instead of double quotes, as its very annoying 
for string in strings
@@ -183,6 +192,9 @@ public class JoorScriptingCompiler extends ServiceSupport 
implements StaticServi
         if (!script.endsWith("}") && !script.endsWith(";")) {
             sb.append(";");
         }
+        if (biFunction && !script.endsWith(";")) {
+            sb.append(";");
+        }
         sb.append("\n");
         sb.append("    }\n");
         sb.append("}\n");
diff --git 
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletEipAggregateJoorTest.java
 
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletEipAggregateJoorTest.java
new file mode 100644
index 0000000..893d921
--- /dev/null
+++ 
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletEipAggregateJoorTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.component.kamelet;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class KameletEipAggregateJoorTest extends CamelTestSupport {
+
+    @Test
+    public void testAggregate() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("A,B,C,D,E");
+
+        template.sendBody("direct:start", "A");
+        template.sendBody("direct:start", "B");
+        template.sendBody("direct:start", "C");
+        template.sendBody("direct:start", "D");
+        template.sendBody("direct:start", "E");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    // **********************************************
+    //
+    // test set-up
+    //
+    // **********************************************
+
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                routeTemplate("my-aggregate")
+                        .templateBean("myAgg", "joor",
+                                // for aggregation we can use a BiFunction 
that takes Exchange as input and return the aggregated response
+                                // camel-joor has special support for this if 
we use (e1, e2) -> { ... } as a lambda expression
+                                "(e1, e2) -> {" +
+                                                       " String b1 = 
e1.getMessage().getBody(String.class);" +
+                                                       " String b2 = 
e2.getMessage().getBody(String.class);" +
+                                                       " return b1 + ',' + b2; 
}")
+                        .templateParameter("count")
+                        .from("kamelet:source")
+                        .aggregate(constant(true))
+                        .completionSize("{{count}}")
+                        // use the groovy script bean for aggregation
+                        .aggregationStrategyRef("{{myAgg}}")
+                        .to("log:aggregate")
+                        .to("kamelet:sink")
+                        .end();
+
+                from("direct:start")
+                        .kamelet("my-aggregate?count=5")
+                        .to("log:info")
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/core/camel-core-processor/src/main/java/org/apache/camel/processor/aggregate/AggregationStrategyBiFunctionAdapter.java
 
b/core/camel-core-processor/src/main/java/org/apache/camel/processor/aggregate/AggregationStrategyBiFunctionAdapter.java
new file mode 100644
index 0000000..ecdef3f
--- /dev/null
+++ 
b/core/camel-core-processor/src/main/java/org/apache/camel/processor/aggregate/AggregationStrategyBiFunctionAdapter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.aggregate;
+
+import java.util.function.BiFunction;
+
+import org.apache.camel.AggregationStrategy;
+import org.apache.camel.Exchange;
+
+/**
+ * An {@link AggregationStrategy} that adapts to a {@link BiFunction}.
+ * <p/>
+ * This allows end users to use {@link BiFunction} for the aggregation logic, 
instead of having to implement the Camel
+ * API {@link AggregationStrategy}.
+ * <p/>
+ * This is supported for example by camel-joor that makes it possible to write 
a {@link BiFunction} as a lambda script
+ * that can be compiled and used by Camel.
+ */
+public class AggregationStrategyBiFunctionAdapter implements 
AggregationStrategy {
+
+    private final BiFunction<Exchange, Exchange, Object> function;
+    private boolean allowNullOldExchange;
+    private boolean allowNullNewExchange;
+
+    public AggregationStrategyBiFunctionAdapter(BiFunction<Exchange, Exchange, 
Object> function) {
+        this.function = function;
+    }
+
+    public boolean isAllowNullOldExchange() {
+        return allowNullOldExchange;
+    }
+
+    public void setAllowNullOldExchange(boolean allowNullOldExchange) {
+        this.allowNullOldExchange = allowNullOldExchange;
+    }
+
+    public boolean isAllowNullNewExchange() {
+        return allowNullNewExchange;
+    }
+
+    public void setAllowNullNewExchange(boolean allowNullNewExchange) {
+        this.allowNullNewExchange = allowNullNewExchange;
+    }
+
+    @Override
+    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
+        if (!allowNullOldExchange && oldExchange == null) {
+            return newExchange;
+        }
+        if (!allowNullNewExchange && newExchange == null) {
+            return oldExchange;
+        }
+
+        try {
+            Object out = function.apply(oldExchange, newExchange);
+            if (out != null && !(out instanceof Exchange)) {
+                if (oldExchange != null) {
+                    oldExchange.getIn().setBody(out);
+                } else {
+                    newExchange.getIn().setBody(out);
+                }
+            }
+        } catch (Exception e) {
+            if (oldExchange != null) {
+                oldExchange.setException(e);
+            } else {
+                newExchange.setException(e);
+            }
+        }
+        return oldExchange != null ? oldExchange : newExchange;
+    }
+}
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/AggregateReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/AggregateReifier.java
index 05a622f..d20ac70 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/AggregateReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/AggregateReifier.java
@@ -18,6 +18,7 @@ package org.apache.camel.reifier;
 
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.BiFunction;
 
 import org.apache.camel.AggregationStrategy;
 import org.apache.camel.AsyncProcessor;
@@ -33,6 +34,7 @@ import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.processor.aggregate.AggregateController;
 import org.apache.camel.processor.aggregate.AggregateProcessor;
 import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
+import 
org.apache.camel.processor.aggregate.AggregationStrategyBiFunctionAdapter;
 import org.apache.camel.processor.aggregate.OptimisticLockRetryPolicy;
 import org.apache.camel.spi.AggregationRepository;
 import org.apache.camel.spi.ExecutorServiceManager;
@@ -234,6 +236,14 @@ public class AggregateReifier extends 
ProcessorReifier<AggregateDefinition> {
             Object aggStrategy = lookup(definition.getStrategyRef(), 
Object.class);
             if (aggStrategy instanceof AggregationStrategy) {
                 strategy = (AggregationStrategy) aggStrategy;
+            } else if (aggStrategy instanceof BiFunction) {
+                AggregationStrategyBiFunctionAdapter adapter
+                        = new 
AggregationStrategyBiFunctionAdapter((BiFunction) aggStrategy);
+                if (definition.getStrategyMethodAllowNull() != null) {
+                    
adapter.setAllowNullNewExchange(parseBoolean(definition.getStrategyMethodAllowNull(),
 false));
+                    
adapter.setAllowNullOldExchange(parseBoolean(definition.getStrategyMethodAllowNull(),
 false));
+                }
+                strategy = adapter;
             } else if (aggStrategy != null) {
                 AggregationStrategyBeanAdapter adapter
                         = new AggregationStrategyBeanAdapter(aggStrategy, 
definition.getAggregationStrategyMethodName());
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ClaimCheckReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ClaimCheckReifier.java
index 8fb2982..6b46b38 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ClaimCheckReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ClaimCheckReifier.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.reifier;
 
+import java.util.function.BiFunction;
+
 import org.apache.camel.AggregationStrategy;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.Processor;
@@ -25,6 +27,7 @@ import org.apache.camel.model.ClaimCheckOperation;
 import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.processor.ClaimCheckProcessor;
 import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
+import 
org.apache.camel.processor.aggregate.AggregationStrategyBiFunctionAdapter;
 import org.apache.camel.support.ObjectHelper;
 
 import static org.apache.camel.util.ObjectHelper.notNull;
@@ -114,6 +117,8 @@ public class ClaimCheckReifier extends 
ProcessorReifier<ClaimCheckDefinition> {
             Object aggStrategy = lookup(ref, Object.class);
             if (aggStrategy instanceof AggregationStrategy) {
                 strategy = (AggregationStrategy) aggStrategy;
+            } else if (aggStrategy instanceof BiFunction) {
+                strategy = new 
AggregationStrategyBiFunctionAdapter((BiFunction) aggStrategy);
             } else if (aggStrategy != null) {
                 strategy = new AggregationStrategyBeanAdapter(aggStrategy, 
definition.getAggregationStrategyMethodName());
             } else {
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/EnrichReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/EnrichReifier.java
index 5e9e90e..1a3b310 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/EnrichReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/EnrichReifier.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.reifier;
 
+import java.util.function.BiFunction;
+
 import org.apache.camel.AggregationStrategy;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.Expression;
@@ -25,6 +27,7 @@ import org.apache.camel.model.EnrichDefinition;
 import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.processor.Enricher;
 import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
+import 
org.apache.camel.processor.aggregate.AggregationStrategyBiFunctionAdapter;
 
 public class EnrichReifier extends ExpressionReifier<EnrichDefinition> {
 
@@ -65,6 +68,14 @@ public class EnrichReifier extends 
ExpressionReifier<EnrichDefinition> {
             Object aggStrategy = 
lookup(definition.getAggregationStrategyRef(), Object.class);
             if (aggStrategy instanceof AggregationStrategy) {
                 strategy = (AggregationStrategy) aggStrategy;
+            } else if (aggStrategy instanceof BiFunction) {
+                AggregationStrategyBiFunctionAdapter adapter
+                        = new 
AggregationStrategyBiFunctionAdapter((BiFunction) aggStrategy);
+                if (definition.getAggregationStrategyMethodAllowNull() != 
null) {
+                    
adapter.setAllowNullNewExchange(parseBoolean(definition.getAggregationStrategyMethodAllowNull(),
 false));
+                    
adapter.setAllowNullOldExchange(parseBoolean(definition.getAggregationStrategyMethodAllowNull(),
 false));
+                }
+                strategy = adapter;
             } else if (aggStrategy != null) {
                 AggregationStrategyBeanAdapter adapter
                         = new AggregationStrategyBeanAdapter(aggStrategy, 
definition.getAggregationStrategyMethodName());
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/MulticastReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/MulticastReifier.java
index 4f99734..aa0962b 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/MulticastReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/MulticastReifier.java
@@ -19,6 +19,7 @@ package org.apache.camel.reifier;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
+import java.util.function.BiFunction;
 
 import org.apache.camel.AggregationStrategy;
 import org.apache.camel.CamelContextAware;
@@ -28,6 +29,7 @@ import org.apache.camel.model.MulticastDefinition;
 import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.processor.MulticastProcessor;
 import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
+import 
org.apache.camel.processor.aggregate.AggregationStrategyBiFunctionAdapter;
 import org.apache.camel.processor.aggregate.ShareUnitOfWorkAggregationStrategy;
 import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy;
 
@@ -88,6 +90,14 @@ public class MulticastReifier extends 
ProcessorReifier<MulticastDefinition> {
             Object aggStrategy = lookup(ref, Object.class);
             if (aggStrategy instanceof AggregationStrategy) {
                 strategy = (AggregationStrategy) aggStrategy;
+            } else if (aggStrategy instanceof BiFunction) {
+                AggregationStrategyBiFunctionAdapter adapter
+                        = new 
AggregationStrategyBiFunctionAdapter((BiFunction) aggStrategy);
+                if (definition.getStrategyMethodAllowNull() != null) {
+                    
adapter.setAllowNullNewExchange(parseBoolean(definition.getStrategyMethodAllowNull(),
 false));
+                    
adapter.setAllowNullOldExchange(parseBoolean(definition.getStrategyMethodAllowNull(),
 false));
+                }
+                strategy = adapter;
             } else if (aggStrategy != null) {
                 AggregationStrategyBeanAdapter adapter
                         = new AggregationStrategyBeanAdapter(aggStrategy, 
parseString(definition.getStrategyMethodName()));
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/PollEnrichReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/PollEnrichReifier.java
index 75c5e51..8ad9057 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/PollEnrichReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/PollEnrichReifier.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.reifier;
 
+import java.util.function.BiFunction;
+
 import org.apache.camel.AggregationStrategy;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.Exchange;
@@ -27,6 +29,7 @@ import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.model.language.ConstantExpression;
 import org.apache.camel.processor.PollEnricher;
 import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
+import 
org.apache.camel.processor.aggregate.AggregationStrategyBiFunctionAdapter;
 import org.apache.camel.support.DefaultExchange;
 
 public class PollEnrichReifier extends ProcessorReifier<PollEnrichDefinition> {
@@ -78,6 +81,14 @@ public class PollEnrichReifier extends 
ProcessorReifier<PollEnrichDefinition> {
             Object aggStrategy = lookup(ref, Object.class);
             if (aggStrategy instanceof AggregationStrategy) {
                 strategy = (AggregationStrategy) aggStrategy;
+            } else if (aggStrategy instanceof BiFunction) {
+                AggregationStrategyBiFunctionAdapter adapter
+                        = new 
AggregationStrategyBiFunctionAdapter((BiFunction) aggStrategy);
+                if (definition.getAggregationStrategyMethodName() != null) {
+                    
adapter.setAllowNullNewExchange(parseBoolean(definition.getAggregationStrategyMethodAllowNull(),
 false));
+                    
adapter.setAllowNullOldExchange(parseBoolean(definition.getAggregationStrategyMethodAllowNull(),
 false));
+                }
+                strategy = adapter;
             } else if (aggStrategy != null) {
                 AggregationStrategyBeanAdapter adapter = new 
AggregationStrategyBeanAdapter(
                         aggStrategy, 
parseString(definition.getAggregationStrategyMethodName()));
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RecipientListReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RecipientListReifier.java
index 6a6238a..2c1793f 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RecipientListReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RecipientListReifier.java
@@ -19,6 +19,7 @@ package org.apache.camel.reifier;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutorService;
+import java.util.function.BiFunction;
 
 import org.apache.camel.AggregationStrategy;
 import org.apache.camel.CamelContextAware;
@@ -30,6 +31,7 @@ import org.apache.camel.model.RecipientListDefinition;
 import org.apache.camel.processor.EvaluateExpressionProcessor;
 import org.apache.camel.processor.RecipientList;
 import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
+import 
org.apache.camel.processor.aggregate.AggregationStrategyBiFunctionAdapter;
 import org.apache.camel.processor.aggregate.ShareUnitOfWorkAggregationStrategy;
 import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy;
 
@@ -118,6 +120,14 @@ public class RecipientListReifier extends 
ProcessorReifier<RecipientListDefiniti
             Object aggStrategy = lookup(ref, Object.class);
             if (aggStrategy instanceof AggregationStrategy) {
                 strategy = (AggregationStrategy) aggStrategy;
+            } else if (aggStrategy instanceof BiFunction) {
+                AggregationStrategyBiFunctionAdapter adapter
+                        = new 
AggregationStrategyBiFunctionAdapter((BiFunction) aggStrategy);
+                if (definition.getStrategyMethodAllowNull() != null) {
+                    
adapter.setAllowNullNewExchange(parseBoolean(definition.getStrategyMethodAllowNull(),
 false));
+                    
adapter.setAllowNullOldExchange(parseBoolean(definition.getStrategyMethodAllowNull(),
 false));
+                }
+                strategy = adapter;
             } else if (aggStrategy != null) {
                 AggregationStrategyBeanAdapter adapter
                         = new AggregationStrategyBeanAdapter(aggStrategy, 
parseString(definition.getStrategyMethodName()));
diff --git 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SplitReifier.java
 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SplitReifier.java
index 71866b9..90ede30 100644
--- 
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SplitReifier.java
+++ 
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SplitReifier.java
@@ -17,6 +17,7 @@
 package org.apache.camel.reifier;
 
 import java.util.concurrent.ExecutorService;
+import java.util.function.BiFunction;
 
 import org.apache.camel.AggregationStrategy;
 import org.apache.camel.CamelContextAware;
@@ -27,6 +28,7 @@ import org.apache.camel.model.ProcessorDefinition;
 import org.apache.camel.model.SplitDefinition;
 import org.apache.camel.processor.Splitter;
 import org.apache.camel.processor.aggregate.AggregationStrategyBeanAdapter;
+import 
org.apache.camel.processor.aggregate.AggregationStrategyBiFunctionAdapter;
 import org.apache.camel.processor.aggregate.ShareUnitOfWorkAggregationStrategy;
 
 public class SplitReifier extends ExpressionReifier<SplitDefinition> {
@@ -83,6 +85,14 @@ public class SplitReifier extends 
ExpressionReifier<SplitDefinition> {
             Object aggStrategy = lookup(definition.getStrategyRef(), 
Object.class);
             if (aggStrategy instanceof AggregationStrategy) {
                 strategy = (AggregationStrategy) aggStrategy;
+            } else if (aggStrategy instanceof BiFunction) {
+                AggregationStrategyBiFunctionAdapter adapter
+                        = new 
AggregationStrategyBiFunctionAdapter((BiFunction) aggStrategy);
+                if (definition.getStrategyMethodAllowNull() != null) {
+                    
adapter.setAllowNullNewExchange(parseBoolean(definition.getStrategyMethodAllowNull(),
 false));
+                    
adapter.setAllowNullOldExchange(parseBoolean(definition.getStrategyMethodAllowNull(),
 false));
+                }
+                strategy = adapter;
             } else if (aggStrategy != null) {
                 AggregationStrategyBeanAdapter adapter
                         = new AggregationStrategyBeanAdapter(aggStrategy, 
definition.getStrategyMethodName());
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/aggregator/AggregateBiFunctionTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/aggregator/AggregateBiFunctionTest.java
new file mode 100644
index 0000000..4cc3dbf
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/aggregator/AggregateBiFunctionTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.aggregator;
+
+import java.util.function.BiFunction;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+public class AggregateBiFunctionTest extends ContextTestSupport {
+
+    private BiFunction<Exchange, Exchange, Object> myAgg
+            = (Exchange e1, Exchange e2) -> 
e1.getMessage().getBody(String.class) + "+" + 
e2.getMessage().getBody(String.class);
+
+    @Test
+    public void testBiFunction() throws Exception {
+        getMockEndpoint("mock:aggregated").expectedBodiesReceived("A+B+C");
+
+        template.sendBodyAndHeader("direct:start", "A", "id", 123);
+        template.sendBodyAndHeader("direct:start", "B", "id", 123);
+        template.sendBodyAndHeader("direct:start", "C", "id", 123);
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                context.getRegistry().bind("myAgg", myAgg);
+
+                from("direct:start")
+                        
.aggregate(header("id")).aggregationStrategyRef("myAgg").completionSize(3).to("mock:aggregated");
+            }
+        };
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/aggregator/AggregateDslTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/aggregator/AggregateDslTest.java
index 8d067f7..20b9bfa 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/processor/aggregator/AggregateDslTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/aggregator/AggregateDslTest.java
@@ -45,11 +45,12 @@ public class AggregateDslTest extends ContextTestSupport {
         return new RouteBuilder() {
             @Override
             public void configure() throws Exception {
-                from("direct:start").aggregate().message(m -> 
m.getHeader("type")).strategy()
+                from("direct:start").aggregate().message(m -> 
m.getHeader("type")).aggregationStrategy()
                         .body(String.class, 
AggregateDslTest::joinString).completion()
                         .body(String.class, s -> s.split(",").length == 
2).to("mock:aggregated");
 
-                
from("direct:start-supplier").aggregate().header("type").strategy(AggregateDslTest::joinStringStrategy)
+                from("direct:start-supplier").aggregate().header("type")
+                        
.aggregationStrategy(AggregateDslTest::joinStringStrategy)
                         .completion()
                         .body(String.class, s -> s.split(",").length == 
3).to("mock:aggregated-supplier");
             }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/processor/enricher/EnricherBiFunctionTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/processor/enricher/EnricherBiFunctionTest.java
new file mode 100644
index 0000000..2cd5646
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/processor/enricher/EnricherBiFunctionTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.enricher;
+
+import java.util.function.BiFunction;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockComponent;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spi.Registry;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class EnricherBiFunctionTest extends ContextTestSupport {
+
+    private MockEndpoint cool = new MockEndpoint("mock:cool", new 
MockComponent(context));
+
+    private BiFunction<Exchange, Exchange, Object> myAgg
+            = (Exchange e1, Exchange e2) -> 
e1.getMessage().getBody(String.class) + "+" + 
e2.getMessage().getBody(String.class);
+
+    @Override
+    protected Registry createRegistry() throws Exception {
+        Registry jndi = super.createRegistry();
+        jndi.bind("cool", cool);
+        jndi.bind("agg", myAgg);
+        return jndi;
+    }
+
+    @Test
+    public void testEnrichRef() throws Exception {
+        cool.whenAnyExchangeReceived(new Processor() {
+            public void process(Exchange exchange) throws Exception {
+                exchange.getMessage().setBody("Bye World");
+            }
+        });
+        cool.expectedBodiesReceived("Hello World");
+
+        String out = template.requestBody("direct:start", "Hello World", 
String.class);
+        assertEquals("Hello World+Bye World", out);
+
+        cool.assertIsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                cool.setCamelContext(context);
+
+                
from("direct:start").enrich().simple("ref:cool").aggregationStrategyRef("agg");
+            }
+        };
+    }
+}
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/function/TriConsumer.java 
b/core/camel-util/src/main/java/org/apache/camel/util/function/TriConsumer.java
index deb9032..5ca376a 100644
--- 
a/core/camel-util/src/main/java/org/apache/camel/util/function/TriConsumer.java
+++ 
b/core/camel-util/src/main/java/org/apache/camel/util/function/TriConsumer.java
@@ -25,13 +25,13 @@ package org.apache.camel.util.function;
  */
 @FunctionalInterface
 public interface TriConsumer<I1, I2, I3> {
+
     /**
-     * Applies this function to the given arguments..
+     * Applies this function to the given arguments.
      *
-     * @param  i1 the first argument
-     * @param  i2 the second argument
-     * @param  i3 the third argument
-     * @return    the function result
+     * @param i1 the first argument
+     * @param i2 the second argument
+     * @param i3 the third argument
      */
     void accept(I1 i1, I2 i2, I3 i3);
 }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml
index 8ba23b0..58add82 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml
@@ -141,6 +141,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
+            <artifactId>camel-joor</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
             <artifactId>camel-mustache</artifactId>
             <scope>test</scope>
         </dependency>
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteTemplateTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteTemplateTest.groovy
index a09f3d1..abc2982 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteTemplateTest.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RouteTemplateTest.groovy
@@ -29,7 +29,7 @@ import org.junit.jupiter.api.Assertions
 class RouteTemplateTest extends YamlTestSupport {
     def "create template"() {
         when:
-            loadRoutes '''
+        loadRoutes '''
                 - template:
                     id: "myTemplate"
                     from:
@@ -38,45 +38,45 @@ class RouteTemplateTest extends YamlTestSupport {
                         - log: "message"
             '''
         then:
-            context.routeTemplateDefinitions.size() == 1
+        context.routeTemplateDefinitions.size() == 1
 
-            with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) 
{
-                id == 'myTemplate'
+        with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) {
+            id == 'myTemplate'
 
-                route.input.endpointUri == 'direct:info'
-                with (route.outputs[0], LogDefinition) {
-                    message == 'message'
-                }
+            route.input.endpointUri == 'direct:info'
+            with(route.outputs[0], LogDefinition) {
+                message == 'message'
             }
+        }
     }
 
     def "create template with beans (#resource.location)"(Resource resource) {
         setup:
-            context.routesLoader.loadRoutes(resource)
+        context.routesLoader.loadRoutes(resource)
 
-            withMock('mock:result') {
-                expectedMessageCount 1
-                expectedBodiesReceived 'HELLO'
-            }
+        withMock('mock:result') {
+            expectedMessageCount 1
+            expectedBodiesReceived 'HELLO'
+        }
         when:
-            context.addRouteFromTemplate('myId', 'myTemplate', ['directName': 
'myId'])
-            context.start()
+        context.addRouteFromTemplate('myId', 'myTemplate', ['directName': 
'myId'])
+        context.start()
 
-            withTemplate {
-                to('direct:start').withBody('hello').send()
-            }
+        withTemplate {
+            to('direct:start').withBody('hello').send()
+        }
         then:
-            context.routeTemplateDefinitions.size() == 1
+        context.routeTemplateDefinitions.size() == 1
 
-            with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) 
{
-                id == 'myTemplate'
-                templateBeans.size() == 1
-            }
+        with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) {
+            id == 'myTemplate'
+            templateBeans.size() == 1
+        }
 
-            MockEndpoint.assertIsSatisfied(context)
+        MockEndpoint.assertIsSatisfied(context)
         where:
-            resource << [
-                    asResource('beans', """
+        resource << [
+                asResource('beans', """
                         - template:
                             id: "myTemplate"
                             beans:
@@ -93,7 +93,7 @@ class RouteTemplateTest extends YamlTestSupport {
                               - to: "direct:myId"
                               - to: "mock:result"
                     """),
-                    asResource('script', """
+                asResource('script', """
                         - template:
                             id: "myTemplate"
                             beans:
@@ -111,7 +111,7 @@ class RouteTemplateTest extends YamlTestSupport {
                               - to: "direct:myId"
                               - to: "mock:result"
                     """),
-                    asResource('script-bean-type', """
+                asResource('script-bean-type', """
                         - template:
                             id: "myTemplate"
                             beans:
@@ -130,7 +130,7 @@ class RouteTemplateTest extends YamlTestSupport {
                               - to: "direct:myId"
                               - to: "mock:result"
                     """),
-                    asResource('script-block', """
+                asResource('script-block', """
                         - template:
                             id: "myTemplate"
                             beans:
@@ -149,12 +149,12 @@ class RouteTemplateTest extends YamlTestSupport {
                               - to: "direct:myId"
                               - to: "mock:result"
                     """)
-            ]
+        ]
     }
 
     def "create template with bean and properties"() {
         setup:
-            loadRoutes """                
+        loadRoutes """                
                 - template:
                     id: "myTemplate"
                     beans:
@@ -174,31 +174,31 @@ class RouteTemplateTest extends YamlTestSupport {
                       - to: "mock:result"
             """
 
-            withMock('mock:result') {
-                expectedMessageCount 1
-                expectedBodiesReceived 'test-payload'
-            }
+        withMock('mock:result') {
+            expectedMessageCount 1
+            expectedBodiesReceived 'test-payload'
+        }
         when:
-            context.addRouteFromTemplate('myId', 'myTemplate', ['directName': 
'myId'])
-            context.start()
+        context.addRouteFromTemplate('myId', 'myTemplate', ['directName': 
'myId'])
+        context.start()
 
-            withTemplate {
-                to('direct:start').withBody('hello').send()
-            }
+        withTemplate {
+            to('direct:start').withBody('hello').send()
+        }
         then:
-            context.routeTemplateDefinitions.size() == 1
+        context.routeTemplateDefinitions.size() == 1
 
-            with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) 
{
-                id == 'myTemplate'
-                templateBeans.size() == 1
-            }
+        with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) {
+            id == 'myTemplate'
+            templateBeans.size() == 1
+        }
 
-            MockEndpoint.assertIsSatisfied(context)
+        MockEndpoint.assertIsSatisfied(context)
     }
 
     def "create template with bean and property"() {
         setup:
-            loadRoutes """                
+        loadRoutes """                
                 - template:
                     id: "myTemplate"
                     beans:
@@ -219,31 +219,31 @@ class RouteTemplateTest extends YamlTestSupport {
                       - to: "mock:result"
             """
 
-            withMock('mock:result') {
-                expectedMessageCount 1
-                expectedBodiesReceived 'test-payload'
-            }
+        withMock('mock:result') {
+            expectedMessageCount 1
+            expectedBodiesReceived 'test-payload'
+        }
         when:
-            context.addRouteFromTemplate('myId', 'myTemplate', ['directName': 
'myId'])
-            context.start()
+        context.addRouteFromTemplate('myId', 'myTemplate', ['directName': 
'myId'])
+        context.start()
 
-            withTemplate {
-                to('direct:start').withBody('hello').send()
-            }
+        withTemplate {
+            to('direct:start').withBody('hello').send()
+        }
         then:
-            context.routeTemplateDefinitions.size() == 1
+        context.routeTemplateDefinitions.size() == 1
 
-            with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) 
{
-                id == 'myTemplate'
-                templateBeans.size() == 1
-            }
+        with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) {
+            id == 'myTemplate'
+            templateBeans.size() == 1
+        }
 
-            MockEndpoint.assertIsSatisfied(context)
+        MockEndpoint.assertIsSatisfied(context)
     }
 
     def "create template with properties"() {
         when:
-            loadRoutes """
+        loadRoutes """
                 - template:
                     id: "myTemplate"
                     parameters:
@@ -258,24 +258,24 @@ class RouteTemplateTest extends YamlTestSupport {
                         - log: "message"
             """
         then:
-            context.routeTemplateDefinitions.size() == 1
+        context.routeTemplateDefinitions.size() == 1
 
-            with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) 
{
-                id == 'myTemplate'
-                configurer == null
-
-                templateParameters.any {
-                    it.name == 'foo' && it.defaultValue == 'myDefaultFoo' && 
it.description == 'myFooDescription'
-                }
-                templateParameters.any {
-                    it.name == 'bar' && it.defaultValue == null && 
it.description == 'myBarDescription'
-                }
-
-                route.input.endpointUri == 'direct:info'
-                with (route.outputs[0], LogDefinition) {
-                    message == 'message'
-                }
+        with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) {
+            id == 'myTemplate'
+            configurer == null
+
+            templateParameters.any {
+                it.name == 'foo' && it.defaultValue == 'myDefaultFoo' && 
it.description == 'myFooDescription'
             }
+            templateParameters.any {
+                it.name == 'bar' && it.defaultValue == null && it.description 
== 'myBarDescription'
+            }
+
+            route.input.endpointUri == 'direct:info'
+            with(route.outputs[0], LogDefinition) {
+                message == 'message'
+            }
+        }
     }
 
     def "create template with optional properties"() {
@@ -307,7 +307,7 @@ class RouteTemplateTest extends YamlTestSupport {
             }
 
             route.input.endpointUri == 'direct:{{foo}}'
-            with (route.outputs[0], ToDefinition) {
+            with(route.outputs[0], ToDefinition) {
                 uri == 'mock:result?retainFirst={{?bar}}'
             }
         }
@@ -330,4 +330,49 @@ class RouteTemplateTest extends YamlTestSupport {
         mock2.assertIsSatisfied()
     }
 
+    def "create template with joor"() {
+        setup:
+            loadRoutes """                
+                    - template:
+                        id: "myTemplate"
+                        beans:
+                          - name: "myAgg"
+                            type: "joor"
+                            script: "(e1, e2) -> { return 
e2.getMessage().getBody(); }"
+                        from:
+                          uri: "direct:route"
+                          steps:
+                            - aggregate:
+                                strategy-ref: "{{myAgg}}"
+                                completion-size: 2
+                                correlation-expression:
+                                  header: "StockSymbol"
+                                steps:  
+                                  - to: "mock:result"
+                """
+            withMock('mock:result') {
+                expectedMessageCount 2
+                expectedBodiesReceived '101', '199'
+            }
+        when:
+            context.addRouteFromTemplate('myId', 'myTemplate', [:])
+            context.start()
+
+            withTemplate {
+                to('direct:route').withBody('99').withHeader('StockSymbol', 
1).send()
+                to('direct:route').withBody('101').withHeader('StockSymbol', 
1).send()
+                to('direct:route').withBody('200').withHeader('StockSymbol', 
2).send()
+                to('direct:route').withBody('199').withHeader('StockSymbol', 
2).send()
+            }
+        then:
+            context.routeTemplateDefinitions.size() == 1
+
+            with(context.routeTemplateDefinitions[0], RouteTemplateDefinition) 
{
+                id == 'myTemplate'
+                templateBeans.size() == 1
+            }
+
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
 }

Reply via email to