You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2024/01/23 15:29:50 UTC

(camel) branch var created (now 337bb993300)

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

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


      at 337bb993300 CAMEL-19749: EIPs should make it easy to use together with variables.

This branch includes the following new commits:

     new 80784f015c6 CAMEL-19749: EIPs should make it easy to use together with variables.
     new f85e00bc80d CAMEL-19749: EIPs should make it easy to use together with variables.
     new f509b636f1c CAMEL-19749: EIPs should make it easy to use together with variables.
     new 048a7eca8f5 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 80448bfe267 CAMEL-19749: EIPs should make it easy to use together with variables.
     new a3598762425 CAMEL-19749: EIPs should make it easy to use together with variables.
     new aeb54da09ab CAMEL-19749: EIPs should make it easy to use together with variables.
     new ab4671a5728 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 7479f2567e2 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 49229c3e849 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 0b0b4b6b72a CAMEL-19749: EIPs should make it easy to use together with variables.
     new ab41934d2e4 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 6a783e47ea2 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 347016a0fc6 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 7320558aeea CAMEL-19749: EIPs should make it easy to use together with variables.
     new 0fcf3c7d984 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 75277dcbc08 CAMEL-19749: EIPs should make it easy to use together with variables.
     new a5f85f6b473 CAMEL-19749: EIPs should make it easy to use together with variables.
     new 337bb993300 CAMEL-19749: EIPs should make it easy to use together with variables.

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



(camel) 03/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f509b636f1c363e350260e49316c90a33de7dd90
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Jan 20 09:30:07 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../org/apache/camel/catalog/schemas/camel-spring.xsd          | 10 ++++++++++
 1 file changed, 10 insertions(+)

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 0a2eeab461d..868efc3a0f3 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
@@ -6092,6 +6092,16 @@ predicate evaluated as true or false.
             <xs:documentation xml:lang="en">
 <![CDATA[
 Sets the URI of the endpoint to use.
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="variable" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store a copy of the incoming message body (only body, not headers). This is handy for easy access
+to the incoming message body via variables.
 ]]>
             </xs:documentation>
           </xs:annotation>


(camel) 19/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 337bb993300fd05090a1aee9030fb45837f8ccb6
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Jan 23 14:52:06 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 docs/user-manual/modules/ROOT/pages/variables.adoc | 138 +++++++++++++++++++--
 1 file changed, 130 insertions(+), 8 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/variables.adoc b/docs/user-manual/modules/ROOT/pages/variables.adoc
index d9e1cc1bab3..31bcc331faa 100644
--- a/docs/user-manual/modules/ROOT/pages/variables.adoc
+++ b/docs/user-manual/modules/ROOT/pages/variables.adoc
@@ -172,17 +172,138 @@ camel.variable.user-template = resource:file:/etc/user.json
 
 == Using Variables with EIPs
 
-The most commonly used EIPs for sending and receiving messages (`from`, `to`, `toD`, `enrich`, `pollEnrich`, and `wireTap`) have
-first class support for using variables with the message body.
+The following commonly used EIPs for sending and receiving, and transforming messages, have
+first class support for using variables with the message body:
+
+- from
+- to
+- toD
+- enrich
+- pollEnrich
+- wireTap
+- unmarshal
+- marshal
 
 The intention is to make it more convenient and easy to _gather data_ from other systems without any ceremony to keep
 existing data by using techniques such as storing the data temporary using headers or exchange properties,
 or with the xref:components:eips:claimCheck-eip.adoc[Claim Check] EIP.
 
+=== Important concept when using variables and EIPs
+
 It is **important** to understand that the variables only use the message **body** and does not have support for anything else such
 as message headers. This is on purpose to keep it simpler and only work with the message body as the user data. If you have need
 to use variables with both message body and headers, then you can use `setVariable` and `getVariable`.
 
+The EIPs listed above have support for using variables when sending and receiving data. This is done by using the `variableSend` and `variableReceive` options
+to specify the name of the variable. When the EIP uses variables then the _data_ itself (i.e. message body) is only what is
+different from 'standard' Camel.
+
+For example given the following `Message` containing:
+
+[source,properties]
+----
+header.foo=123
+header.bar=456
+body=Hello World
+----
+
+And a remote service is called via the route below, and this service returns a new header and body:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:service")
+  .to("http:myservice")
+  .to("log:after");
+----
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:service"/>
+  <to uri="http:myservice"/>
+  <to uri="log:after"/>
+</route>
+----
+YAML::
++
+[source,yaml]
+----
+from:
+  uri: "direct:service"
+  steps:
+    - to: "http:myservice"
+    - to: "log:after"
+----
+====
+
+Then calling this route, then the `Message` is updated:
+
+[source,properties]
+----
+header.foo=123
+header.bar=456
+header.level=gold
+body=Bye World
+----
+
+However, if you use a variable (_myVar_) as the _sink_ to store the returned data from calling the remote service as shown:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:service")
+  .toV("http:myservice", null, "myVar")
+  .to("log:after");
+----
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:service"/>
+  <to uri="http:myservice" variableReceive="myVar"/>
+  <to uri="log:after"/>
+</route>
+----
+YAML::
++
+[source,yaml]
+----
+from:
+  uri: "direct:service"
+  steps:
+    - to:
+        uri: http:myservice
+        variableReceive: myVar
+    - to: "log:after"
+----
+====
+
+Then the `Message` body is not changed, but everything else is changed as without using variables:
+
+[source,properties]
+----
+header.foo=123
+header.bar=456
+header.level=gold
+body=Hello World
+----
+
+And the variable contains the data:
+
+[source,properties]
+----
+myVar=Bye World
+----
+
 === Using variable to store a copy of the incoming message body
 
 You can configure the `from` to store a copy of the message body into a variable. This makes it easy to have quick access
@@ -200,7 +321,7 @@ Java::
 fromV("direct:start", "myKey")
     .transform().simple("Bye ${body}")
     .to("mock:foo")
-    .setBody(simple("${variable:myKey}"))
+    .setBody(variable("myKey"))
     .to("mock:result");
 ----
 XML::
@@ -209,10 +330,12 @@ XML::
 ----
 <route>
   <from uri="direct:start" variableReceive="myKey"/>
-  <transform><simple>Bye ${body}</simple></transform>
+  <transform>
+    <simple>Bye ${body}</simple>
+  </transform>
   <to uri="mock:foo"/>
   <setBody>
-    <simple>${variable:myKey}</simple>
+    <variable>myKey</variable>
   </setBody>
   <to uri="mock:result"/>
 </route>
@@ -229,7 +352,7 @@ from:
         simple: "Bye ${body}"
     - to: "mock:foo"
     - setBody:
-        simple: "${variable:myKey}"
+        variable: "myKey"
     - to: "mock:result"
 ----
 ====
@@ -364,8 +487,7 @@ YAML::
         - to:
             uri: mock:after
         - setBody:
-            simple:
-              expression: "${variable:bye}"
+            variable: bye
         - to:
             uri: mock:result
 - route:


(camel) 11/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0b0b4b6b72adeffbe0fe98178b8cf5e342849147
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Jan 22 17:24:15 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../api/management/mbean/ManagedSendDynamicProcessorMBean.java |  6 ++++++
 .../camel/api/management/mbean/ManagedSendProcessorMBean.java  |  6 ++++++
 .../camel/management/mbean/ManagedSendDynamicProcessor.java    | 10 ++++++++++
 .../apache/camel/management/mbean/ManagedSendProcessor.java    | 10 ++++++++++
 4 files changed, 32 insertions(+)

diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedSendDynamicProcessorMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedSendDynamicProcessorMBean.java
index 36a0333acab..11224a5884d 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedSendDynamicProcessorMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedSendDynamicProcessorMBean.java
@@ -27,6 +27,12 @@ public interface ManagedSendDynamicProcessorMBean extends ManagedProcessorMBean,
                       mask = true)
     String getUri();
 
+    @ManagedAttribute(description = "Variable as the source for the message body to send")
+    String getVariableSend();
+
+    @ManagedAttribute(description = "Variable to store the received message body (only body, not headers)")
+    String getVariableReceive();
+
     @ManagedAttribute(description = "Message Exchange Pattern")
     String getMessageExchangePattern();
 
diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedSendProcessorMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedSendProcessorMBean.java
index 580841fe5a3..e0e31150032 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedSendProcessorMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedSendProcessorMBean.java
@@ -26,6 +26,12 @@ public interface ManagedSendProcessorMBean extends ManagedProcessorMBean, Manage
     @ManagedAttribute(description = "Destination as Endpoint URI", mask = true)
     String getDestination();
 
+    @ManagedAttribute(description = "Variable as the source for the message body to send")
+    String getVariableSend();
+
+    @ManagedAttribute(description = "Variable to store the received message body (only body, not headers)")
+    String getVariableReceive();
+
     @ManagedAttribute(description = "Message Exchange Pattern")
     String getMessageExchangePattern();
 
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedSendDynamicProcessor.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedSendDynamicProcessor.java
index dbe317f7ba0..f3cd46cb9e9 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedSendDynamicProcessor.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedSendDynamicProcessor.java
@@ -81,6 +81,16 @@ public class ManagedSendDynamicProcessor extends ManagedProcessor implements Man
         return uri;
     }
 
+    @Override
+    public String getVariableSend() {
+        return processor.getVariableSend();
+    }
+
+    @Override
+    public String getVariableReceive() {
+        return processor.getVariableReceive();
+    }
+
     @Override
     public String getMessageExchangePattern() {
         if (processor.getPattern() != null) {
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedSendProcessor.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedSendProcessor.java
index 51706e5a44d..467805c46cf 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedSendProcessor.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedSendProcessor.java
@@ -74,6 +74,16 @@ public class ManagedSendProcessor extends ManagedProcessor implements ManagedSen
         return destination;
     }
 
+    @Override
+    public String getVariableSend() {
+        return processor.getVariableSend();
+    }
+
+    @Override
+    public String getVariableReceive() {
+        return processor.getVariableReceive();
+    }
+
     @Override
     public String getMessageExchangePattern() {
         if (processor.getPattern() != null) {


(camel) 04/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 048a7eca8f5ab5af1f61806fdae422dd1c2fafc8
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Jan 20 10:03:40 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../camel/spring/processor/FromVariableTest.xml    |  2 +-
 .../resources/org/apache/camel/model/from.json     |  2 +-
 .../org/apache/camel/model/FromDefinition.java     | 15 ++++++------
 .../org/apache/camel/model/RouteDefinition.java    | 28 +++++++++++-----------
 .../org/apache/camel/model/RoutesDefinition.java   | 28 +++++++++++-----------
 .../org/apache/camel/reifier/RouteReifier.java     |  2 +-
 .../java/org/apache/camel/xml/in/ModelParser.java  |  2 +-
 .../java/org/apache/camel/xml/out/ModelWriter.java |  2 +-
 .../org/apache/camel/yaml/out/ModelWriter.java     |  2 +-
 .../deserializers/FromDefinitionDeserializer.java  | 12 +++++-----
 .../OutputAwareFromDefinitionDeserializer.java     | 12 +++++-----
 .../generated/resources/schema/camelYamlDsl.json   |  6 ++---
 .../apache/camel/dsl/yaml/FromVariableTest.groovy  |  2 +-
 13 files changed, 57 insertions(+), 58 deletions(-)

diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/FromVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/FromVariableTest.xml
index 00e697e7f59..1f578ec457d 100644
--- a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/FromVariableTest.xml
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/FromVariableTest.xml
@@ -28,7 +28,7 @@
   <camelContext xmlns="http://camel.apache.org/schema/spring">
     <jmxAgent id="jmx" disabled="true"/>
     <route>
-      <from uri="direct:start" variable="myKey"/>
+      <from uri="direct:start" variableReceive="myKey"/>
       <transform><simple>Bye ${body}</simple></transform>
       <to uri="mock:foo"/>
       <setBody>
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/from.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/from.json
index b753ab0fa4f..8eee8ec59c0 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/from.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/from.json
@@ -15,6 +15,6 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "uri": { "index": 2, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the URI of the endpoint to use" },
-    "variable": { "index": 3, "kind": "attribute", "displayName": "Variable", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store a copy of the incoming message body (only body, not headers). This is handy for easy access to the incoming message body via variables." }
+    "variableReceive": { "index": 3, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store a copy of the received message body (only body, not headers). This is handy for easy access to the received message body via variables." }
   }
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
index a0b29da52ee..c23893241cf 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
@@ -46,7 +46,7 @@ public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition>
     @Metadata(required = true)
     private String uri;
     @XmlAttribute
-    private String variable;
+    private String variableReceive;
 
     public FromDefinition() {
     }
@@ -121,17 +121,16 @@ public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition>
         this.uri = uri;
     }
 
-    public String getVariable() {
-        return variable;
+    public String getVariableReceive() {
+        return variableReceive;
     }
 
     /**
-     * To use a variable to store a copy of the incoming message body (only body, not headers).
-     *
-     * This is handy for easy access to the incoming message body via variables.
+     * To use a variable to store a copy of the received message body (only body, not headers).
+     * This is handy for easy access to the received message body via variables.
      */
-    public void setVariable(String variable) {
-        this.variable = variable;
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
     }
 
     /**
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 dc4fc72c2cd..c62560e9259 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
@@ -218,31 +218,31 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition>
     }
 
     /**
-     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not headers).
-     * This is handy for easy access to the incoming message body via variables.
+     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not headers).
+     * This is handy for easy access to the received message body via variables.
      *
-     * @param  uri the from uri
-     * @param variable the name of the variable
-     * @return     the builder
+     * @param  uri            the from uri
+     * @param variableReceive the name of the variable
+     * @return                the builder
      */
-    public RouteDefinition fromV(@AsEndpointUri String uri, String variable) {
+    public RouteDefinition fromV(@AsEndpointUri String uri, String variableReceive) {
         FromDefinition from = new FromDefinition(uri);
-        from.setVariable(variable);
+        from.setVariableReceive(variableReceive);
         setInput(from);
         return this;
     }
 
     /**
-     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not headers).
-     * This is handy for easy access to the incoming message body via variables.
+     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not headers).
+     * This is handy for easy access to the received message body via variables.
      *
-     * @param  endpoint the from endpoint
-     * @param variable the name of the variable
-     * @return          the builder
+     * @param  endpoint       the from endpoint
+     * @param variableReceive the name of the variable
+     * @return                the builder
      */
-    public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variable) {
+    public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variableReceive) {
         FromDefinition from = new FromDefinition(endpoint);
-        from.setVariable(variable);
+        from.setVariableReceive(variableReceive);
         setInput(from);
         return this;
     }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
index d6dcb4832c0..81b013e4d0b 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
@@ -197,16 +197,16 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
     }
 
     /**
-     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not headers).
-     * This is handy for easy access to the incoming message body via variables.
+     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not headers).
+     * This is handy for easy access to the received message body via variables.
      *
-     * @param uri the from uri
-     * @param variable the name of the variable
-     * @return     the builder
+     * @param uri             the from uri
+     * @param variableReceive the name of the variable
+     * @return                the builder
      */
-    public RouteDefinition fromV(@AsEndpointUri String uri, String variable) {
+    public RouteDefinition fromV(@AsEndpointUri String uri, String variableReceive) {
         RouteDefinition route = createRoute();
-        route.fromV(uri, variable);
+        route.fromV(uri, variableReceive);
         return route(route);
     }
 
@@ -235,16 +235,16 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
     }
 
     /**
-     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not headers).
-     * This is handy for easy access to the incoming message body via variables.
+     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not headers).
+     * This is handy for easy access to the received message body via variables.
      *
-     * @param  endpoint the from endpoint
-     * @param variable the name of the variable
-     * @return          the builder
+     * @param  endpoint       the from endpoint
+     * @param variableReceive the name of the variable
+     * @return                the builder
      */
-    public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variable) {
+    public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variableReceive) {
         RouteDefinition route = createRoute();
-        route.fromV(endpoint, variable);
+        route.fromV(endpoint, variableReceive);
         return route(route);
     }
 
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
index 8d1088b5e11..025497ace88 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
@@ -110,7 +110,7 @@ public class RouteReifier extends ProcessorReifier<RouteDefinition> {
         route.setErrorHandlerFactory(definition.getErrorHandlerFactory());
 
         // configure variable
-        String variable = definition.getInput().getVariable();
+        String variable = definition.getInput().getVariableReceive();
         if (variable != null) {
             // when using variable we need to turn on original message
             route.setAllowUseOriginalMessage(true);
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 61c0e7c0174..f542022a12f 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
@@ -478,7 +478,7 @@ public class ModelParser extends BaseParser {
         return doParse(new FromDefinition(), (def, key, val) -> {
             switch (key) {
                 case "uri": def.setUri(val); break;
-                case "variable": def.setVariable(val); break;
+                case "variableReceive": def.setVariableReceive(val); break;
                 default: return optionalIdentifiedDefinitionAttributeHandler().accept(def, key, val);
             }
             return true;
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 023547e5bd8..fd48d046a6c 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -1365,7 +1365,7 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteOptionalIdentifiedDefinitionAttributes(def);
-        doWriteAttribute("variable", def.getVariable());
+        doWriteAttribute("variableReceive", def.getVariableReceive());
         doWriteAttribute("uri", def.getUri());
         endElement(name);
     }
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index 4daf3bd618d..0f2cf3ae648 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -1365,7 +1365,7 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteOptionalIdentifiedDefinitionAttributes(def);
-        doWriteAttribute("variable", def.getVariable());
+        doWriteAttribute("variableReceive", def.getVariableReceive());
         doWriteAttribute("uri", def.getUri());
         endElement(name);
     }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/FromDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/FromDefinitionDeserializer.java
index 041eb98795a..ece57eeff55 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/FromDefinitionDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/FromDefinitionDeserializer.java
@@ -38,7 +38,7 @@ import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.getDeseri
           order = YamlDeserializerResolver.ORDER_DEFAULT,
           properties = {
                   @YamlProperty(name = "uri", type = "string", required = true),
-                  @YamlProperty(name = "variable", type = "string"),
+                  @YamlProperty(name = "variableReceive", type = "string"),
                   @YamlProperty(name = "id", type = "string"),
                   @YamlProperty(name = "description", type = "string"),
                   @YamlProperty(name = "parameters", type = "object"),
@@ -55,7 +55,7 @@ public class FromDefinitionDeserializer implements ConstructNode {
 
         String desc = null;
         String id = null;
-        String variable = null;
+        String variableReceive = null;
         if (node.getNodeType() == NodeType.MAPPING) {
             final MappingNode mn = (MappingNode) node;
             for (NodeTuple tuple : mn.getValue()) {
@@ -69,8 +69,8 @@ public class FromDefinitionDeserializer implements ConstructNode {
                     desc = asText(tuple.getValueNode());
                 } else if ("id".equals(key)) {
                     id = asText(tuple.getValueNode());
-                } else if ("variable".equals(key)) {
-                    variable = asText(tuple.getValueNode());
+                } else if ("variableReceive".equals(key)) {
+                    variableReceive = asText(tuple.getValueNode());
                 }
             }
         }
@@ -95,8 +95,8 @@ public class FromDefinitionDeserializer implements ConstructNode {
         if (id != null) {
             target.setId(id);
         }
-        if (variable != null) {
-            target.setVariable(variable);
+        if (variableReceive != null) {
+            target.setVariableReceive(variableReceive);
         }
 
         // enrich model with line number
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/OutputAwareFromDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/OutputAwareFromDefinitionDeserializer.java
index b932c448e10..82e4388e3dd 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/OutputAwareFromDefinitionDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/OutputAwareFromDefinitionDeserializer.java
@@ -36,7 +36,7 @@ import org.snakeyaml.engine.v2.nodes.NodeTuple;
           order = YamlDeserializerResolver.ORDER_DEFAULT,
           properties = {
                   @YamlProperty(name = "uri", type = "string", required = true),
-                  @YamlProperty(name = "variable", type = "string"),
+                  @YamlProperty(name = "variableReceive", type = "string"),
                   @YamlProperty(name = "id", type = "string"),
                   @YamlProperty(name = "description", type = "string"),
                   @YamlProperty(name = "parameters", type = "object"),
@@ -70,7 +70,7 @@ public class OutputAwareFromDefinitionDeserializer extends YamlDeserializerBase<
         String uri = null;
         String id = null;
         String desc = null;
-        String variable = null;
+        String variableReceive = null;
         Map<String, Object> parameters = null;
 
         for (NodeTuple tuple : node.getValue()) {
@@ -90,8 +90,8 @@ public class OutputAwareFromDefinitionDeserializer extends YamlDeserializerBase<
                 case "uri":
                     uri = asText(val);
                     break;
-                case "variable":
-                    variable = asText(val);
+                case "variableReceive":
+                    variableReceive = asText(val);
                     break;
                 case "parameters":
                     parameters = parseParameters(tuple);
@@ -119,8 +119,8 @@ public class OutputAwareFromDefinitionDeserializer extends YamlDeserializerBase<
             if (desc != null) {
                 from.setDescription(desc);
             }
-            if (variable != null) {
-                from.setVariable(variable);
+            if (variableReceive != null) {
+                from.setVariableReceive(variableReceive);
             }
             target.setDelegate(from);
         }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index e4c6f9e3fdc..6964a70e2b1 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -314,7 +314,7 @@
           "uri" : {
             "type" : "string"
           },
-          "variable" : {
+          "variableReceive" : {
             "type" : "string"
           }
         },
@@ -343,7 +343,7 @@
           "uri" : {
             "type" : "string"
           },
-          "variable" : {
+          "variableReceive" : {
             "type" : "string"
           }
         },
@@ -1968,7 +1968,7 @@
           "uri" : {
             "type" : "string"
           },
-          "variable" : {
+          "variableReceive" : {
             "type" : "string"
           }
         },
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/FromVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/FromVariableTest.groovy
index 08eecb16ad2..7316a4f874a 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/FromVariableTest.groovy
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/FromVariableTest.groovy
@@ -26,7 +26,7 @@ class FromVariableTest extends YamlTestSupport {
             loadRoutes '''
                 - from:
                     uri: "direct:start"
-                    variable: "myKey"
+                    variableReceive: "myKey"
                     steps:
                       - transform:
                           simple: "Bye ${body}"


(camel) 10/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 49229c3e8494832120274d2cb750d4cf1e201d73
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Jan 22 17:16:54 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../org/apache/camel/catalog/models/toD.json       |  12 +-
 .../org/apache/camel/catalog/models/wireTap.json   |  12 +-
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  23 ++
 .../processor/SpringToDynamicVariableTest.java     |  29 +++
 .../spring/processor/ToDynamicVariableTest.xml     |  66 ++++++
 .../org/apache/camel/model/WireTapDefinition.java  |   2 +
 docs/user-manual/modules/ROOT/pages/variables.adoc |   2 +-
 .../dsl/yaml/deserializers/ModelDeserializers.java |  28 ++-
 .../generated/resources/schema/camelYamlDsl.json   |  20 ++
 .../camel/dsl/yaml/ToVariableDynamicTest.groovy    | 255 +++++++++++++++++++++
 10 files changed, 436 insertions(+), 13 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/toD.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/toD.json
index d914a72f257..2f1ff5355c4 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/toD.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/toD.json
@@ -16,10 +16,12 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "uri": { "index": 3, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression." },
-    "pattern": { "index": 4, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" },
-    "cacheSize": { "index": 5, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this recipient list, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can [...]
-    "ignoreInvalidEndpoint": { "index": 6, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to ignore invalid endpoint URIs and skip sending the message." },
-    "allowOptimisedComponents": { "index": 7, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise toD if they are org.apache.camel.spi.SendDynamicAware ." },
-    "autoStartComponents": { "index": 8, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when toD is starting up." }
+    "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this  [...]
+    "pattern": { "index": 6, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" },
+    "cacheSize": { "index": 7, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this recipient list, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can [...]
+    "ignoreInvalidEndpoint": { "index": 8, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to ignore invalid endpoint URIs and skip sending the message." },
+    "allowOptimisedComponents": { "index": 9, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise toD if they are org.apache.camel.spi.SendDynamicAware ." },
+    "autoStartComponents": { "index": 10, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when toD is starting up." }
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/wireTap.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/wireTap.json
index 46d3a6d7432..3681587d368 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/wireTap.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/wireTap.json
@@ -20,10 +20,12 @@
     "onPrepare": { "index": 5, "kind": "attribute", "displayName": "On Prepare", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.Processor", "deprecated": false, "autowired": false, "secret": false, "description": "Uses the Processor when preparing the org.apache.camel.Exchange to be sent. This can be used to deep-clone messages that should be sent, or any custom logic needed before the exchange is sent." },
     "executorService": { "index": 6, "kind": "attribute", "displayName": "Executor Service", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "Uses a custom thread pool" },
     "uri": { "index": 7, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression." },
-    "pattern": { "index": 8, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" },
-    "cacheSize": { "index": 9, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this recipient list, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can [...]
-    "ignoreInvalidEndpoint": { "index": 10, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to ignore invalid endpoint URIs and skip sending the message." },
-    "allowOptimisedComponents": { "index": 11, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise toD if they are org.apache.camel.spi.SendDynamicAware ." },
-    "autoStartComponents": { "index": 12, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when toD is starting up." }
+    "variableSend": { "index": 8, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 9, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this  [...]
+    "pattern": { "index": 10, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" },
+    "cacheSize": { "index": 11, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this recipient list, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache ca [...]
+    "ignoreInvalidEndpoint": { "index": 12, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to ignore invalid endpoint URIs and skip sending the message." },
+    "allowOptimisedComponents": { "index": 13, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise toD if they are org.apache.camel.spi.SendDynamicAware ." },
+    "autoStartComponents": { "index": 14, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when toD is starting up." }
   }
 }
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 a465a963fc8..9360b3518d2 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
@@ -13569,6 +13569,29 @@ Sets the optional ExchangePattern used to invoke this endpoint.
 <![CDATA[
 The uri of the endpoint to send to. The uri can be dynamic computed using the
 org.apache.camel.language.simple.SimpleLanguage expression.
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="variableSend" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="variableReceive" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to
+easily control what data to use for sending and receiving. Important: When using send variable then the message body is
+taken from this variable instead of the current Message , however the headers from the Message will still be used as
+well. In other words, the variable is used instead of the message body, but everything else is as usual.
 ]]>
             </xs:documentation>
           </xs:annotation>
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringToDynamicVariableTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringToDynamicVariableTest.java
new file mode 100644
index 00000000000..1c834c5c283
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringToDynamicVariableTest.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.ToDynamicVariableTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringToDynamicVariableTest extends ToDynamicVariableTest {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/ToDynamicVariableTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToDynamicVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToDynamicVariableTest.xml
new file mode 100644
index 00000000000..69cb6510342
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToDynamicVariableTest.xml
@@ -0,0 +1,66 @@
+<?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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <!-- START SNIPPET: example -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+    <route>
+      <from uri="direct:send"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <toD uri="direct:${header.where}" variableSend="hello"/>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:receive"/>
+      <toD uri="direct:${header.where}" variableReceive="bye"/>
+      <to uri="mock:after"/>
+      <setBody>
+        <simple>${variable:bye}</simple>
+      </setBody>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:sendAndReceive"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <toD uri="direct:${header.where}" variableSend="hello" variableReceive="bye"/>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:foo"/>
+      <transform>
+        <simple>Bye ${body}</simple>
+      </transform>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: example -->
+
+</beans>
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/WireTapDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/WireTapDefinition.java
index 547f965b14b..5ea132c8068 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/WireTapDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/WireTapDefinition.java
@@ -38,6 +38,8 @@ import org.apache.camel.spi.Metadata;
 public class WireTapDefinition<Type extends ProcessorDefinition<Type>> extends ToDynamicDefinition
         implements ExecutorServiceAwareDefinition<WireTapDefinition<Type>> {
 
+    // TODO: variables due to extending ToDynamic
+
     @XmlTransient
     private ExecutorService executorServiceBean;
     @XmlTransient
diff --git a/docs/user-manual/modules/ROOT/pages/variables.adoc b/docs/user-manual/modules/ROOT/pages/variables.adoc
index da4ec933221..d9e1cc1bab3 100644
--- a/docs/user-manual/modules/ROOT/pages/variables.adoc
+++ b/docs/user-manual/modules/ROOT/pages/variables.adoc
@@ -172,7 +172,7 @@ camel.variable.user-template = resource:file:/etc/user.json
 
 == Using Variables with EIPs
 
-The most commonly used EIPs for sending and receiving messages (`from`, `to`, `toD`, `enrich` and `pollEnrich`) have
+The most commonly used EIPs for sending and receiving messages (`from`, `to`, `toD`, `enrich`, `pollEnrich`, and `wireTap`) have
 first class support for using variables with the message body.
 
 The intention is to make it more convenient and easy to _gather data_ from other systems without any ceremony to keep
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 cab2a31da16..d0bf6091766 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
@@ -18151,7 +18151,9 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "inheritErrorHandler", type = "boolean"),
                     @YamlProperty(name = "parameters", type = "object"),
                     @YamlProperty(name = "pattern", type = "enum:InOnly,InOut", description = "Sets the optional ExchangePattern used to invoke this endpoint", displayName = "Pattern"),
-                    @YamlProperty(name = "uri", type = "string", required = true, description = "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression.", displayName = "Uri")
+                    @YamlProperty(name = "uri", type = "string", required = true, description = "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression.", displayName = "Uri"),
+                    @YamlProperty(name = "variableReceive", type = "string", description = "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this variable instead of the current Message , however the headers from the Message will still be used as well. In other words, the variable is used  [...]
+                    @YamlProperty(name = "variableSend", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Send")
             }
     )
     public static class ToDynamicDefinitionDeserializer extends YamlDeserializerEndpointAwareBase<ToDynamicDefinition> {
@@ -18220,6 +18222,16 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     target.setUri(val);
                     break;
                 }
+                case "variableReceive": {
+                    String val = asText(node);
+                    target.setVariableReceive(val);
+                    break;
+                }
+                case "variableSend": {
+                    String val = asText(node);
+                    target.setVariableSend(val);
+                    break;
+                }
                 case "id": {
                     String val = asText(node);
                     target.setId(val);
@@ -19929,7 +19941,9 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "onPrepare", type = "string", description = "Uses the Processor when preparing the org.apache.camel.Exchange to be sent. This can be used to deep-clone messages that should be sent, or any custom logic needed before the exchange is sent.", displayName = "On Prepare"),
                     @YamlProperty(name = "parameters", type = "object"),
                     @YamlProperty(name = "pattern", type = "enum:InOnly,InOut", description = "Sets the optional ExchangePattern used to invoke this endpoint", displayName = "Pattern"),
-                    @YamlProperty(name = "uri", type = "string", required = true, description = "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression.", displayName = "Uri")
+                    @YamlProperty(name = "uri", type = "string", required = true, description = "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression.", displayName = "Uri"),
+                    @YamlProperty(name = "variableReceive", type = "string", description = "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this variable instead of the current Message , however the headers from the Message will still be used as well. In other words, the variable is used  [...]
+                    @YamlProperty(name = "variableSend", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Send")
             }
     )
     public static class WireTapDefinitionDeserializer extends YamlDeserializerEndpointAwareBase<WireTapDefinition> {
@@ -20013,6 +20027,16 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     target.setUri(val);
                     break;
                 }
+                case "variableReceive": {
+                    String val = asText(node);
+                    target.setVariableReceive(val);
+                    break;
+                }
+                case "variableSend": {
+                    String val = asText(node);
+                    target.setVariableSend(val);
+                    break;
+                }
                 case "id": {
                     String val = asText(node);
                     target.setId(val);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 6089a141ca4..4f7c733fd1c 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -6921,6 +6921,16 @@
               "type" : "string",
               "title" : "Uri",
               "description" : "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression."
+            },
+            "variableReceive" : {
+              "type" : "string",
+              "title" : "Variable Receive",
+              "description" : "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this variable instead of the current Message , however the headers from the Message will still be used as well. In other words, the variable is used instead of the message body, but everything else is as usual."
+            },
+            "variableSend" : {
+              "type" : "string",
+              "title" : "Variable Send",
+              "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
             }
           }
         } ],
@@ -8059,6 +8069,16 @@
             "type" : "string",
             "title" : "Uri",
             "description" : "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression."
+          },
+          "variableReceive" : {
+            "type" : "string",
+            "title" : "Variable Receive",
+            "description" : "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this variable instead of the current Message , however the headers from the Message will still be used as well. In other words, the variable is used instead of the message body, but everything else is as usual."
+          },
+          "variableSend" : {
+            "type" : "string",
+            "title" : "Variable Send",
+            "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
           }
         },
         "required" : [ "uri" ]
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableDynamicTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableDynamicTest.groovy
new file mode 100644
index 00000000000..1112010f449
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableDynamicTest.groovy
@@ -0,0 +1,255 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+
+class ToVariableDynamicTest extends YamlTestSupport {
+
+    def "toDVariable send"() {
+        setup:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - toD:
+                            uri: direct:${header.where}
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - toD:
+                            uri: direct:${header.where}
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - toD:
+                            uri: direct:${header.where}
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+            withMock('mock:before') {
+                expectedBodiesReceived 'World'
+                expectedVariableReceived("hello", "Camel")
+            }
+            withMock('mock:result') {
+                expectedBodiesReceived 'Bye Camel'
+                expectedVariableReceived("hello", "Camel")
+            }
+
+        when:
+            context.start()
+
+            withTemplate {
+                to('direct:send').withBody('World').withHeader("where", "foo").send()
+            }
+
+        then:
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
+
+    def "toDVariable receive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - toD:
+                            uri: direct:${header.where}
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - toD:
+                            uri: direct:${header.where}
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - toD:
+                            uri: direct:${header.where}
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:after') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'Bye World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+
+        when:
+        context.start()
+
+        withTemplate {
+            to('direct:receive').withBody('World').withHeader("where", "foo").send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+
+    def "toDVariable sendAndReceive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - toD:
+                            uri: direct:${header.where}
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - toD:
+                            uri: direct:${header.where}
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - toD:
+                            uri: direct:${header.where}
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:before') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("hello", "Camel")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye Camel")
+        }
+
+        when:
+        context.start()
+
+        withTemplate {
+            to('direct:sendAndReceive').withBody('World').withHeader("where", "foo").send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+}


(camel) 09/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 7479f2567e22062331024f2b5b84d57334b9940c
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Jan 22 17:02:45 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../resources/org/apache/camel/model/toD.json      | 12 +--
 .../resources/org/apache/camel/model/wireTap.json  | 12 +--
 .../apache/camel/model/ProcessorDefinition.java    | 34 +++++++++
 .../apache/camel/model/ToDynamicDefinition.java    | 46 +++++++++++
 .../camel/processor/SendDynamicProcessor.java      | 46 +++++++++++
 .../org/apache/camel/reifier/ToDynamicReifier.java |  2 +
 .../camel/processor/ToDynamicVariableTest.java     | 89 ++++++++++++++++++++++
 .../java/org/apache/camel/xml/in/ModelParser.java  |  2 +
 .../java/org/apache/camel/xml/out/ModelWriter.java |  2 +
 .../org/apache/camel/yaml/out/ModelWriter.java     |  2 +
 10 files changed, 237 insertions(+), 10 deletions(-)

diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/toD.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/toD.json
index d914a72f257..2f1ff5355c4 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/toD.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/toD.json
@@ -16,10 +16,12 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "uri": { "index": 3, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression." },
-    "pattern": { "index": 4, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" },
-    "cacheSize": { "index": 5, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this recipient list, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can [...]
-    "ignoreInvalidEndpoint": { "index": 6, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to ignore invalid endpoint URIs and skip sending the message." },
-    "allowOptimisedComponents": { "index": 7, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise toD if they are org.apache.camel.spi.SendDynamicAware ." },
-    "autoStartComponents": { "index": 8, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when toD is starting up." }
+    "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this  [...]
+    "pattern": { "index": 6, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" },
+    "cacheSize": { "index": 7, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this recipient list, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can [...]
+    "ignoreInvalidEndpoint": { "index": 8, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to ignore invalid endpoint URIs and skip sending the message." },
+    "allowOptimisedComponents": { "index": 9, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise toD if they are org.apache.camel.spi.SendDynamicAware ." },
+    "autoStartComponents": { "index": 10, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when toD is starting up." }
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/wireTap.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/wireTap.json
index 46d3a6d7432..3681587d368 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/wireTap.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/wireTap.json
@@ -20,10 +20,12 @@
     "onPrepare": { "index": 5, "kind": "attribute", "displayName": "On Prepare", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.Processor", "deprecated": false, "autowired": false, "secret": false, "description": "Uses the Processor when preparing the org.apache.camel.Exchange to be sent. This can be used to deep-clone messages that should be sent, or any custom logic needed before the exchange is sent." },
     "executorService": { "index": 6, "kind": "attribute", "displayName": "Executor Service", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "Uses a custom thread pool" },
     "uri": { "index": 7, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The uri of the endpoint to send to. The uri can be dynamic computed using the org.apache.camel.language.simple.SimpleLanguage expression." },
-    "pattern": { "index": 8, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" },
-    "cacheSize": { "index": 9, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this recipient list, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can [...]
-    "ignoreInvalidEndpoint": { "index": 10, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to ignore invalid endpoint URIs and skip sending the message." },
-    "allowOptimisedComponents": { "index": 11, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise toD if they are org.apache.camel.spi.SendDynamicAware ." },
-    "autoStartComponents": { "index": 12, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when toD is starting up." }
+    "variableSend": { "index": 8, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 9, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this  [...]
+    "pattern": { "index": 10, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" },
+    "cacheSize": { "index": 11, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this recipient list, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache ca [...]
+    "ignoreInvalidEndpoint": { "index": 12, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to ignore invalid endpoint URIs and skip sending the message." },
+    "allowOptimisedComponents": { "index": 13, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise toD if they are org.apache.camel.spi.SendDynamicAware ." },
+    "autoStartComponents": { "index": 14, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when toD is starting up." }
   }
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
index 5d6e933a95b..63a1cd5e5b8 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
@@ -295,6 +295,22 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
         return asType();
     }
 
+    /**
+     * Sends the exchange to the given dynamic endpoint
+     *
+     * @param  uri             the dynamic endpoint to send to (resolved using simple language by default)
+     * @param  variableSend    to use a variable as the source for the message body to send.
+     * @param  variableReceive to use a variable to store the received message body (only body, not headers).
+     * @return                 the builder
+     */
+    public Type toD(@AsEndpointUri String uri, String variableSend, String variableReceive) {
+        ToDynamicDefinition answer = new ToDynamicDefinition(uri);
+        answer.setVariableSend(variableSend);
+        answer.setVariableReceive(variableReceive);
+        addOutput(answer);
+        return asType();
+    }
+
     /**
      * Sends the exchange to the given dynamic endpoint
      *
@@ -308,6 +324,24 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
         return asType();
     }
 
+    /**
+     * Sends the exchange to the given dynamic endpoint
+     *
+     * @param  endpointProducerBuilder the dynamic endpoint to send to (resolved using simple language by default)
+     * @param  variableSend            to use a variable as the source for the message body to send.
+     * @param  variableReceive         to use a variable to store the received message body (only body, not headers).
+     * @return                         the builder
+     */
+    public Type toD(
+            @AsEndpointUri EndpointProducerBuilder endpointProducerBuilder, String variableSend, String variableReceive) {
+        ToDynamicDefinition answer = new ToDynamicDefinition();
+        answer.setEndpointProducerBuilder(endpointProducerBuilder);
+        answer.setVariableSend(variableSend);
+        answer.setVariableReceive(variableReceive);
+        addOutput(answer);
+        return asType();
+    }
+
     /**
      * Sends the exchange to the given dynamic endpoint
      *
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ToDynamicDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ToDynamicDefinition.java
index c7fb6363198..3daac2cbf52 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/ToDynamicDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ToDynamicDefinition.java
@@ -23,6 +23,7 @@ import jakarta.xml.bind.annotation.XmlRootElement;
 import jakarta.xml.bind.annotation.XmlTransient;
 
 import org.apache.camel.ExchangePattern;
+import org.apache.camel.Message;
 import org.apache.camel.builder.EndpointProducerBuilder;
 import org.apache.camel.spi.AsEndpointUri;
 import org.apache.camel.spi.Metadata;
@@ -42,6 +43,10 @@ public class ToDynamicDefinition extends NoOutputDefinition<ToDynamicDefinition>
     @Metadata(required = true)
     private String uri;
     @XmlAttribute
+    private String variableSend;
+    @XmlAttribute
+    private String variableReceive;
+    @XmlAttribute
     @Metadata(label = "advanced", javaType = "org.apache.camel.ExchangePattern", enums = "InOnly,InOut")
     private String pattern;
     @XmlAttribute
@@ -101,6 +106,31 @@ public class ToDynamicDefinition extends NoOutputDefinition<ToDynamicDefinition>
         return this;
     }
 
+    /**
+     * To use a variable as the source for the message body to send. This makes it handy to use variables for user data
+     * and to easily control what data to use for sending and receiving.
+     *
+     * Important: When using send variable then the message body is taken from this variable instead of the current
+     * {@link Message}, however the headers from the {@link Message} will still be used as well. In other words, the
+     * variable is used instead of the message body, but everything else is as usual.
+     */
+    public ToDynamicDefinition variableReceive(String variableReceive) {
+        setVariableReceive(variableReceive);
+        return this;
+    }
+
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public ToDynamicDefinition variableSend(String variableSend) {
+        setVariableSend(variableSend);
+        return this;
+    }
+
     /**
      * Sets the optional {@link ExchangePattern} used to invoke this endpoint
      */
@@ -230,6 +260,22 @@ public class ToDynamicDefinition extends NoOutputDefinition<ToDynamicDefinition>
         this.endpointProducerBuilder = endpointProducerBuilder;
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     public String getPattern() {
         return pattern;
     }
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendDynamicProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendDynamicProcessor.java
index fc842482972..13620cc6ce6 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendDynamicProcessor.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendDynamicProcessor.java
@@ -58,6 +58,8 @@ public class SendDynamicProcessor extends AsyncProcessorSupport implements IdAwa
     protected CamelContext camelContext;
     protected final String uri;
     protected final Expression expression;
+    protected String variableSend;
+    protected String variableReceive;
     protected ExchangePattern pattern;
     protected ProducerCache producerCache;
     protected String id;
@@ -99,6 +101,8 @@ public class SendDynamicProcessor extends AsyncProcessorSupport implements IdAwa
 
     @Override
     public boolean process(Exchange exchange, final AsyncCallback callback) {
+        // TODO: variables
+
         if (!isStarted()) {
             exchange.setException(new IllegalStateException("SendProcessor has not been started: " + this));
             callback.done(true);
@@ -172,6 +176,20 @@ public class SendDynamicProcessor extends AsyncProcessorSupport implements IdAwa
             return true;
         }
 
+        // if we should store the received message body in a variable,
+        // then we need to preserve the original message body
+        Object body = null;
+        if (variableReceive != null) {
+            try {
+                body = exchange.getMessage().getBody();
+            } catch (Exception throwable) {
+                exchange.setException(throwable);
+                callback.done(true);
+                return true;
+            }
+        }
+        final Object originalBody = body;
+
         // send the exchange to the destination using the producer cache
         final Processor preProcessor = preAwareProcessor;
         final Processor postProcessor = postAwareProcessor;
@@ -184,6 +202,12 @@ public class SendDynamicProcessor extends AsyncProcessorSupport implements IdAwa
                 if (preProcessor != null) {
                     preProcessor.process(target);
                 }
+                // replace message body with variable
+                if (variableSend != null) {
+                    // it may be a global variable
+                    Object value = ExchangeHelper.getVariable(exchange, variableSend);
+                    exchange.getMessage().setBody(value);
+                }
             } catch (Exception t) {
                 e.setException(t);
                 // restore previous MEP
@@ -208,6 +232,12 @@ public class SendDynamicProcessor extends AsyncProcessorSupport implements IdAwa
                     if (stopEndpoint) {
                         ServiceHelper.stopAndShutdownService(endpoint);
                     }
+                    // result should be stored in variable instead of message body
+                    if (variableReceive != null) {
+                        Object value = exchange.getMessage().getBody();
+                        ExchangeHelper.setVariable(exchange, variableReceive, value);
+                        exchange.getMessage().setBody(originalBody);
+                    }
                     // signal we are done
                     c.done(doneSync);
                 }
@@ -410,6 +440,22 @@ public class SendDynamicProcessor extends AsyncProcessorSupport implements IdAwa
         this.pattern = pattern;
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     public boolean isIgnoreInvalidEndpoint() {
         return ignoreInvalidEndpoint;
     }
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
index f9fb814c5d8..cfa004429fa 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
@@ -56,6 +56,8 @@ public class ToDynamicReifier<T extends ToDynamicDefinition> extends ProcessorRe
         SendDynamicProcessor processor = new SendDynamicProcessor(uri, exp);
         processor.setCamelContext(camelContext);
         processor.setPattern(parse(ExchangePattern.class, definition.getPattern()));
+        processor.setVariableSend(definition.getVariableSend());
+        processor.setVariableReceive(definition.getVariableReceive());
         Integer num = parseInt(definition.getCacheSize());
         if (num != null) {
             processor.setCacheSize(num);
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/ToDynamicVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/ToDynamicVariableTest.java
new file mode 100644
index 00000000000..06f87a05cbb
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/ToDynamicVariableTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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 ToDynamicVariableTest extends ContextTestSupport {
+
+    @Test
+    public void testSend() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye Camel");
+        getMockEndpoint("mock:result").expectedVariableReceived("hello", "Camel");
+
+        template.sendBodyAndHeader("direct:send", "World", "where", "foo");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testReceive() throws Exception {
+        getMockEndpoint("mock:after").expectedBodiesReceived("World");
+        getMockEndpoint("mock:after").expectedVariableReceived("bye", "Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye World");
+
+        template.sendBodyAndHeader("direct:receive", "World", "where", "foo");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testSendAndReceive() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye Camel");
+
+        template.sendBodyAndHeader("direct:sendAndReceive", "World", "where", "foo");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:send")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .toD("direct:${header.where}", "hello", null)
+                        .to("mock:result");
+
+                from("direct:receive")
+                        .toD("direct:${header.where}", null, "bye")
+                        .to("mock:after")
+                        .setBody(simple("${variable:bye}"))
+                        .to("mock:result");
+
+                from("direct:sendAndReceive")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .toD("direct:${header.where}", "hello", "bye")
+                        .to("mock:result");
+
+                from("direct:foo")
+                        .transform().simple("Bye ${body}");
+            }
+        };
+    }
+}
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 a2b661193ca..ccc33dcc6d1 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
@@ -1555,6 +1555,8 @@ public class ModelParser extends BaseParser {
                 case "ignoreInvalidEndpoint": def.setIgnoreInvalidEndpoint(val); break;
                 case "pattern": def.setPattern(val); break;
                 case "uri": def.setUri(val); break;
+                case "variableReceive": def.setVariableReceive(val); break;
+                case "variableSend": def.setVariableSend(val); break;
                 default: return processorDefinitionAttributeHandler().accept(def, key, val);
             }
             return true;
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 7e16687fce7..b012cca083c 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -2437,6 +2437,8 @@ public class ModelWriter extends BaseWriter {
             ToDynamicDefinition def)
             throws IOException {
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteAttribute("cacheSize", def.getCacheSize());
         doWriteAttribute("ignoreInvalidEndpoint", def.getIgnoreInvalidEndpoint());
         doWriteAttribute("autoStartComponents", def.getAutoStartComponents());
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index da3fa16773b..cecac392932 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -2437,6 +2437,8 @@ public class ModelWriter extends BaseWriter {
             ToDynamicDefinition def)
             throws IOException {
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteAttribute("cacheSize", def.getCacheSize());
         doWriteAttribute("ignoreInvalidEndpoint", def.getIgnoreInvalidEndpoint());
         doWriteAttribute("autoStartComponents", def.getAutoStartComponents());


(camel) 14/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 347016a0fc6991e626dcf12916cbb175c3af00a0
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Jan 23 12:03:24 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../org/apache/camel/catalog/models/enrich.json    |  20 +-
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  22 ++
 .../spring/processor/SpringEnrichVariableTest.java |  29 +++
 .../camel/spring/processor/EnrichVariableTest.xml  |  72 ++++++
 .../resources/org/apache/camel/model/enrich.json   |  20 +-
 .../org/apache/camel/model/EnrichDefinition.java   |  44 ++++
 .../java/org/apache/camel/processor/Enricher.java  |  40 ++++
 .../org/apache/camel/processor/PollEnricher.java   |   2 +-
 .../org/apache/camel/reifier/EnrichReifier.java    |   2 +
 .../apache/camel/processor/EnrichVariableTest.java |  89 +++++++
 .../api/management/mbean/ManagedEnricherMBean.java |   6 +
 .../camel/management/mbean/ManagedEnricher.java    |  10 +
 .../java/org/apache/camel/xml/in/ModelParser.java  |   2 +
 .../java/org/apache/camel/xml/out/ModelWriter.java |   2 +
 .../org/apache/camel/yaml/out/ModelWriter.java     |   2 +
 .../dsl/yaml/deserializers/ModelDeserializers.java |  14 +-
 .../generated/resources/schema/camelYamlDsl.json   |  10 +
 .../camel/dsl/yaml/EnrichVariableTest.groovy       | 264 +++++++++++++++++++++
 18 files changed, 630 insertions(+), 20 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/enrich.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/enrich.json
index 7326f89b55c..e11f1e6a6be 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/enrich.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/enrich.json
@@ -16,14 +16,16 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
-    "aggregationStrategy": { "index": 4, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
-    "aggregationStrategyMethodName": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
-    "aggregationStrategyMethodAllowNull": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Allow Null", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "If this option is false then the aggregate method is not used if there was no data to enrich. If this option is true then null values is used as the oldExchange (when no data to enrich), when us [...]
-    "aggregateOnException": { "index": 7, "kind": "attribute", "displayName": "Aggregate On Exception", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If this option is false then the aggregate method is not used if there was an exception thrown while trying to retrieve the data to enrich from the resource. Setting this option to true allows end us [...]
-    "shareUnitOfWork": { "index": 8, "kind": "attribute", "displayName": "Share Unit Of Work", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Shares the org.apache.camel.spi.UnitOfWork with the parent and the resource exchange. Enrich will by default not share unit of work between the parent exchange and the resource exchange. This means the resour [...]
-    "cacheSize": { "index": 9, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producer when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can be utilized. If each dynamic end [...]
-    "ignoreInvalidEndpoint": { "index": 10, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
-    "allowOptimisedComponents": { "index": 11, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise enricher if they are org.apache.camel.spi.SendDynamicAware ." },
-    "autoStartComponents": { "index": 12, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when enricher is starting up." }
+    "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
+    "aggregationStrategy": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
+    "aggregationStrategyMethodName": { "index": 7, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
+    "aggregationStrategyMethodAllowNull": { "index": 8, "kind": "attribute", "displayName": "Aggregation Strategy Method Allow Null", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "If this option is false then the aggregate method is not used if there was no data to enrich. If this option is true then null values is used as the oldExchange (when no data to enrich), when us [...]
+    "aggregateOnException": { "index": 9, "kind": "attribute", "displayName": "Aggregate On Exception", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If this option is false then the aggregate method is not used if there was an exception thrown while trying to retrieve the data to enrich from the resource. Setting this option to true allows end us [...]
+    "shareUnitOfWork": { "index": 10, "kind": "attribute", "displayName": "Share Unit Of Work", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Shares the org.apache.camel.spi.UnitOfWork with the parent and the resource exchange. Enrich will by default not share unit of work between the parent exchange and the resource exchange. This means the resou [...]
+    "cacheSize": { "index": 11, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producer when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can be utilized. If each dynamic en [...]
+    "ignoreInvalidEndpoint": { "index": 12, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
+    "allowOptimisedComponents": { "index": 13, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise enricher if they are org.apache.camel.spi.SendDynamicAware ." },
+    "autoStartComponents": { "index": 14, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when enricher is starting up." }
   }
 }
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 be712c5bf41..0dc0cfd581e 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
@@ -5300,6 +5300,28 @@ reduce memory usage to avoid storing too many non frequent used producers.
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
         </xs:choice>
+        <xs:attribute name="variableSend" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="variableReceive" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
         <xs:attribute name="aggregationStrategy" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringEnrichVariableTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringEnrichVariableTest.java
new file mode 100644
index 00000000000..3e4c8769f42
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringEnrichVariableTest.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.EnrichVariableTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringEnrichVariableTest extends EnrichVariableTest {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/EnrichVariableTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/EnrichVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/EnrichVariableTest.xml
new file mode 100644
index 00000000000..a6b15b135e6
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/EnrichVariableTest.xml
@@ -0,0 +1,72 @@
+<?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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <!-- START SNIPPET: example -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+    <route>
+      <from uri="direct:send"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <enrich variableSend="hello">
+        <constant>direct:foo</constant>
+      </enrich>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:receive"/>
+      <enrich variableReceive="bye">
+        <constant>direct:foo</constant>
+      </enrich>
+      <to uri="mock:after"/>
+      <setBody>
+        <simple>${variable:bye}</simple>
+      </setBody>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:sendAndReceive"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <enrich variableSend="hello" variableReceive="bye">
+        <constant>direct:foo</constant>
+      </enrich>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:foo"/>
+      <transform>
+        <simple>Bye ${body}</simple>
+      </transform>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: example -->
+
+</beans>
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/enrich.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/enrich.json
index 7326f89b55c..e11f1e6a6be 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/enrich.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/enrich.json
@@ -16,14 +16,16 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
-    "aggregationStrategy": { "index": 4, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
-    "aggregationStrategyMethodName": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
-    "aggregationStrategyMethodAllowNull": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Allow Null", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "If this option is false then the aggregate method is not used if there was no data to enrich. If this option is true then null values is used as the oldExchange (when no data to enrich), when us [...]
-    "aggregateOnException": { "index": 7, "kind": "attribute", "displayName": "Aggregate On Exception", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If this option is false then the aggregate method is not used if there was an exception thrown while trying to retrieve the data to enrich from the resource. Setting this option to true allows end us [...]
-    "shareUnitOfWork": { "index": 8, "kind": "attribute", "displayName": "Share Unit Of Work", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Shares the org.apache.camel.spi.UnitOfWork with the parent and the resource exchange. Enrich will by default not share unit of work between the parent exchange and the resource exchange. This means the resour [...]
-    "cacheSize": { "index": 9, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producer when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can be utilized. If each dynamic end [...]
-    "ignoreInvalidEndpoint": { "index": 10, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
-    "allowOptimisedComponents": { "index": 11, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise enricher if they are org.apache.camel.spi.SendDynamicAware ." },
-    "autoStartComponents": { "index": 12, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when enricher is starting up." }
+    "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
+    "aggregationStrategy": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
+    "aggregationStrategyMethodName": { "index": 7, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
+    "aggregationStrategyMethodAllowNull": { "index": 8, "kind": "attribute", "displayName": "Aggregation Strategy Method Allow Null", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "If this option is false then the aggregate method is not used if there was no data to enrich. If this option is true then null values is used as the oldExchange (when no data to enrich), when us [...]
+    "aggregateOnException": { "index": 9, "kind": "attribute", "displayName": "Aggregate On Exception", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If this option is false then the aggregate method is not used if there was an exception thrown while trying to retrieve the data to enrich from the resource. Setting this option to true allows end us [...]
+    "shareUnitOfWork": { "index": 10, "kind": "attribute", "displayName": "Share Unit Of Work", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Shares the org.apache.camel.spi.UnitOfWork with the parent and the resource exchange. Enrich will by default not share unit of work between the parent exchange and the resource exchange. This means the resou [...]
+    "cacheSize": { "index": 11, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producer when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can be utilized. If each dynamic en [...]
+    "ignoreInvalidEndpoint": { "index": 12, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
+    "allowOptimisedComponents": { "index": 13, "kind": "attribute", "displayName": "Allow Optimised Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to allow components to optimise enricher if they are org.apache.camel.spi.SendDynamicAware ." },
+    "autoStartComponents": { "index": 14, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when enricher is starting up." }
   }
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/EnrichDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/EnrichDefinition.java
index 8f8baa14f2a..7b94d4e0bf2 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/EnrichDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/EnrichDefinition.java
@@ -39,6 +39,10 @@ public class EnrichDefinition extends ExpressionNode implements AggregationStrat
     @XmlTransient
     private AggregationStrategy aggregationStrategyBean;
 
+    @XmlAttribute
+    private String variableSend;
+    @XmlAttribute
+    private String variableReceive;
     @XmlAttribute
     @Metadata(javaType = "org.apache.camel.AggregationStrategy")
     private String aggregationStrategy;
@@ -93,6 +97,30 @@ public class EnrichDefinition extends ExpressionNode implements AggregationStrat
     // Fluent API
     // -------------------------------------------------------------------------
 
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public EnrichDefinition variableReceive(String variableReceive) {
+        setVariableReceive(variableReceive);
+        return this;
+    }
+
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public EnrichDefinition variableSend(String variableSend) {
+        setVariableSend(variableSend);
+        return this;
+    }
+
     /**
      * Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing
      * message. By default Camel will use the reply from the external service as outgoing message.
@@ -296,6 +324,22 @@ public class EnrichDefinition extends ExpressionNode implements AggregationStrat
         this.aggregateOnException = aggregateOnException;
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     public String getShareUnitOfWork() {
         return shareUnitOfWork;
     }
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/Enricher.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/Enricher.java
index d0ac88b9624..bb8350971ec 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/Enricher.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/Enricher.java
@@ -54,6 +54,8 @@ public class Enricher extends AsyncProcessorSupport implements IdAware, RouteIdA
     private String routeId;
     private final Expression expression;
     private final String uri;
+    private String variableSend;
+    private String variableReceive;
     private AggregationStrategy aggregationStrategy;
     private boolean aggregateOnException;
     private boolean shareUnitOfWork;
@@ -107,6 +109,22 @@ public class Enricher extends AsyncProcessorSupport implements IdAware, RouteIdA
         return sendDynamicProcessor.getEndpointUtilizationStatistics();
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
         this.aggregationStrategy = aggregationStrategy;
     }
@@ -166,6 +184,21 @@ public class Enricher extends AsyncProcessorSupport implements IdAware, RouteIdA
     @Override
     public boolean process(final Exchange exchange, final AsyncCallback callback) {
         final Exchange resourceExchange = createResourceExchange(exchange, ExchangePattern.InOut);
+
+        // if we should store the received message body in a variable,
+        // then we need to preserve the original message body
+        Object body = null;
+        if (variableReceive != null) {
+            try {
+                body = exchange.getMessage().getBody();
+            } catch (Exception throwable) {
+                exchange.setException(throwable);
+                callback.done(true);
+                return true;
+            }
+        }
+        final Object originalBody = body;
+
         return sendDynamicProcessor.process(resourceExchange, new AsyncCallback() {
             @Override
             public void done(boolean doneSync) {
@@ -181,6 +214,12 @@ public class Enricher extends AsyncProcessorSupport implements IdAware, RouteIdA
 
                         Exchange aggregatedExchange = aggregationStrategy.aggregate(exchange, resourceExchange);
                         if (aggregatedExchange != null) {
+                            if (variableReceive != null) {
+                                // result should be stored in variable instead of message body
+                                Object value = aggregatedExchange.getMessage().getBody();
+                                ExchangeHelper.setVariable(exchange, variableReceive, value);
+                                aggregatedExchange.getMessage().setBody(originalBody);
+                            }
                             // copy aggregation result onto original exchange (preserving pattern)
                             copyResultsWithoutCorrelationId(exchange, aggregatedExchange);
                             // handover any synchronization (if unit of work is not shared)
@@ -248,6 +287,7 @@ public class Enricher extends AsyncProcessorSupport implements IdAware, RouteIdA
         this.sendDynamicProcessor.setIgnoreInvalidEndpoint(ignoreInvalidEndpoint);
         this.sendDynamicProcessor.setAllowOptimisedComponents(allowOptimisedComponents);
         this.sendDynamicProcessor.setAutoStartupComponents(autoStartupComponents);
+        this.sendDynamicProcessor.setVariableSend(variableSend);
 
         // create a per processor exchange factory
         this.processorExchangeFactory = getCamelContext().getCamelContextExtension()
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/PollEnricher.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/PollEnricher.java
index ca029a6525a..21759c73f24 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/PollEnricher.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/PollEnricher.java
@@ -67,8 +67,8 @@ public class PollEnricher extends AsyncProcessorSupport implements IdAware, Rout
     protected volatile String scheme;
     private String id;
     private String routeId;
-    private AggregationStrategy aggregationStrategy;
     private String variableReceive;
+    private AggregationStrategy aggregationStrategy;
     private final Expression expression;
     private final String uri;
     private long timeout;
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 2302628c569..08bcf089987 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
@@ -56,6 +56,8 @@ public class EnrichReifier extends ExpressionReifier<EnrichDefinition> {
         }
 
         Enricher enricher = new Enricher(exp, uri);
+        enricher.setVariableSend(parseString(definition.getVariableSend()));
+        enricher.setVariableReceive(parseString(definition.getVariableReceive()));
         enricher.setShareUnitOfWork(parseBoolean(definition.getShareUnitOfWork(), false));
         enricher.setIgnoreInvalidEndpoint(parseBoolean(definition.getIgnoreInvalidEndpoint(), false));
         enricher.setAggregateOnException(parseBoolean(definition.getAggregateOnException(), false));
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/EnrichVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/EnrichVariableTest.java
new file mode 100644
index 00000000000..a0e1e53f7a3
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/EnrichVariableTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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 EnrichVariableTest extends ContextTestSupport {
+
+    @Test
+    public void testSend() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye Camel");
+        getMockEndpoint("mock:result").expectedVariableReceived("hello", "Camel");
+
+        template.sendBody("direct:send", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testReceive() throws Exception {
+        getMockEndpoint("mock:after").expectedBodiesReceived("World");
+        getMockEndpoint("mock:after").expectedVariableReceived("bye", "Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye World");
+
+        template.sendBody("direct:receive", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testSendAndReceive() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye Camel");
+
+        template.sendBody("direct:sendAndReceive", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:send")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .enrich().constant("direct:foo").variableSend("hello")
+                        .to("mock:result");
+
+                from("direct:receive")
+                        .enrich().constant("direct:foo").variableReceive("bye")
+                        .to("mock:after")
+                        .setBody(simple("${variable:bye}"))
+                        .to("mock:result");
+
+                from("direct:sendAndReceive")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .enrich().constant("direct:foo").variableSend("hello").variableReceive("bye")
+                        .to("mock:result");
+
+                from("direct:foo")
+                        .transform().simple("Bye ${body}");
+            }
+        };
+    }
+}
diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedEnricherMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedEnricherMBean.java
index 773f99c25f6..e8183ab20ea 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedEnricherMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedEnricherMBean.java
@@ -30,6 +30,12 @@ public interface ManagedEnricherMBean extends ManagedProcessorMBean, ManagedExte
                       mask = true)
     String getExpression();
 
+    @ManagedAttribute(description = "Variable as the source for the message body to send")
+    String getVariableSend();
+
+    @ManagedAttribute(description = "Variable to store the received message body (only body, not headers)")
+    String getVariableReceive();
+
     @ManagedAttribute(description = "Sets the maximum size used by the ProducerCache which is used to cache and reuse producers")
     Integer getCacheSize();
 
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedEnricher.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedEnricher.java
index 76f9a07a53a..cd7cec862c8 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedEnricher.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedEnricher.java
@@ -89,6 +89,16 @@ public class ManagedEnricher extends ManagedProcessor implements ManagedEnricher
         return uri;
     }
 
+    @Override
+    public String getVariableSend() {
+        return processor.getVariableSend();
+    }
+
+    @Override
+    public String getVariableReceive() {
+        return processor.getVariableReceive();
+    }
+
     @Override
     public Integer getCacheSize() {
         return processor.getCacheSize();
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 6a4f5892c57..a976bf9ba52 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
@@ -408,6 +408,8 @@ public class ModelParser extends BaseParser {
                 case "cacheSize": def.setCacheSize(val); break;
                 case "ignoreInvalidEndpoint": def.setIgnoreInvalidEndpoint(val); break;
                 case "shareUnitOfWork": def.setShareUnitOfWork(val); break;
+                case "variableReceive": def.setVariableReceive(val); break;
+                case "variableSend": def.setVariableSend(val); break;
                 default: return processorDefinitionAttributeHandler().accept(def, key, val);
             }
             return true;
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 29a3ac921fe..dafa67a7831 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -1250,6 +1250,8 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteAttribute("cacheSize", def.getCacheSize());
         doWriteAttribute("aggregationStrategy", def.getAggregationStrategy());
         doWriteAttribute("ignoreInvalidEndpoint", def.getIgnoreInvalidEndpoint());
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index 974cebf1f05..7d60741bd7b 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -1250,6 +1250,8 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteAttribute("cacheSize", def.getCacheSize());
         doWriteAttribute("aggregationStrategy", def.getAggregationStrategy());
         doWriteAttribute("ignoreInvalidEndpoint", def.getIgnoreInvalidEndpoint());
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 7ef2c0a270e..b6116965d2a 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
@@ -5041,7 +5041,9 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "id", type = "string", description = "Sets the id of this node", displayName = "Id"),
                     @YamlProperty(name = "ignoreInvalidEndpoint", type = "boolean", description = "Ignore the invalidate endpoint exception when try to create a producer with that endpoint", displayName = "Ignore Invalid Endpoint"),
                     @YamlProperty(name = "inheritErrorHandler", type = "boolean"),
-                    @YamlProperty(name = "shareUnitOfWork", type = "boolean", description = "Shares the org.apache.camel.spi.UnitOfWork with the parent and the resource exchange. Enrich will by default not share unit of work between the parent exchange and the resource exchange. This means the resource exchange has its own individual unit of work.", displayName = "Share Unit Of Work")
+                    @YamlProperty(name = "shareUnitOfWork", type = "boolean", description = "Shares the org.apache.camel.spi.UnitOfWork with the parent and the resource exchange. Enrich will by default not share unit of work between the parent exchange and the resource exchange. This means the resource exchange has its own individual unit of work.", displayName = "Share Unit Of Work"),
+                    @YamlProperty(name = "variableReceive", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Receive"),
+                    @YamlProperty(name = "variableSend", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Send")
             }
     )
     public static class EnrichDefinitionDeserializer extends YamlDeserializerBase<EnrichDefinition> {
@@ -5119,6 +5121,16 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     target.setShareUnitOfWork(val);
                     break;
                 }
+                case "variableReceive": {
+                    String val = asText(node);
+                    target.setVariableReceive(val);
+                    break;
+                }
+                case "variableSend": {
+                    String val = asText(node);
+                    target.setVariableSend(val);
+                    break;
+                }
                 case "id": {
                     String val = asText(node);
                     target.setId(val);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index bafd77a84db..8737486cc97 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -1324,6 +1324,16 @@
             "title" : "Share Unit Of Work",
             "description" : "Shares the org.apache.camel.spi.UnitOfWork with the parent and the resource exchange. Enrich will by default not share unit of work between the parent exchange and the resource exchange. This means the resource exchange has its own individual unit of work."
           },
+          "variableReceive" : {
+            "type" : "string",
+            "title" : "Variable Receive",
+            "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
+          },
+          "variableSend" : {
+            "type" : "string",
+            "title" : "Variable Send",
+            "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
+          },
           "constant" : { },
           "csimple" : { },
           "datasonnet" : { },
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/EnrichVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/EnrichVariableTest.groovy
new file mode 100644
index 00000000000..3a889dfa05d
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/EnrichVariableTest.groovy
@@ -0,0 +1,264 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+
+class EnrichVariableTest extends YamlTestSupport {
+
+    def "enrichVariable send"() {
+        setup:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+            withMock('mock:before') {
+                expectedBodiesReceived 'World'
+                expectedVariableReceived("hello", "Camel")
+            }
+            withMock('mock:result') {
+                expectedBodiesReceived 'Bye Camel'
+                expectedVariableReceived("hello", "Camel")
+            }
+
+        when:
+            context.start()
+
+            withTemplate {
+                to('direct:send').withBody('World').send()
+            }
+
+        then:
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
+
+    def "enrichVariable receive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:after') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'Bye World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+
+        when:
+        context.start()
+
+        withTemplate {
+            to('direct:receive').withBody('World').send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+
+    def "enrichVariable sendAndReceive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - enrich:
+                            expression: 
+                              constant: "direct:foo"
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:before') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("hello", "Camel")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye Camel")
+        }
+
+        when:
+        context.start()
+
+        withTemplate {
+            to('direct:sendAndReceive').withBody('World').send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+}


(camel) 06/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a3598762425a7e5f82ee8c7d6a29c5c45e041504
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Jan 20 10:31:12 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../resources/org/apache/camel/model/to.json       |  6 ++-
 .../apache/camel/model/ProcessorDefinition.java    | 16 +++++++
 .../java/org/apache/camel/model/ToDefinition.java  | 36 +++++++++++++++
 .../org/apache/camel/processor/SendProcessor.java  | 18 ++++++++
 .../java/org/apache/camel/reifier/SendReifier.java | 12 ++---
 .../org/apache/camel/processor/ToVariableTest.java | 51 ++++++++++++++++++++++
 .../java/org/apache/camel/xml/in/ModelParser.java  | 10 +++--
 .../java/org/apache/camel/xml/out/ModelWriter.java |  2 +
 .../org/apache/camel/yaml/out/ModelWriter.java     |  2 +
 .../dsl/yaml/deserializers/ModelDeserializers.java | 14 +++++-
 .../generated/resources/schema/camelYamlDsl.json   |  6 +++
 11 files changed, 161 insertions(+), 12 deletions(-)

diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/to.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/to.json
index 053e6c95045..8f8698291c4 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/to.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/to.json
@@ -15,7 +15,9 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "uri": { "index": 3, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the uri of the endpoint to send to." },
-    "pattern": { "index": 4, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" }
+    "variableSend": { "index": 3, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this variab [...]
+    "variableReceive": { "index": 4, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
+    "uri": { "index": 5, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the uri of the endpoint to send to." },
+    "pattern": { "index": 6, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" }
   }
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
index 27e520df1ac..b4bf7f5605f 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
@@ -255,6 +255,22 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
         return asType();
     }
 
+    /**
+     * Sends the exchange to the given endpoint
+     *
+     * @param  uri the endpoint to send to
+     * @param variableSend to use a variable as the source for the message body to send.
+     * @param variableReceive to use a variable to store the received message body (only body, not headers).
+     * @return     the builder
+     */
+    public Type toV(@AsEndpointUri String uri, String variableSend, String variableReceive) {
+        ToDefinition to = new ToDefinition(uri);
+        to.setVariableSend(variableSend);
+        to.setVariableReceive(variableReceive);
+        addOutput(to);
+        return asType();
+    }
+
     /**
      * Sends the exchange to the given dynamic endpoint
      *
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ToDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ToDefinition.java
index eb889cd24b8..4b4864e59c8 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/ToDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ToDefinition.java
@@ -23,6 +23,7 @@ import jakarta.xml.bind.annotation.XmlRootElement;
 
 import org.apache.camel.Endpoint;
 import org.apache.camel.ExchangePattern;
+import org.apache.camel.Message;
 import org.apache.camel.builder.EndpointProducerBuilder;
 import org.apache.camel.spi.Metadata;
 
@@ -34,6 +35,10 @@ import org.apache.camel.spi.Metadata;
 @XmlAccessorType(XmlAccessType.FIELD)
 public class ToDefinition extends SendDefinition<ToDefinition> {
 
+    @XmlAttribute
+    private String variableSend;
+    @XmlAttribute
+    private String variableReceive;
     @XmlAttribute
     @Metadata(label = "advanced", javaType = "org.apache.camel.ExchangePattern", enums = "InOnly,InOut")
     private String pattern;
@@ -93,4 +98,35 @@ public class ToDefinition extends SendDefinition<ToDefinition> {
         this.pattern = pattern;
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    /**
+     * To use a variable as the source for the message body to send.
+     * This makes it handy to use variables for user data and to easily control what data to use for sending and receiving.
+     *
+     * Important: When using send variable then the message body
+     * is taken from this variable instead of the current {@link Message}, however
+     * the headers from the {@link Message} will still be used as well. In other words,
+     * the variable is used instead of the message body, but everything else is as usual.
+     */
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    /**
+     * To use a variable to store the received message body (only body, not headers).
+     * This is handy for easy access to the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored
+     * only in this variable and <b>not</b> on the current {@link org.apache.camel.Message}.
+     */
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
 }
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
index 516da700ce2..326f103a4cc 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
@@ -56,6 +56,8 @@ public class SendProcessor extends AsyncProcessorSupport implements Traceable, E
     protected ProducerCache producerCache;
     protected AsyncProducer producer;
     protected final Endpoint destination;
+    protected String variableSend;
+    protected String variableReceive;
     protected ExchangePattern destinationExchangePattern;
     protected String id;
     protected String routeId;
@@ -201,6 +203,22 @@ public class SendProcessor extends AsyncProcessorSupport implements Traceable, E
         }
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     public Endpoint getDestination() {
         return destination;
     }
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SendReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SendReifier.java
index 3f6f4b1f504..4eaa13a4f17 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SendReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SendReifier.java
@@ -22,20 +22,22 @@ import org.apache.camel.LineNumberAware;
 import org.apache.camel.Processor;
 import org.apache.camel.Route;
 import org.apache.camel.model.ProcessorDefinition;
-import org.apache.camel.model.SendDefinition;
+import org.apache.camel.model.ToDefinition;
 import org.apache.camel.processor.SendProcessor;
 import org.apache.camel.support.CamelContextHelper;
 
-public class SendReifier extends ProcessorReifier<SendDefinition<?>> {
+public class SendReifier extends ProcessorReifier<ToDefinition> {
 
     public SendReifier(Route route, ProcessorDefinition<?> definition) {
-        super(route, (SendDefinition) definition);
+        super(route, (ToDefinition) definition);
     }
 
     @Override
     public Processor createProcessor() throws Exception {
-        Endpoint endpoint = resolveEndpoint();
-        return new SendProcessor(endpoint, parse(ExchangePattern.class, definition.getPattern()));
+        SendProcessor answer = new SendProcessor(resolveEndpoint(), parse(ExchangePattern.class, definition.getPattern()));
+        answer.setVariableSend(definition.getVariableSend());
+        answer.setVariableReceive(definition.getVariableReceive());
+        return answer;
     }
 
     public Endpoint resolveEndpoint() {
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java
new file mode 100644
index 00000000000..561ca0a06b0
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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 ToVariableTest extends ContextTestSupport {
+
+    @Test
+    public void testOriginalBody() throws Exception {
+        getMockEndpoint("mock:after").expectedBodiesReceived("World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("World");
+
+        template.sendBody("direct:start", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                        .toV("direct:foo", null, "bye")
+                        .to("mock:after")
+                        .setBody(simple("${variable:bye}"))
+                        .to("mock:result");
+
+                from("direct:foo")
+                        .transform().simple("Bye ${body}");
+            }
+        };
+    }
+}
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 f542022a12f..a2b661193ca 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
@@ -1528,11 +1528,13 @@ public class ModelParser extends BaseParser {
     }
     protected ToDefinition doParseToDefinition() throws IOException, XmlPullParserException {
         return doParse(new ToDefinition(), (def, key, val) -> {
-            if ("pattern".equals(key)) {
-                def.setPattern(val);
-                return true;
+            switch (key) {
+                case "pattern": def.setPattern(val); break;
+                case "variableReceive": def.setVariableReceive(val); break;
+                case "variableSend": def.setVariableSend(val); break;
+                default: return sendDefinitionAttributeHandler().accept(def, key, val);
             }
-            return sendDefinitionAttributeHandler().accept(def, key, val);
+            return true;
         }, optionalIdentifiedDefinitionElementHandler(), noValueHandler());
     }
     protected <T extends SendDefinition> AttributeHandler<T> sendDefinitionAttributeHandler() {
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index fd48d046a6c..7e16687fce7 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -2428,6 +2428,8 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteSendDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteAttribute("pattern", def.getPattern());
         endElement(name);
     }
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index 0f2cf3ae648..da3fa16773b 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -2428,6 +2428,8 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteSendDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteAttribute("pattern", def.getPattern());
         endElement(name);
     }
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 a5b552a0785..aea4c113504 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
@@ -18050,7 +18050,9 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "inheritErrorHandler", type = "boolean"),
                     @YamlProperty(name = "parameters", type = "object"),
                     @YamlProperty(name = "pattern", type = "enum:InOnly,InOut", description = "Sets the optional ExchangePattern used to invoke this endpoint", displayName = "Pattern"),
-                    @YamlProperty(name = "uri", type = "string", required = true, description = "Sets the uri of the endpoint to send to.", displayName = "Uri")
+                    @YamlProperty(name = "uri", type = "string", required = true, description = "Sets the uri of the endpoint to send to.", displayName = "Uri"),
+                    @YamlProperty(name = "variableReceive", type = "string"),
+                    @YamlProperty(name = "variableSend", type = "string")
             }
     )
     public static class ToDefinitionDeserializer extends YamlDeserializerEndpointAwareBase<ToDefinition> {
@@ -18099,6 +18101,16 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     target.setUri(val);
                     break;
                 }
+                case "variableReceive": {
+                    String val = asText(node);
+                    target.setVariableReceive(val);
+                    break;
+                }
+                case "variableSend": {
+                    String val = asText(node);
+                    target.setVariableSend(val);
+                    break;
+                }
                 case "id": {
                     String val = asText(node);
                     target.setId(val);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 6964a70e2b1..87797184ab2 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -6849,6 +6849,12 @@
               "type" : "string",
               "title" : "Uri",
               "description" : "Sets the uri of the endpoint to send to."
+            },
+            "variableReceive" : {
+              "type" : "string"
+            },
+            "variableSend" : {
+              "type" : "string"
             }
           }
         } ],


(camel) 02/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f85e00bc80d33ffb0fba9d4271e445eb06a148e7
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Jan 19 12:34:03 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../deserializers/FromDefinitionDeserializer.java  |  7 +++
 .../OutputAwareFromDefinitionDeserializer.java     |  8 +++
 .../generated/resources/schema/camelYamlDsl.json   |  9 ++++
 .../apache/camel/dsl/yaml/FromVariableTest.groovy  | 57 ++++++++++++++++++++++
 4 files changed, 81 insertions(+)

diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/FromDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/FromDefinitionDeserializer.java
index 5e17eeff940..041eb98795a 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/FromDefinitionDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/FromDefinitionDeserializer.java
@@ -38,6 +38,7 @@ import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.getDeseri
           order = YamlDeserializerResolver.ORDER_DEFAULT,
           properties = {
                   @YamlProperty(name = "uri", type = "string", required = true),
+                  @YamlProperty(name = "variable", type = "string"),
                   @YamlProperty(name = "id", type = "string"),
                   @YamlProperty(name = "description", type = "string"),
                   @YamlProperty(name = "parameters", type = "object"),
@@ -54,6 +55,7 @@ public class FromDefinitionDeserializer implements ConstructNode {
 
         String desc = null;
         String id = null;
+        String variable = null;
         if (node.getNodeType() == NodeType.MAPPING) {
             final MappingNode mn = (MappingNode) node;
             for (NodeTuple tuple : mn.getValue()) {
@@ -67,6 +69,8 @@ public class FromDefinitionDeserializer implements ConstructNode {
                     desc = asText(tuple.getValueNode());
                 } else if ("id".equals(key)) {
                     id = asText(tuple.getValueNode());
+                } else if ("variable".equals(key)) {
+                    variable = asText(tuple.getValueNode());
                 }
             }
         }
@@ -91,6 +95,9 @@ public class FromDefinitionDeserializer implements ConstructNode {
         if (id != null) {
             target.setId(id);
         }
+        if (variable != null) {
+            target.setVariable(variable);
+        }
 
         // enrich model with line number
         if (line != -1) {
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/OutputAwareFromDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/OutputAwareFromDefinitionDeserializer.java
index c356d78cbd2..b932c448e10 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/OutputAwareFromDefinitionDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/OutputAwareFromDefinitionDeserializer.java
@@ -36,6 +36,7 @@ import org.snakeyaml.engine.v2.nodes.NodeTuple;
           order = YamlDeserializerResolver.ORDER_DEFAULT,
           properties = {
                   @YamlProperty(name = "uri", type = "string", required = true),
+                  @YamlProperty(name = "variable", type = "string"),
                   @YamlProperty(name = "id", type = "string"),
                   @YamlProperty(name = "description", type = "string"),
                   @YamlProperty(name = "parameters", type = "object"),
@@ -69,6 +70,7 @@ public class OutputAwareFromDefinitionDeserializer extends YamlDeserializerBase<
         String uri = null;
         String id = null;
         String desc = null;
+        String variable = null;
         Map<String, Object> parameters = null;
 
         for (NodeTuple tuple : node.getValue()) {
@@ -88,6 +90,9 @@ public class OutputAwareFromDefinitionDeserializer extends YamlDeserializerBase<
                 case "uri":
                     uri = asText(val);
                     break;
+                case "variable":
+                    variable = asText(val);
+                    break;
                 case "parameters":
                     parameters = parseParameters(tuple);
                     break;
@@ -114,6 +119,9 @@ public class OutputAwareFromDefinitionDeserializer extends YamlDeserializerBase<
             if (desc != null) {
                 from.setDescription(desc);
             }
+            if (variable != null) {
+                from.setVariable(variable);
+            }
             target.setDelegate(from);
         }
     }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 5ab84e6d6ef..e4c6f9e3fdc 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -313,6 +313,9 @@
           },
           "uri" : {
             "type" : "string"
+          },
+          "variable" : {
+            "type" : "string"
           }
         },
         "required" : [ "steps", "uri" ]
@@ -339,6 +342,9 @@
           },
           "uri" : {
             "type" : "string"
+          },
+          "variable" : {
+            "type" : "string"
           }
         },
         "required" : [ "steps", "uri" ]
@@ -1961,6 +1967,9 @@
           },
           "uri" : {
             "type" : "string"
+          },
+          "variable" : {
+            "type" : "string"
           }
         },
         "required" : [ "steps", "uri" ]
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/FromVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/FromVariableTest.groovy
new file mode 100644
index 00000000000..08eecb16ad2
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/FromVariableTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+
+class FromVariableTest extends YamlTestSupport {
+
+    def "fromVariable"() {
+        setup:
+            loadRoutes '''
+                - from:
+                    uri: "direct:start"
+                    variable: "myKey"
+                    steps:
+                      - transform:
+                          simple: "Bye ${body}"
+                      - to: "mock:foo"
+                      - setBody:
+                          simple: "${variable:myKey}"
+                      - to: "mock:result"
+            '''
+
+            withMock('mock:foo') {
+                expectedBodiesReceived 'Bye World'
+            }
+            withMock('mock:result') {
+                expectedBodiesReceived 'World'
+            }
+
+        when:
+            context.start()
+
+            withTemplate {
+                to('direct:start').withBody('World').send()
+            }
+
+        then:
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
+}


(camel) 05/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 80448bfe267b87ed6afd574b97db426d0f8d6a98
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Jan 20 10:04:07 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../src/generated/resources/org/apache/camel/catalog/models/from.json   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/from.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/from.json
index b753ab0fa4f..8eee8ec59c0 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/from.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/from.json
@@ -15,6 +15,6 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "uri": { "index": 2, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the URI of the endpoint to use" },
-    "variable": { "index": 3, "kind": "attribute", "displayName": "Variable", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store a copy of the incoming message body (only body, not headers). This is handy for easy access to the incoming message body via variables." }
+    "variableReceive": { "index": 3, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store a copy of the received message body (only body, not headers). This is handy for easy access to the received message body via variables." }
   }
 }


(camel) 15/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 7320558aeeac4c0c45fd993e15eae2c0c83d0a32
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Jan 23 13:28:19 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../resources/org/apache/camel/model/marshal.json  |   4 +-
 .../org/apache/camel/model/unmarshal.json          |   4 +-
 .../org/apache/camel/builder/DataFormatClause.java |  43 +++++++-
 .../org/apache/camel/model/MarshalDefinition.java  |  48 +++++++++
 .../apache/camel/model/UnmarshalDefinition.java    |  44 +++++++++
 .../org/apache/camel/reifier/MarshalReifier.java   |   5 +-
 .../org/apache/camel/reifier/UnmarshalReifier.java |   6 +-
 .../camel/processor/MarshalVariableTest.java       | 109 +++++++++++++++++++++
 .../camel/processor/UnmarshalVariableTest.java     | 107 ++++++++++++++++++++
 .../api/management/mbean/ManagedMarshalMBean.java  |   6 ++
 .../management/mbean/ManagedUnmarshalMBean.java    |   9 ++
 .../camel/management/mbean/ManagedMarshal.java     |  10 ++
 .../camel/management/mbean/ManagedUnmarshal.java   |  15 +++
 .../camel/support/processor/MarshalProcessor.java  |  34 ++++++-
 .../support/processor/UnmarshalProcessor.java      |  53 ++++++++--
 .../java/org/apache/camel/xml/in/ModelParser.java  |  20 ++--
 .../java/org/apache/camel/xml/out/ModelWriter.java |   4 +
 .../org/apache/camel/yaml/out/ModelWriter.java     |   4 +
 18 files changed, 503 insertions(+), 22 deletions(-)

diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/marshal.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/marshal.json
index 178597ebd63..3f33b46e27e 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/marshal.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/marshal.json
@@ -15,6 +15,8 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "dataFormatType": { "index": 3, "kind": "element", "displayName": "Data Format Type", "required": true, "type": "object", "javaType": "org.apache.camel.model.DataFormatDefinition", "oneOf": [ "asn1", "avro", "barcode", "base64", "bindy", "cbor", "crypto", "csv", "custom", "fhirJson", "fhirXml", "flatpack", "grok", "gzipDeflater", "hl7", "ical", "jacksonXml", "jaxb", "json", "jsonApi", "lzf", "mimeMultipart", "parquetAvro", "pgp", "protobuf", "rss", "soap", "swiftMt", "swiftMx", "sysl [...]
+    "dataFormatType": { "index": 3, "kind": "element", "displayName": "Data Format Type", "required": true, "type": "object", "javaType": "org.apache.camel.model.DataFormatDefinition", "oneOf": [ "asn1", "avro", "barcode", "base64", "bindy", "cbor", "crypto", "csv", "custom", "fhirJson", "fhirXml", "flatpack", "grok", "gzipDeflater", "hl7", "ical", "jacksonXml", "jaxb", "json", "jsonApi", "lzf", "mimeMultipart", "parquetAvro", "pgp", "protobuf", "rss", "soap", "swiftMt", "swiftMx", "sysl [...]
+    "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/unmarshal.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/unmarshal.json
index fe21bf9f735..202b7a8f74b 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/unmarshal.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/unmarshal.json
@@ -16,6 +16,8 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "dataFormatType": { "index": 3, "kind": "element", "displayName": "Data Format Type", "required": true, "type": "object", "javaType": "org.apache.camel.model.DataFormatDefinition", "oneOf": [ "asn1", "avro", "barcode", "base64", "bindy", "cbor", "crypto", "csv", "custom", "fhirJson", "fhirXml", "flatpack", "grok", "gzipDeflater", "hl7", "ical", "jacksonXml", "jaxb", "json", "jsonApi", "lzf", "mimeMultipart", "parquetAvro", "pgp", "protobuf", "rss", "soap", "swiftMt", "swiftMx", "sysl [...]
-    "allowNullBody": { "index": 4, "kind": "attribute", "displayName": "Allow Null Body", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Indicates whether null is allowed as value of a body to unmarshall." }
+    "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
+    "allowNullBody": { "index": 6, "kind": "attribute", "displayName": "Allow Null Body", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Indicates whether null is allowed as value of a body to unmarshall." }
   }
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/DataFormatClause.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/DataFormatClause.java
index 59f2e1649e4..73c6d1eb97a 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/DataFormatClause.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/DataFormatClause.java
@@ -21,7 +21,9 @@ import java.util.Map;
 import org.w3c.dom.Node;
 
 import org.apache.camel.model.DataFormatDefinition;
+import org.apache.camel.model.MarshalDefinition;
 import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.model.UnmarshalDefinition;
 import org.apache.camel.model.dataformat.ASN1DataFormat;
 import org.apache.camel.model.dataformat.AvroDataFormat;
 import org.apache.camel.model.dataformat.AvroLibrary;
@@ -69,6 +71,8 @@ import org.apache.camel.support.jsse.KeyStoreParameters;
 public class DataFormatClause<T extends ProcessorDefinition<?>> {
     private final T processorType;
     private final Operation operation;
+    private String variableSend;
+    private String variableReceive;
     private boolean allowNullBody;
 
     /**
@@ -1349,13 +1353,48 @@ public class DataFormatClause<T extends ProcessorDefinition<?>> {
         return this;
     }
 
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public DataFormatClause<T> variableSend(String variableSend) {
+        this.variableSend = variableSend;
+        return this;
+    }
+
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public DataFormatClause<T> variableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+        return this;
+    }
+
     @SuppressWarnings("unchecked")
     private T dataFormat(DataFormatDefinition dataFormatType) {
         switch (operation) {
             case Unmarshal:
-                return (T) processorType.unmarshal(dataFormatType, allowNullBody);
+                UnmarshalDefinition unmarshal = new UnmarshalDefinition(dataFormatType);
+                if (allowNullBody) {
+                    unmarshal.allowNullBody(true);
+                }
+                unmarshal.setVariableReceive(variableReceive);
+                unmarshal.setVariableSend(variableSend);
+                processorType.addOutput(unmarshal);
+                return processorType;
             case Marshal:
-                return (T) processorType.marshal(dataFormatType);
+                MarshalDefinition marshal = new MarshalDefinition(dataFormatType);
+                marshal.setVariableReceive(variableReceive);
+                marshal.setVariableSend(variableSend);
+                processorType.addOutput(marshal);
+                return processorType;
             default:
                 throw new IllegalArgumentException("Unknown DataFormat operation: " + operation);
         }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/MarshalDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/MarshalDefinition.java
index 0e87066c92d..75bc403dd83 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/MarshalDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/MarshalDefinition.java
@@ -18,6 +18,7 @@ package org.apache.camel.model;
 
 import jakarta.xml.bind.annotation.XmlAccessType;
 import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlAttribute;
 import jakarta.xml.bind.annotation.XmlElement;
 import jakarta.xml.bind.annotation.XmlElements;
 import jakarta.xml.bind.annotation.XmlRootElement;
@@ -114,6 +115,10 @@ public class MarshalDefinition extends NoOutputDefinition<MarshalDefinition> imp
             @XmlElement(name = "zipDeflater", type = ZipDeflaterDataFormat.class),
             @XmlElement(name = "zipFile", type = ZipFileDataFormat.class) })
     private DataFormatDefinition dataFormatType;
+    @XmlAttribute
+    private String variableSend;
+    @XmlAttribute
+    private String variableReceive;
 
     public MarshalDefinition() {
     }
@@ -154,4 +159,47 @@ public class MarshalDefinition extends NoOutputDefinition<MarshalDefinition> imp
         this.dataFormatType = dataFormatType;
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
+    // Fluent API
+    // -------------------------------------------------------------------------
+
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public MarshalDefinition variableReceive(String variableReceive) {
+        setVariableReceive(variableReceive);
+        return this;
+    }
+
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public MarshalDefinition variableSend(String variableSend) {
+        setVariableSend(variableSend);
+        return this;
+    }
+
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/UnmarshalDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/UnmarshalDefinition.java
index f948dc85819..cb603a8d004 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/UnmarshalDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/UnmarshalDefinition.java
@@ -116,6 +116,10 @@ public class UnmarshalDefinition extends NoOutputDefinition<UnmarshalDefinition>
             @XmlElement(name = "zipFile", type = ZipFileDataFormat.class) })
     private DataFormatDefinition dataFormatType;
     @XmlAttribute
+    private String variableSend;
+    @XmlAttribute
+    private String variableReceive;
+    @XmlAttribute
     @Metadata(label = "advanced", javaType = "java.lang.Boolean", defaultValue = "false")
     private String allowNullBody;
 
@@ -162,6 +166,22 @@ public class UnmarshalDefinition extends NoOutputDefinition<UnmarshalDefinition>
         this.dataFormatType = dataFormatType;
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     public String getAllowNullBody() {
         return allowNullBody;
     }
@@ -176,6 +196,30 @@ public class UnmarshalDefinition extends NoOutputDefinition<UnmarshalDefinition>
     // Fluent API
     // -------------------------------------------------------------------------
 
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public UnmarshalDefinition variableReceive(String variableReceive) {
+        setVariableReceive(variableReceive);
+        return this;
+    }
+
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public UnmarshalDefinition variableSend(String variableSend) {
+        setVariableSend(variableSend);
+        return this;
+    }
+
     /**
      * Indicates whether {@code null} is allowed as value of a body to unmarshall.
      *
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/MarshalReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/MarshalReifier.java
index dc79f718ce4..e39e96ea9cc 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/MarshalReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/MarshalReifier.java
@@ -33,7 +33,10 @@ public class MarshalReifier extends ProcessorReifier<MarshalDefinition> {
     @Override
     public Processor createProcessor() {
         DataFormat dataFormat = DataFormatReifier.getDataFormat(camelContext, definition.getDataFormatType());
-        return new MarshalProcessor(dataFormat);
+        MarshalProcessor answer = new MarshalProcessor(dataFormat);
+        answer.setVariableSend(parseString(definition.getVariableSend()));
+        answer.setVariableReceive(parseString(definition.getVariableReceive()));
+        return answer;
     }
 
 }
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/UnmarshalReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/UnmarshalReifier.java
index 025fed2a1c6..f1bd1fc79ef 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/UnmarshalReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/UnmarshalReifier.java
@@ -33,6 +33,10 @@ public class UnmarshalReifier extends ProcessorReifier<UnmarshalDefinition> {
     @Override
     public Processor createProcessor() {
         DataFormat dataFormat = DataFormatReifier.getDataFormat(camelContext, definition.getDataFormatType());
-        return new UnmarshalProcessor(dataFormat, Boolean.TRUE == parseBoolean(definition.getAllowNullBody()));
+        UnmarshalProcessor answer
+                = new UnmarshalProcessor(dataFormat, Boolean.TRUE == parseBoolean(definition.getAllowNullBody()));
+        answer.setVariableSend(parseString(definition.getVariableSend()));
+        answer.setVariableReceive(parseString(definition.getVariableReceive()));
+        return answer;
     }
 }
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/MarshalVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/MarshalVariableTest.java
new file mode 100644
index 00000000000..55f140697d7
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/MarshalVariableTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.processor;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.support.service.ServiceSupport;
+import org.junit.jupiter.api.Test;
+
+public class MarshalVariableTest extends ContextTestSupport {
+
+    @Test
+    public void testSend() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye Camel");
+        getMockEndpoint("mock:result").expectedVariableReceived("hello", "Camel");
+
+        template.sendBody("direct:send", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testReceive() throws Exception {
+        getMockEndpoint("mock:after").expectedBodiesReceived("World");
+        getMockEndpoint("mock:after").expectedVariableReceived("bye", "Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye World");
+
+        template.sendBody("direct:receive", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testSendAndReceive() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye Camel");
+
+        template.sendBody("direct:sendAndReceive", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                context.getRegistry().bind("myDF", new MyByeDataFormat());
+
+                from("direct:send")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .marshal().variableSend("hello").custom("myDF")
+                        .to("mock:result");
+
+                from("direct:receive")
+                        .marshal().variableReceive("bye").custom("myDF")
+                        .to("mock:after")
+                        .setBody(simple("${variable:bye}"))
+                        .to("mock:result");
+
+                from("direct:sendAndReceive")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .marshal().variableSend("hello").variableReceive("bye").custom("myDF")
+                        .to("mock:result");
+            }
+        };
+    }
+
+    public static class MyByeDataFormat extends ServiceSupport implements DataFormat {
+
+        @Override
+        public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
+            String line = "Bye " + graph.toString();
+            stream.write(line.getBytes());
+        }
+
+        @Override
+        public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
+            // noop
+            return null;
+        }
+    }
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/UnmarshalVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/UnmarshalVariableTest.java
new file mode 100644
index 00000000000..7eb9aeecd9a
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/UnmarshalVariableTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.processor;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.support.service.ServiceSupport;
+import org.junit.jupiter.api.Test;
+
+public class UnmarshalVariableTest extends ContextTestSupport {
+
+    @Test
+    public void testSend() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye Camel");
+        getMockEndpoint("mock:result").expectedVariableReceived("hello", "Camel");
+
+        template.sendBody("direct:send", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testReceive() throws Exception {
+        getMockEndpoint("mock:after").expectedBodiesReceived("World");
+        getMockEndpoint("mock:after").expectedVariableReceived("bye", "Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye World");
+
+        template.sendBody("direct:receive", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testSendAndReceive() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye Camel");
+
+        template.sendBody("direct:sendAndReceive", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                context.getRegistry().bind("myDF", new MyByeDataFormat());
+
+                from("direct:send")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .unmarshal().variableSend("hello").custom("myDF")
+                        .to("mock:result");
+
+                from("direct:receive")
+                        .unmarshal().variableReceive("bye").custom("myDF")
+                        .to("mock:after")
+                        .setBody(simple("${variable:bye}"))
+                        .to("mock:result");
+
+                from("direct:sendAndReceive")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .unmarshal().variableSend("hello").variableReceive("bye").custom("myDF")
+                        .to("mock:result");
+            }
+        };
+    }
+
+    public static class MyByeDataFormat extends ServiceSupport implements DataFormat {
+
+        @Override
+        public void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
+            // noop
+        }
+
+        @Override
+        public Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
+            return "Bye " + exchange.getContext().getTypeConverter().convertTo(String.class, exchange, stream);
+        }
+    }
+}
diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedMarshalMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedMarshalMBean.java
index dc6a8144cb5..9d8e8684581 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedMarshalMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedMarshalMBean.java
@@ -23,4 +23,10 @@ public interface ManagedMarshalMBean extends ManagedProcessorMBean {
     @ManagedAttribute(description = "The name of the DataFormat to use for marshal")
     String getDataFormatName();
 
+    @ManagedAttribute(description = "Variable as the source for the message body to send")
+    String getVariableSend();
+
+    @ManagedAttribute(description = "Variable to store the received message body (only body, not headers)")
+    String getVariableReceive();
+
 }
diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedUnmarshalMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedUnmarshalMBean.java
index 550fcec168c..88ac0aa9991 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedUnmarshalMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedUnmarshalMBean.java
@@ -23,4 +23,13 @@ public interface ManagedUnmarshalMBean extends ManagedProcessorMBean {
     @ManagedAttribute(description = "The name of the DataFormat to use for unmarshal")
     String getDataFormatName();
 
+    @ManagedAttribute(description = "Variable as the source for the message body to send")
+    String getVariableSend();
+
+    @ManagedAttribute(description = "Variable to store the received message body (only body, not headers)")
+    String getVariableReceive();
+
+    @ManagedAttribute(description = "Whether null is allowed as value of a body to unmarshall")
+    boolean isAllowNullBody();
+
 }
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedMarshal.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedMarshal.java
index 40745fc2674..1722cc94c80 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedMarshal.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedMarshal.java
@@ -44,4 +44,14 @@ public class ManagedMarshal extends ManagedProcessor implements ManagedMarshalMB
         }
         return name;
     }
+
+    @Override
+    public String getVariableSend() {
+        return processor.getVariableSend();
+    }
+
+    @Override
+    public String getVariableReceive() {
+        return processor.getVariableReceive();
+    }
 }
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedUnmarshal.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedUnmarshal.java
index cdfd71ac1f6..90159743815 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedUnmarshal.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedUnmarshal.java
@@ -44,4 +44,19 @@ public class ManagedUnmarshal extends ManagedProcessor implements ManagedUnmarsh
         }
         return name;
     }
+
+    @Override
+    public String getVariableSend() {
+        return processor.getVariableSend();
+    }
+
+    @Override
+    public String getVariableReceive() {
+        return processor.getVariableReceive();
+    }
+
+    @Override
+    public boolean isAllowNullBody() {
+        return processor.isAllowNullBody();
+    }
 }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/processor/MarshalProcessor.java b/core/camel-support/src/main/java/org/apache/camel/support/processor/MarshalProcessor.java
index 0c116c40e54..f1a299106ef 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/processor/MarshalProcessor.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/processor/MarshalProcessor.java
@@ -26,6 +26,7 @@ import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.IdAware;
 import org.apache.camel.spi.RouteIdAware;
 import org.apache.camel.support.AsyncProcessorSupport;
+import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.builder.OutputStreamBuilder;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.ObjectHelper;
@@ -39,6 +40,8 @@ public class MarshalProcessor extends AsyncProcessorSupport implements Traceable
     private String routeId;
     private CamelContext camelContext;
     private final DataFormat dataFormat;
+    private String variableSend;
+    private String variableReceive;
 
     public MarshalProcessor(DataFormat dataFormat) {
         this.dataFormat = dataFormat;
@@ -53,7 +56,12 @@ public class MarshalProcessor extends AsyncProcessorSupport implements Traceable
         OutputStreamBuilder osb = OutputStreamBuilder.withExchange(exchange);
 
         Message in = exchange.getIn();
-        Object body = in.getBody();
+        final Object originalBody = in.getBody();
+        Object body = originalBody;
+        if (variableSend != null) {
+            // it may be a global variable
+            body = ExchangeHelper.getVariable(exchange, variableSend);
+        }
 
         // lets setup the out message before we invoke the dataFormat
         // so that it can mutate it if necessary
@@ -62,7 +70,13 @@ public class MarshalProcessor extends AsyncProcessorSupport implements Traceable
 
         try {
             dataFormat.marshal(exchange, body, osb);
-            out.setBody(osb.build());
+            Object result = osb.build();
+            // result should be stored in variable instead of message body
+            if (variableReceive != null) {
+                ExchangeHelper.setVariable(exchange, variableReceive, result);
+            } else {
+                out.setBody(result);
+            }
         } catch (Exception e) {
             // remove OUT message, as an exception occurred
             exchange.setOut(null);
@@ -113,6 +127,22 @@ public class MarshalProcessor extends AsyncProcessorSupport implements Traceable
         this.camelContext = camelContext;
     }
 
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     @Override
     protected void doStart() throws Exception {
         // inject CamelContext on data format
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/processor/UnmarshalProcessor.java b/core/camel-support/src/main/java/org/apache/camel/support/processor/UnmarshalProcessor.java
index 25e2acba8ac..dbfb9e7206c 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/processor/UnmarshalProcessor.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/processor/UnmarshalProcessor.java
@@ -30,6 +30,7 @@ import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.IdAware;
 import org.apache.camel.spi.RouteIdAware;
 import org.apache.camel.support.AsyncProcessorSupport;
+import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
@@ -44,6 +45,8 @@ public class UnmarshalProcessor extends AsyncProcessorSupport implements Traceab
     private CamelContext camelContext;
     private final DataFormat dataFormat;
     private final boolean allowNullBody;
+    private String variableSend;
+    private String variableReceive;
 
     public UnmarshalProcessor(DataFormat dataFormat) {
         this(dataFormat, false);
@@ -62,17 +65,20 @@ public class UnmarshalProcessor extends AsyncProcessorSupport implements Traceab
         Object result = null;
         try {
             final Message in = exchange.getIn();
+            final Object originalBody = in.getBody();
+            Object body = originalBody;
+            if (variableSend != null) {
+                // it may be a global variable
+                body = ExchangeHelper.getVariable(exchange, variableSend);
+            }
             final Message out;
-            if (allowNullBody && in.getBody() == null) {
+            if (allowNullBody && body == null) {
                 // The body is null, and it is an allowed value so let's skip the unmarshalling
                 out = exchange.getOut();
             } else {
-                Object body = in.getBody();
-
                 // lets set up the out message before we invoke the dataFormat so that it can mutate it if necessary
                 out = exchange.getOut();
                 out.copyFrom(in);
-
                 result = dataFormat.unmarshal(exchange, body);
             }
             if (result instanceof Exchange) {
@@ -82,11 +88,22 @@ public class UnmarshalProcessor extends AsyncProcessorSupport implements Traceab
                             "The returned exchange " + result + " is not the same as " + exchange
                                                     + " provided to the DataFormat");
                 }
-            } else if (result instanceof Message) {
-                // the dataformat has probably set headers, attachments, etc. so let's use it as the outbound payload
-                exchange.setOut((Message) result);
+            } else if (result instanceof Message msg) {
+                // result should be stored in variable instead of message body
+                if (variableReceive != null) {
+                    Object value = msg.getBody();
+                    ExchangeHelper.setVariable(exchange, variableReceive, value);
+                } else {
+                    // the dataformat has probably set headers, attachments, etc. so let's use it as the outbound payload
+                    exchange.setOut(msg);
+                }
             } else {
-                out.setBody(result);
+                // result should be stored in variable instead of message body
+                if (variableReceive != null) {
+                    ExchangeHelper.setVariable(exchange, variableReceive, result);
+                } else {
+                    out.setBody(result);
+                }
             }
         } catch (Exception e) {
             // remove OUT message, as an exception occurred
@@ -142,6 +159,26 @@ public class UnmarshalProcessor extends AsyncProcessorSupport implements Traceab
         this.camelContext = camelContext;
     }
 
+    public boolean isAllowNullBody() {
+        return allowNullBody;
+    }
+
+    public String getVariableSend() {
+        return variableSend;
+    }
+
+    public void setVariableSend(String variableSend) {
+        this.variableSend = variableSend;
+    }
+
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     @Override
     protected void doStart() throws Exception {
         // inject CamelContext on data format
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 a976bf9ba52..8d9af8a709d 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
@@ -603,8 +603,14 @@ public class ModelParser extends BaseParser {
         }, outputExpressionNodeElementHandler(), noValueHandler());
     }
     protected MarshalDefinition doParseMarshalDefinition() throws IOException, XmlPullParserException {
-        return doParse(new MarshalDefinition(),
-            processorDefinitionAttributeHandler(), (def, key) -> {
+        return doParse(new MarshalDefinition(), (def, key, val) -> {
+            switch (key) {
+                case "variableReceive": def.setVariableReceive(val); break;
+                case "variableSend": def.setVariableSend(val); break;
+                default: return processorDefinitionAttributeHandler().accept(def, key, val);
+            }
+            return true;
+        }, (def, key) -> {
             DataFormatDefinition v = doParseDataFormatDefinitionRef(key);
             if (v != null) { 
                 def.setDataFormatType(v);
@@ -1593,11 +1599,13 @@ public class ModelParser extends BaseParser {
     }
     protected UnmarshalDefinition doParseUnmarshalDefinition() throws IOException, XmlPullParserException {
         return doParse(new UnmarshalDefinition(), (def, key, val) -> {
-            if ("allowNullBody".equals(key)) {
-                def.setAllowNullBody(val);
-                return true;
+            switch (key) {
+                case "allowNullBody": def.setAllowNullBody(val); break;
+                case "variableReceive": def.setVariableReceive(val); break;
+                case "variableSend": def.setVariableSend(val); break;
+                default: return processorDefinitionAttributeHandler().accept(def, key, val);
             }
-            return processorDefinitionAttributeHandler().accept(def, key, val);
+            return true;
         }, (def, key) -> {
             DataFormatDefinition v = doParseDataFormatDefinitionRef(key);
             if (v != null) { 
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index dafa67a7831..f87189f2681 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -1540,6 +1540,8 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteElement(null, def.getDataFormatType(), (n, v) -> {
             switch (v.getClass().getSimpleName()) {
                 case "ASN1DataFormat" -> doWriteASN1DataFormat("asn1", (ASN1DataFormat) def.getDataFormatType());
@@ -2493,6 +2495,8 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteAttribute("allowNullBody", def.getAllowNullBody());
         doWriteElement(null, def.getDataFormatType(), (n, v) -> {
             switch (v.getClass().getSimpleName()) {
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index 7d60741bd7b..fc9f63d4dfe 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -1540,6 +1540,8 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteElement(null, def.getDataFormatType(), (n, v) -> {
             switch (v.getClass().getSimpleName()) {
                 case "ASN1DataFormat" -> doWriteASN1DataFormat("asn1", (ASN1DataFormat) def.getDataFormatType());
@@ -2493,6 +2495,8 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
+        doWriteAttribute("variableSend", def.getVariableSend());
         doWriteAttribute("allowNullBody", def.getAllowNullBody());
         doWriteElement(null, def.getDataFormatType(), (n, v) -> {
             switch (v.getClass().getSimpleName()) {


(camel) 08/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ab4671a57289dd2dd05eacb0850ab7a55a827b68
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Jan 20 16:17:43 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 docs/user-manual/modules/ROOT/pages/variables.adoc | 276 +++++++++++++++++++++
 1 file changed, 276 insertions(+)

diff --git a/docs/user-manual/modules/ROOT/pages/variables.adoc b/docs/user-manual/modules/ROOT/pages/variables.adoc
index 275369402bd..da4ec933221 100644
--- a/docs/user-manual/modules/ROOT/pages/variables.adoc
+++ b/docs/user-manual/modules/ROOT/pages/variables.adoc
@@ -169,3 +169,279 @@ as shown below:
 ----
 camel.variable.user-template = resource:file:/etc/user.json
 ----
+
+== Using Variables with EIPs
+
+The most commonly used EIPs for sending and receiving messages (`from`, `to`, `toD`, `enrich` and `pollEnrich`) have
+first class support for using variables with the message body.
+
+The intention is to make it more convenient and easy to _gather data_ from other systems without any ceremony to keep
+existing data by using techniques such as storing the data temporary using headers or exchange properties,
+or with the xref:components:eips:claimCheck-eip.adoc[Claim Check] EIP.
+
+It is **important** to understand that the variables only use the message **body** and does not have support for anything else such
+as message headers. This is on purpose to keep it simpler and only work with the message body as the user data. If you have need
+to use variables with both message body and headers, then you can use `setVariable` and `getVariable`.
+
+=== Using variable to store a copy of the incoming message body
+
+You can configure the `from` to store a copy of the message body into a variable. This makes it easy to have quick access
+to the original incoming message body via the variable.
+
+The following example from a unit test shows how to do this. Notice how Java DSL uses `fromV` to make it possible to specify
+the name of the variable. In XML and YAML DSL you specify this using the `variableReceive` parameter.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+fromV("direct:start", "myKey")
+    .transform().simple("Bye ${body}")
+    .to("mock:foo")
+    .setBody(simple("${variable:myKey}"))
+    .to("mock:result");
+----
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:start" variableReceive="myKey"/>
+  <transform><simple>Bye ${body}</simple></transform>
+  <to uri="mock:foo"/>
+  <setBody>
+    <simple>${variable:myKey}</simple>
+  </setBody>
+  <to uri="mock:result"/>
+</route>
+----
+YAML::
++
+[source,yaml]
+----
+from:
+  uri: "direct:start"
+  variableReceive: "myKey"
+  steps:
+    - transform:
+        simple: "Bye ${body}"
+    - to: "mock:foo"
+    - setBody:
+        simple: "${variable:myKey}"
+    - to: "mock:result"
+----
+====
+
+=== Using variables when sending and receiving messages to an endpoint
+
+You can configure the `to` to use variables for any of the following (or both) when sending and receiving:
+
+- variableSend - name of variable that contains the message body to send instead of the current message body on the `Exchange`.
+- variableReceive - name of variable that should store the returned message payload (will not change the message body on the `Exchange`).
+
+For example, you can use the `variableSend` to tell Camel to use the variable as the message body when sending to an endpoint.
+If the `variableReceive` is also configured, then the reply message will be stored in the variable instead of the `Exchange` message body.
+
+IMPORTANT: This is only the message body. Message headers keep acting as usual.
+
+In the following example, we use a variable named `hello` as the message body when sending to the `direct:foo` endpoint:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:send")
+    .setVariable("hello", simple("Camel"))
+    .to("mock:before")
+    .toV("direct:foo", "hello", null)
+    .to("mock:result");
+
+from("direct:foo")
+    .transform().simple("Bye ${body}");
+----
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:send"/>
+  <setVariable name="hello">
+    <simple>Camel</simple>
+  </setVariable>
+  <to uri="mock:before"/>
+  <to uri="direct:foo" variableSend="hello"/>
+  <to uri="mock:result"/>
+</route>
+<route>
+  <from uri="direct:foo"/>
+  <transform>
+    <simple>Bye ${body}</simple>
+  </transform>
+</route>
+----
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:send
+      steps:
+        - setVariable:
+            name: hello
+            simple:
+              expression: Camel
+        - to:
+            uri: mock:before
+        - to:
+            uri: direct:foo
+            variableSend: hello
+        - to:
+            uri: mock:result
+- route:
+    from:
+      uri: direct:foo
+      steps:
+        - transform:
+            simple:
+              expression: "Bye ${body}"
+----
+====
+
+If you only want to store the result in a variable instead of the current `Exchange` message body, then you should use `variableReceive`
+as shown in the following:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:receive")
+    .toV("direct:foo", null, "bye")
+    .to("mock:after")
+    .setBody(simple("${variable:bye}"))
+    .to("mock:result");
+
+from("direct:foo")
+    .transform().simple("Bye ${body}");
+----
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:receive"/>
+  <to uri="direct:foo" variableReceive="bye"/>
+  <to uri="mock:after"/>
+  <setBody>
+    <simple>${variable:bye}</simple>
+  </setBody>
+  <to uri="mock:result"/>
+</route>
+<route>
+  <from uri="direct:foo"/>
+  <transform>
+    <simple>Bye ${body}</simple>
+  </transform>
+</route>
+----
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:receive
+      steps:
+        - to:
+            uri: direct:foo
+            variableReceive: bye
+        - to:
+            uri: mock:after
+        - setBody:
+            simple:
+              expression: "${variable:bye}"
+        - to:
+            uri: mock:result
+- route:
+    from:
+      uri: direct:foo
+      steps:
+        - transform:
+            simple:
+              expression: "Bye ${body}"
+----
+====
+
+And you can also use both of them together which means you are using variables for both what to send, and to store the result in a variable.
+This means the current `Exchange` message body is not in use at all.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:sendAndReceive")
+    .setVariable("hello", simple("Camel"))
+    .to("mock:before")
+    .toV("direct:foo", "hello", "bye")
+    .to("mock:result");
+
+from("direct:foo")
+    .transform().simple("Bye ${body}");
+----
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="direct:sendAndReceive"/>
+  <setVariable name="hello">
+    <simple>Camel</simple>
+  </setVariable>
+  <to uri="mock:before"/>
+  <to uri="direct:foo" variableSend="hello" variableReceive="bye"/>
+  <to uri="mock:result"/>
+</route>
+<route>
+  <from uri="direct:foo"/>
+  <transform>
+    <simple>Bye ${body}</simple>
+  </transform>
+</route>
+----
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:sendAndReceive
+      steps:
+        - setVariable:
+            name: hello
+            simple:
+              expression: Camel
+        - to:
+            uri: mock:before
+        - to:
+            uri: direct:foo
+            variableReceive: bye
+            variableSend: hello
+        - to:
+            uri: mock:result
+- route:
+    from:
+      uri: direct:foo
+      steps:
+        - transform:
+            simple:
+              expression: "Bye ${body}"
+----
+====


(camel) 17/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 75277dcbc08b0a4c67abe57cb077c869ccf1fd15
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Jan 23 14:16:17 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../org/apache/camel/catalog/languages.properties  |   1 +
 .../apache/camel/catalog/languages/variable.json   |  23 ++++
 .../org/apache/camel/catalog/models.properties     |   1 +
 .../org/apache/camel/catalog/models/aggregate.json |   8 +-
 .../org/apache/camel/catalog/models/delay.json     |   2 +-
 .../apache/camel/catalog/models/dynamicRouter.json |   2 +-
 .../org/apache/camel/catalog/models/enrich.json    |   2 +-
 .../org/apache/camel/catalog/models/filter.json    |   2 +-
 .../camel/catalog/models/idempotentConsumer.json   |   2 +-
 .../org/apache/camel/catalog/models/loop.json      |   2 +-
 .../apache/camel/catalog/models/onException.json   |   6 +-
 .../apache/camel/catalog/models/pollEnrich.json    |   2 +-
 .../camel/catalog/models/propertyExpression.json   |   2 +-
 .../apache/camel/catalog/models/recipientList.json |   2 +-
 .../apache/camel/catalog/models/resequence.json    |   2 +-
 .../apache/camel/catalog/models/routingSlip.json   |   2 +-
 .../org/apache/camel/catalog/models/script.json    |   2 +-
 .../org/apache/camel/catalog/models/setBody.json   |   2 +-
 .../org/apache/camel/catalog/models/setHeader.json |   2 +-
 .../apache/camel/catalog/models/setProperty.json   |   2 +-
 .../apache/camel/catalog/models/setVariable.json   |   2 +-
 .../org/apache/camel/catalog/models/sort.json      |   2 +-
 .../org/apache/camel/catalog/models/split.json     |   2 +-
 .../org/apache/camel/catalog/models/sticky.json    |   2 +-
 .../org/apache/camel/catalog/models/throttle.json  |   4 +-
 .../org/apache/camel/catalog/models/transform.json |   2 +-
 .../org/apache/camel/catalog/models/validate.json  |   2 +-
 .../org/apache/camel/catalog/models/variable.json  |  20 ++++
 .../org/apache/camel/catalog/models/when.json      |   2 +-
 .../catalog/models/whenSkipSendToEndpoint.json     |   2 +-
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  39 +++++++
 .../camel/spring/processor/ToVariableTest.xml      |   2 +-
 .../services/org/apache/camel/language.properties  |   2 +-
 .../services/org/apache/camel/language/variable    |   2 +
 .../apache/camel/language/variable/variable.json   |  23 ++++
 .../modules/languages/pages/variable-language.adoc |  52 +++++++++
 .../camel/language/variable/VariableLanguage.java  |  50 +++++++++
 .../services/org/apache/camel/model.properties     |   1 +
 .../org/apache/camel/model/aggregate.json          |   8 +-
 .../resources/org/apache/camel/model/delay.json    |   2 +-
 .../org/apache/camel/model/dynamicRouter.json      |   2 +-
 .../resources/org/apache/camel/model/enrich.json   |   2 +-
 .../resources/org/apache/camel/model/filter.json   |   2 +-
 .../org/apache/camel/model/idempotentConsumer.json |   2 +-
 .../org/apache/camel/model/language/jaxb.index     |   1 +
 .../org/apache/camel/model/language/variable.json  |  20 ++++
 .../apache/camel/model/loadbalancer/sticky.json    |   2 +-
 .../resources/org/apache/camel/model/loop.json     |   2 +-
 .../org/apache/camel/model/onException.json        |   6 +-
 .../org/apache/camel/model/pollEnrich.json         |   2 +-
 .../org/apache/camel/model/propertyExpression.json |   2 +-
 .../org/apache/camel/model/recipientList.json      |   2 +-
 .../org/apache/camel/model/resequence.json         |   2 +-
 .../org/apache/camel/model/routingSlip.json        |   2 +-
 .../resources/org/apache/camel/model/script.json   |   2 +-
 .../resources/org/apache/camel/model/setBody.json  |   2 +-
 .../org/apache/camel/model/setHeader.json          |   2 +-
 .../org/apache/camel/model/setProperty.json        |   2 +-
 .../org/apache/camel/model/setVariable.json        |   2 +-
 .../resources/org/apache/camel/model/sort.json     |   2 +-
 .../resources/org/apache/camel/model/split.json    |   2 +-
 .../resources/org/apache/camel/model/throttle.json |   4 +-
 .../org/apache/camel/model/transform.json          |   2 +-
 .../resources/org/apache/camel/model/validate.json |   2 +-
 .../resources/org/apache/camel/model/when.json     |   2 +-
 .../apache/camel/model/whenSkipSendToEndpoint.json |   2 +-
 .../java/org/apache/camel/builder/Builder.java     |   9 ++
 .../org/apache/camel/builder/BuilderSupport.java   |   9 ++
 .../camel/builder/ExpressionClauseSupport.java     |   8 ++
 .../camel/builder/LanguageBuilderFactory.java      |   8 ++
 .../camel/model/language/VariableExpression.java   |  61 +++++++++++
 .../org/apache/camel/language/VariableTest.java    |  52 +++++++++
 .../org/apache/camel/processor/ToVariableTest.java |   2 +-
 .../org/apache/camel/main/languages.properties     |   1 +
 .../java/org/apache/camel/xml/in/ModelParser.java  |   5 +
 .../java/org/apache/camel/xml/out/ModelWriter.java |  15 +++
 .../org/apache/camel/yaml/out/ModelWriter.java     |  15 +++
 .../deserializers/ExpressionDeserializers.java     |   5 +
 .../dsl/yaml/deserializers/ModelDeserializers.java |  67 ++++++++++++
 .../deserializers/ModelDeserializersResolver.java  |   2 +
 .../generated/resources/schema/camelYamlDsl.json   | 117 +++++++++++++++++++++
 .../apache/camel/dsl/yaml/ToVariableTest.groovy    |   9 +-
 82 files changed, 677 insertions(+), 73 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages.properties
index 86fcea79981..c478818f78c 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages.properties
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages.properties
@@ -19,6 +19,7 @@ ref
 simple
 spel
 tokenize
+variable
 xpath
 xquery
 xtokenize
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/variable.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/variable.json
new file mode 100644
index 00000000000..2b6b713c321
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/languages/variable.json
@@ -0,0 +1,23 @@
+{
+  "language": {
+    "kind": "language",
+    "name": "variable",
+    "title": "Variable",
+    "description": "Gets a variable",
+    "deprecated": false,
+    "firstVersion": "4.4.0",
+    "label": "language,core",
+    "javaType": "org.apache.camel.language.variable.VariableLanguage",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-core-languages",
+    "version": "4.4.0-SNAPSHOT",
+    "modelName": "variable",
+    "modelJavaType": "org.apache.camel.model.language.VariableExpression"
+  },
+  "properties": {
+    "id": { "index": 0, "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" },
+    "expression": { "index": 1, "kind": "value", "displayName": "Expression", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax" },
+    "trim": { "index": 2, "kind": "attribute", "displayName": "Trim", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" }
+  }
+}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models.properties
index 4c1e0b75d21..4cc5b5a08aa 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models.properties
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models.properties
@@ -215,6 +215,7 @@ unmarshal
 validate
 validators
 value
+variable
 weighted
 when
 whenSkipSendToEndpoint
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/aggregate.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/aggregate.json
index 2a5e8a5d0f4..146ff687660 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/aggregate.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/aggregate.json
@@ -15,10 +15,10 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "correlationExpression": { "index": 3, "kind": "expression", "displayName": "Correlation Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false,  [...]
-    "completionPredicate": { "index": 4, "kind": "expression", "displayName": "Completion Predicate", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "de [...]
-    "completionTimeoutExpression": { "index": 5, "kind": "expression", "displayName": "Completion Timeout Expression", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", " [...]
-    "completionSizeExpression": { "index": 6, "kind": "expression", "displayName": "Completion Size Expression", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtoken [...]
+    "correlationExpression": { "index": 3, "kind": "expression", "displayName": "Correlation Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecat [...]
+    "completionPredicate": { "index": 4, "kind": "expression", "displayName": "Completion Predicate", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtoke [...]
+    "completionTimeoutExpression": { "index": 5, "kind": "expression", "displayName": "Completion Timeout Expression", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", [...]
+    "completionSizeExpression": { "index": 6, "kind": "expression", "displayName": "Completion Size Expression", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xque [...]
     "optimisticLockRetryPolicy": { "index": 7, "kind": "element", "displayName": "Optimistic Lock Retry Policy", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.OptimisticLockRetryPolicyDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "Allows to configure retry settings when using optimistic locking." },
     "parallelProcessing": { "index": 8, "kind": "attribute", "displayName": "Parallel Processing", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "When aggregated are completed they are being send out of the aggregator. This option indicates whether or not Camel should use a thread pool with multiple threads for concurrency. If no custom thread pool has been specified t [...]
     "optimisticLocking": { "index": 9, "kind": "attribute", "displayName": "Optimistic Locking", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Turns on using optimistic locking, which requires the aggregationRepository being used, is supporting this by implementing org.apache.camel.spi.OptimisticLockingAggregationRepository ." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/delay.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/delay.json
index 4bea1637852..cd76ab10920 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/delay.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/delay.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "asyncDelayed": { "index": 4, "kind": "attribute", "displayName": "Async Delayed", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Enables asynchronous delay which means the thread will not block while delaying." },
     "callerRunsWhenRejected": { "index": 5, "kind": "attribute", "displayName": "Caller Runs When Rejected", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether or not the caller should run the task when it was rejected by the thread pool. Is by default true" },
     "executorService": { "index": 6, "kind": "attribute", "displayName": "Executor Service", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Thread Pool if asyncDelay has been enabled." }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/dynamicRouter.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/dynamicRouter.json
index 20a80eb709a..75315b7fce5 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/dynamicRouter.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/dynamicRouter.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "uriDelimiter": { "index": 4, "kind": "attribute", "displayName": "Uri Delimiter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": ",", "description": "Sets the uri delimiter to use" },
     "ignoreInvalidEndpoints": { "index": 5, "kind": "attribute", "displayName": "Ignore Invalid Endpoints", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
     "cacheSize": { "index": 6, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this dynamic router, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can [...]
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/enrich.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/enrich.json
index e11f1e6a6be..bf05d0bef3a 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/enrich.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/enrich.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
     "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
     "aggregationStrategy": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/filter.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/filter.json
index 208e188fee3..5d5ebe12c54 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/filter.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/filter.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "statusPropertyName": { "index": 4, "kind": "attribute", "displayName": "Status Property Name", "label": "advanced", "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." },
     "outputs": { "index": 5, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.model.ProcessorDefinition<java.lang.Object>>", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "k [...]
   }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/idempotentConsumer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/idempotentConsumer.json
index b6925b0a6a9..cda1aea40aa 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/idempotentConsumer.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/idempotentConsumer.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "idempotentRepository": { "index": 4, "kind": "attribute", "displayName": "Idempotent Repository", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the reference name of the message id repository" },
     "eager": { "index": 5, "kind": "attribute", "displayName": "Eager", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Sets whether to eagerly add the key to the idempotent repository or wait until the exchange is complete. Eager is default enabled." },
     "completionEager": { "index": 6, "kind": "attribute", "displayName": "Completion Eager", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Sets whether to complete the idempotent consumer eager or when the exchange is done. If this option is true to complete eager, then the idempotent consumer will trigger its completion when the exchange reached  [...]
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/loop.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/loop.json
index 530b30462ca..f24ae6b0489 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/loop.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/loop.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "copy": { "index": 4, "kind": "attribute", "displayName": "Copy", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If the copy attribute is true, a copy of the input Exchange is used for each iteration. That means each iteration will start from a copy of the same message. By default loop will loop the same exchange all over, so each iteration may [...]
     "doWhile": { "index": 5, "kind": "attribute", "displayName": "Do While", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enables the while loop that loops until the predicate evaluates to false or null." },
     "breakOnShutdown": { "index": 6, "kind": "attribute", "displayName": "Break On Shutdown", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If the breakOnShutdown attribute is true, then the loop will not iterate until it reaches the end when Camel is shut down." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/onException.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/onException.json
index 2da14d671a7..b4f40c86db1 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/onException.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/onException.json
@@ -17,11 +17,11 @@
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "exception": { "index": 3, "kind": "element", "displayName": "Exception", "required": true, "type": "array", "javaType": "java.util.List<java.lang.String>", "deprecated": false, "autowired": false, "secret": false, "description": "A set of exceptions to react upon." },
     "onWhen": { "index": 4, "kind": "element", "displayName": "On When", "required": false, "type": "object", "javaType": "org.apache.camel.model.WhenDefinition", "deprecated": false, "autowired": false, "secret": false, "asPredicate": true, "description": "Sets an additional predicate that should be true before the onException is triggered. To be used for fine grained controlling whether a thrown exception should be intercepted by this exception type or not." },
-    "retryWhile": { "index": 5, "kind": "expression", "displayName": "Retry While", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false,  [...]
+    "retryWhile": { "index": 5, "kind": "expression", "displayName": "Retry While", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecat [...]
     "redeliveryPolicy": { "index": 6, "kind": "element", "displayName": "Redelivery Policy", "required": false, "type": "object", "javaType": "org.apache.camel.model.RedeliveryPolicyDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "Used for configuring redelivery options" },
     "redeliveryPolicyRef": { "index": 7, "kind": "attribute", "displayName": "Redelivery Policy Ref", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a reference to a redelivery policy to lookup in the org.apache.camel.spi.Registry to be used." },
-    "handled": { "index": 8, "kind": "expression", "displayName": "Handled", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret" [...]
-    "continued": { "index": 9, "kind": "expression", "displayName": "Continued", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "au [...]
+    "handled": { "index": 8, "kind": "expression", "displayName": "Handled", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": fal [...]
+    "continued": { "index": 9, "kind": "expression", "displayName": "Continued", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated" [...]
     "onRedeliveryRef": { "index": 10, "kind": "attribute", "displayName": "On Redelivery Ref", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a reference to a processor that should be processed before a redelivery attempt. Can be used to change the org.apache.camel.Exchange before its being redelivered." },
     "onExceptionOccurredRef": { "index": 11, "kind": "attribute", "displayName": "On Exception Occurred Ref", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a reference to a processor that should be processed just after an exception occurred. Can be used to perform custom logging about the occurred exception at the exact time it happened. Important: Any exception thro [...]
     "useOriginalMessage": { "index": 12, "kind": "attribute", "displayName": "Use Original Message", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Will use the original input org.apache.camel.Message (original body and headers) when an org.apache.camel.Exchange is moved to the dead letter queue. Notice: this only applies when all redeliveries atte [...]
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pollEnrich.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pollEnrich.json
index 9f4fb8bafc5..ae0e0259ac0 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pollEnrich.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pollEnrich.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "variableReceive": { "index": 4, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
     "aggregationStrategy": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
     "aggregationStrategyMethodName": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/propertyExpression.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/propertyExpression.json
index ab1f52b9f9e..b72f1b7e093 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/propertyExpression.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/propertyExpression.json
@@ -13,6 +13,6 @@
   },
   "properties": {
     "key": { "index": 0, "kind": "attribute", "displayName": "Key", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Property key" },
-    "expression": { "index": 1, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 1, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/recipientList.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/recipientList.json
index f2ef0bf2eac..c40db279178 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/recipientList.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/recipientList.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "delimiter": { "index": 4, "kind": "attribute", "displayName": "Delimiter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": ",", "description": "Delimiter used if the Expression returned multiple endpoints. Can be turned off using the value false. The default value is ," },
     "aggregationStrategy": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to assemble the replies from the recipients, into a single outgoing message from the RecipientList. By default Camel will use the last reply as the outgoing message. You can also use a POJ [...]
     "aggregationStrategyMethodName": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/resequence.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/resequence.json
index d35481f201c..69cfc9eb88b 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/resequence.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/resequence.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "resequencerConfig": { "index": 4, "kind": "element", "displayName": "Resequencer Config", "required": true, "type": "object", "javaType": "org.apache.camel.model.config.ResequencerConfig", "oneOf": [ "batchConfig", "streamConfig" ], "deprecated": false, "autowired": false, "secret": false, "description": "To configure the resequencer in using either batch or stream configuration. Will by default use batch configuration." },
     "outputs": { "index": 5, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "kamelet", "loadBalance", "log", "loop", "marshal", "multicast", [...]
   }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routingSlip.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routingSlip.json
index d95ee6705c7..5042855089d 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routingSlip.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/routingSlip.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "uriDelimiter": { "index": 4, "kind": "attribute", "displayName": "Uri Delimiter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": ",", "description": "Sets the uri delimiter to use" },
     "ignoreInvalidEndpoints": { "index": 5, "kind": "attribute", "displayName": "Ignore Invalid Endpoints", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
     "cacheSize": { "index": 6, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this routing slip, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can b [...]
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/script.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/script.json
index f07706008cc..0f139fc2f84 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/script.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/script.json
@@ -15,6 +15,6 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setBody.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setBody.json
index 92a4df5afd6..df51f413def 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setBody.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setBody.json
@@ -15,6 +15,6 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setHeader.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setHeader.json
index 1747eec86e6..17f06bfac6b 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setHeader.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setHeader.json
@@ -16,6 +16,6 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "name": { "index": 3, "kind": "attribute", "displayName": "Name", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of message header to set a new value The simple language can be used to define a dynamic evaluated header name to be used. Otherwise a constant name will be used." },
-    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setProperty.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setProperty.json
index 6ea025227cb..8c84ae538ad 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setProperty.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setProperty.json
@@ -16,6 +16,6 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "name": { "index": 3, "kind": "attribute", "displayName": "Name", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of exchange property to set a new value. The simple language can be used to define a dynamic evaluated exchange property name to be used. Otherwise a constant name will be used." },
-    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setVariable.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setVariable.json
index 727ca75bd3b..8228bfa223c 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setVariable.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/setVariable.json
@@ -16,6 +16,6 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "name": { "index": 3, "kind": "attribute", "displayName": "Name", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of variable to set a new value The simple language can be used to define a dynamic evaluated variable name to be used. Otherwise a constant name will be used." },
-    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/sort.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/sort.json
index c6293eae018..835c12cdf28 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/sort.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/sort.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "comparator": { "index": 4, "kind": "attribute", "displayName": "Comparator", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.Comparator", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the comparator to use for sorting" }
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/split.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/split.json
index edf22cd176d..655a4e2c4c1 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/split.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/split.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "delimiter": { "index": 4, "kind": "attribute", "displayName": "Delimiter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": ",", "description": "Delimiter used in splitting messages. Can be turned off using the value false. To force not splitting then the delimiter can be set to single to use the value as a single list, this can be needed in some special situations. The default value is comma." },
     "aggregationStrategy": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a reference to the AggregationStrategy to be used to assemble the replies from the split messages, into a single outgoing message from the Splitter. By default Camel will use the original incoming message to the splitter (l [...]
     "aggregationStrategyMethodName": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/sticky.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/sticky.json
index ea297d88e47..28d5b9d01d1 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/sticky.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/sticky.json
@@ -13,6 +13,6 @@
   },
   "properties": {
     "id": { "index": 0, "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" },
-    "correlationExpression": { "index": 1, "kind": "expression", "displayName": "Correlation Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false,  [...]
+    "correlationExpression": { "index": 1, "kind": "expression", "displayName": "Correlation Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecat [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/throttle.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/throttle.json
index 4c22a696ced..0290faa517f 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/throttle.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/throttle.json
@@ -15,8 +15,8 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
-    "correlationExpression": { "index": 4, "kind": "expression", "displayName": "Correlation Expression", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
+    "correlationExpression": { "index": 4, "kind": "expression", "displayName": "Correlation Expression", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "depreca [...]
     "executorService": { "index": 5, "kind": "attribute", "displayName": "Executor Service", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom thread pool (ScheduledExecutorService) by the throttler." },
     "asyncDelayed": { "index": 6, "kind": "attribute", "displayName": "Async Delayed", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enables asynchronous delay which means the thread will not block while delaying." },
     "callerRunsWhenRejected": { "index": 7, "kind": "attribute", "displayName": "Caller Runs When Rejected", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether or not the caller should run the task when it was rejected by the thread pool. Is by default true" },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/transform.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/transform.json
index 423db060d0a..4c606557371 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/transform.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/transform.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "fromType": { "index": 4, "kind": "attribute", "displayName": "From Type", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "From type used in data type transformation." },
     "toType": { "index": 5, "kind": "attribute", "displayName": "To Type", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To type used as a target data type in the transformation." }
   }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/validate.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/validate.json
index 4b8205cc17f..5572af27a33 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/validate.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/validate.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "predicateExceptionFactory": { "index": 4, "kind": "attribute", "displayName": "Predicate Exception Factory", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.PredicateExceptionFactory", "deprecated": false, "autowired": false, "secret": false, "description": "The bean id of custom PredicateExceptionFactory to use for creating the exception when the validation fails. By default, Camel will throw PredicateValidationException. By using a custo [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/variable.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/variable.json
new file mode 100644
index 00000000000..459bc626869
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/variable.json
@@ -0,0 +1,20 @@
+{
+  "model": {
+    "kind": "model",
+    "name": "variable",
+    "title": "Variable",
+    "description": "Gets a variable",
+    "deprecated": false,
+    "firstVersion": "4.4.0",
+    "label": "language,core",
+    "javaType": "org.apache.camel.model.language.VariableExpression",
+    "abstract": false,
+    "input": false,
+    "output": false
+  },
+  "properties": {
+    "id": { "index": 0, "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" },
+    "expression": { "index": 1, "kind": "value", "displayName": "Expression", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax" },
+    "trim": { "index": 2, "kind": "attribute", "displayName": "Trim", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" }
+  }
+}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/when.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/when.json
index 575173bbb67..c5594bd442a 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/when.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/when.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "outputs": { "index": 4, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.model.ProcessorDefinition<java.lang.Object>>", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "k [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/whenSkipSendToEndpoint.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/whenSkipSendToEndpoint.json
index e343ba7a734..9bf421232e1 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/whenSkipSendToEndpoint.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/whenSkipSendToEndpoint.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "outputs": { "index": 4, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.model.ProcessorDefinition<java.lang.Object>>", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "k [...]
   }
 }
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 37d14072f8a..39385a5b832 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
@@ -2153,6 +2153,15 @@ To configure validators.
       <xs:documentation xml:lang="en">
 <![CDATA[
 A single value
+]]>
+      </xs:documentation>
+    </xs:annotation>
+  </xs:element>
+  <xs:element name="variable" type="tns:variableExpression">
+    <xs:annotation>
+      <xs:documentation xml:lang="en">
+<![CDATA[
+Gets a variable
 ]]>
       </xs:documentation>
     </xs:annotation>
@@ -3833,6 +3842,7 @@ complete all those exchanges before its stopped, when stopping CamelContext or t
       <xs:element ref="tns:simple"/>
       <xs:element ref="tns:spel"/>
       <xs:element ref="tns:tokenize"/>
+      <xs:element ref="tns:variable"/>
       <xs:element ref="tns:xtokenize"/>
       <xs:element ref="tns:xpath"/>
       <xs:element ref="tns:xquery"/>
@@ -4159,6 +4169,7 @@ controlling whether a thrown exception should be intercepted by this exception t
             <xs:element ref="tns:simple"/>
             <xs:element ref="tns:spel"/>
             <xs:element ref="tns:tokenize"/>
+            <xs:element ref="tns:variable"/>
             <xs:element ref="tns:xtokenize"/>
             <xs:element ref="tns:xpath"/>
             <xs:element ref="tns:xquery"/>
@@ -5167,6 +5178,7 @@ To use a specific charset when converting.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -5228,6 +5240,7 @@ To use a custom Thread Pool if asyncDelay has been enabled.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -5296,6 +5309,7 @@ reduce memory usage to avoid storing too many non frequent used producers.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -5935,6 +5949,7 @@ Sets the logging level to use for logging transactional rollback. This option is
             <xs:element ref="tns:simple"/>
             <xs:element ref="tns:spel"/>
             <xs:element ref="tns:tokenize"/>
+            <xs:element ref="tns:variable"/>
             <xs:element ref="tns:xtokenize"/>
             <xs:element ref="tns:xpath"/>
             <xs:element ref="tns:xquery"/>
@@ -6184,6 +6199,7 @@ Global option value.
             <xs:element ref="tns:simple"/>
             <xs:element ref="tns:spel"/>
             <xs:element ref="tns:tokenize"/>
+            <xs:element ref="tns:variable"/>
             <xs:element ref="tns:xtokenize"/>
             <xs:element ref="tns:xpath"/>
             <xs:element ref="tns:xquery"/>
@@ -6959,6 +6975,7 @@ To refer to a custom logger instance to lookup from the registry.
             <xs:element ref="tns:simple"/>
             <xs:element ref="tns:spel"/>
             <xs:element ref="tns:tokenize"/>
+            <xs:element ref="tns:variable"/>
             <xs:element ref="tns:xtokenize"/>
             <xs:element ref="tns:xpath"/>
             <xs:element ref="tns:xquery"/>
@@ -11155,6 +11172,7 @@ Sets a reference to use for lookup the policy in the registry.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -11311,6 +11329,7 @@ type in the registry by prefixing with #type: syntax, eg #type:com.foo.MyClassTy
       <xs:element ref="tns:simple"/>
       <xs:element ref="tns:spel"/>
       <xs:element ref="tns:tokenize"/>
+      <xs:element ref="tns:variable"/>
       <xs:element ref="tns:xtokenize"/>
       <xs:element ref="tns:xpath"/>
       <xs:element ref="tns:xquery"/>
@@ -11351,6 +11370,7 @@ Property key.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -11660,6 +11680,7 @@ Name of variable to remove.
             <xs:element ref="tns:simple"/>
             <xs:element ref="tns:spel"/>
             <xs:element ref="tns:tokenize"/>
+            <xs:element ref="tns:variable"/>
             <xs:element ref="tns:xtokenize"/>
             <xs:element ref="tns:xpath"/>
             <xs:element ref="tns:xquery"/>
@@ -12343,6 +12364,7 @@ Description of the parameter.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -12612,6 +12634,7 @@ Sets the sample message count which only a single Exchange will pass through aft
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -12645,6 +12668,7 @@ Sets the sample message count which only a single Exchange will pass through aft
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -12694,6 +12718,7 @@ Sets the new exchange pattern of the Exchange to be used from this point forward
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -12746,6 +12771,7 @@ be used. Otherwise a constant name will be used.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -12789,6 +12815,7 @@ property name to be used. Otherwise a constant name will be used.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -12832,6 +12859,7 @@ used. Otherwise a constant name will be used.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -12875,6 +12903,7 @@ Sets the comparator to use for sorting.
             <xs:element ref="tns:simple"/>
             <xs:element ref="tns:spel"/>
             <xs:element ref="tns:tokenize"/>
+            <xs:element ref="tns:variable"/>
             <xs:element ref="tns:xtokenize"/>
             <xs:element ref="tns:xpath"/>
             <xs:element ref="tns:xquery"/>
@@ -13485,6 +13514,7 @@ handler. Is by default true. Default value: true
             <xs:element ref="tns:simple"/>
             <xs:element ref="tns:spel"/>
             <xs:element ref="tns:tokenize"/>
+            <xs:element ref="tns:variable"/>
             <xs:element ref="tns:xtokenize"/>
             <xs:element ref="tns:xpath"/>
             <xs:element ref="tns:xquery"/>
@@ -13822,6 +13852,7 @@ Sets a reference to use for lookup the policy in the registry.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -14034,6 +14065,7 @@ Indicates whether null is allowed as value of a body to unmarshall. Default valu
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
@@ -14783,6 +14815,7 @@ Set the base path to store in ZK.
             <xs:element ref="tns:simple"/>
             <xs:element ref="tns:spel"/>
             <xs:element ref="tns:tokenize"/>
+            <xs:element ref="tns:variable"/>
             <xs:element ref="tns:xtokenize"/>
             <xs:element ref="tns:xpath"/>
             <xs:element ref="tns:xquery"/>
@@ -15453,6 +15486,11 @@ To skip the very first element. Default value: false
       </xs:extension>
     </xs:simpleContent>
   </xs:complexType>
+  <xs:complexType name="variableExpression">
+    <xs:simpleContent>
+      <xs:extension base="tns:expression"/>
+    </xs:simpleContent>
+  </xs:complexType>
   <xs:complexType name="xmlTokenizerExpression">
     <xs:simpleContent>
       <xs:extension base="tns:namespaceAwareExpression">
@@ -16942,6 +16980,7 @@ To configure a special tag for the operations within this rest definition.
           <xs:element ref="tns:simple"/>
           <xs:element ref="tns:spel"/>
           <xs:element ref="tns:tokenize"/>
+          <xs:element ref="tns:variable"/>
           <xs:element ref="tns:xtokenize"/>
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToVariableTest.xml
index 0c23f7592b3..3ddb93fd3e9 100644
--- a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToVariableTest.xml
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToVariableTest.xml
@@ -41,7 +41,7 @@
       <to uri="direct:foo" variableReceive="bye"/>
       <to uri="mock:after"/>
       <setBody>
-        <simple>${variable:bye}</simple>
+        <variable>bye</variable>
       </setBody>
       <to uri="mock:result"/>
     </route>
diff --git a/core/camel-core-languages/src/generated/resources/META-INF/services/org/apache/camel/language.properties b/core/camel-core-languages/src/generated/resources/META-INF/services/org/apache/camel/language.properties
index eaff3acb6f0..d2497ad07e7 100644
--- a/core/camel-core-languages/src/generated/resources/META-INF/services/org/apache/camel/language.properties
+++ b/core/camel-core-languages/src/generated/resources/META-INF/services/org/apache/camel/language.properties
@@ -1,5 +1,5 @@
 # Generated by camel build tools - do NOT edit this file!
-languages=constant csimple exchangeProperty file header ref simple tokenize
+languages=constant csimple exchangeProperty file header ref simple tokenize variable
 groupId=org.apache.camel
 artifactId=camel-core-languages
 version=4.4.0-SNAPSHOT
diff --git a/core/camel-core-languages/src/generated/resources/META-INF/services/org/apache/camel/language/variable b/core/camel-core-languages/src/generated/resources/META-INF/services/org/apache/camel/language/variable
new file mode 100644
index 00000000000..e7e576358c1
--- /dev/null
+++ b/core/camel-core-languages/src/generated/resources/META-INF/services/org/apache/camel/language/variable
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.language.variable.VariableLanguage
diff --git a/core/camel-core-languages/src/generated/resources/org/apache/camel/language/variable/variable.json b/core/camel-core-languages/src/generated/resources/org/apache/camel/language/variable/variable.json
new file mode 100644
index 00000000000..2b6b713c321
--- /dev/null
+++ b/core/camel-core-languages/src/generated/resources/org/apache/camel/language/variable/variable.json
@@ -0,0 +1,23 @@
+{
+  "language": {
+    "kind": "language",
+    "name": "variable",
+    "title": "Variable",
+    "description": "Gets a variable",
+    "deprecated": false,
+    "firstVersion": "4.4.0",
+    "label": "language,core",
+    "javaType": "org.apache.camel.language.variable.VariableLanguage",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-core-languages",
+    "version": "4.4.0-SNAPSHOT",
+    "modelName": "variable",
+    "modelJavaType": "org.apache.camel.model.language.VariableExpression"
+  },
+  "properties": {
+    "id": { "index": 0, "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" },
+    "expression": { "index": 1, "kind": "value", "displayName": "Expression", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax" },
+    "trim": { "index": 2, "kind": "attribute", "displayName": "Trim", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" }
+  }
+}
diff --git a/core/camel-core-languages/src/main/docs/modules/languages/pages/variable-language.adoc b/core/camel-core-languages/src/main/docs/modules/languages/pages/variable-language.adoc
new file mode 100644
index 00000000000..0dbe57b4837
--- /dev/null
+++ b/core/camel-core-languages/src/main/docs/modules/languages/pages/variable-language.adoc
@@ -0,0 +1,52 @@
+= Variable Language
+:doctitle: Variable
+:shortname: variable
+:artifactid: camel-core-languages
+:description: Gets a variable
+:since: 4.4
+:supportlevel: Preview
+:tabs-sync-option:
+//Manually maintained attributes
+:camel-spring-boot-name: core
+:starter-artifactid: camel-core-starter
+
+*Since Camel {since}*
+
+The Variable Expression Language allows you to extract values of named
+variables.
+
+== Variable Options
+
+// language options: START
+include::partial$language-options.adoc[]
+// language options: END
+
+== Example usage
+
+The `recipientList` EIP can utilize a variable:
+
+[source,xml]
+----
+<route>
+  <from uri="direct:a" />
+  <recipientList>
+    <variable>myVar</variable>
+  </recipientList>
+</route>
+----
+
+In this case, the list of recipients are contained in the variable
+'myVar'.
+
+And the same example in Java DSL:
+
+[source,java]
+----
+from("direct:a").recipientList(variable("myVar"));
+----
+
+== Dependencies
+
+The Header language is part of *camel-core*.
+
+include::spring-boot:partial$starter.adoc[]
diff --git a/core/camel-core-languages/src/main/java/org/apache/camel/language/variable/VariableLanguage.java b/core/camel-core-languages/src/main/java/org/apache/camel/language/variable/VariableLanguage.java
new file mode 100644
index 00000000000..e1d6bb8f51b
--- /dev/null
+++ b/core/camel-core-languages/src/main/java/org/apache/camel/language/variable/VariableLanguage.java
@@ -0,0 +1,50 @@
+/*
+ * 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.language.variable;
+
+import org.apache.camel.Expression;
+import org.apache.camel.IsSingleton;
+import org.apache.camel.Predicate;
+import org.apache.camel.spi.Language;
+import org.apache.camel.support.ExpressionToPredicateAdapter;
+import org.apache.camel.support.builder.ExpressionBuilder;
+
+/**
+ * A language for variable expressions.
+ */
+@org.apache.camel.spi.annotations.Language("variable")
+public class VariableLanguage implements Language, IsSingleton {
+
+    public static Expression variable(String name) {
+        return ExpressionBuilder.variableExpression(name);
+    }
+
+    @Override
+    public Predicate createPredicate(String expression) {
+        return ExpressionToPredicateAdapter.toPredicate(createExpression(expression));
+    }
+
+    @Override
+    public Expression createExpression(String expression) {
+        return VariableLanguage.variable(expression);
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+}
diff --git a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
index fd135d9bdc6..f1bb551c573 100644
--- a/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
+++ b/core/camel-core-model/src/generated/resources/META-INF/services/org/apache/camel/model.properties
@@ -216,6 +216,7 @@ unmarshal
 validate
 validators
 value
+variable
 weighted
 when
 whenSkipSendToEndpoint
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/aggregate.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/aggregate.json
index 2a5e8a5d0f4..146ff687660 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/aggregate.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/aggregate.json
@@ -15,10 +15,10 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "correlationExpression": { "index": 3, "kind": "expression", "displayName": "Correlation Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false,  [...]
-    "completionPredicate": { "index": 4, "kind": "expression", "displayName": "Completion Predicate", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "de [...]
-    "completionTimeoutExpression": { "index": 5, "kind": "expression", "displayName": "Completion Timeout Expression", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", " [...]
-    "completionSizeExpression": { "index": 6, "kind": "expression", "displayName": "Completion Size Expression", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtoken [...]
+    "correlationExpression": { "index": 3, "kind": "expression", "displayName": "Correlation Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecat [...]
+    "completionPredicate": { "index": 4, "kind": "expression", "displayName": "Completion Predicate", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtoke [...]
+    "completionTimeoutExpression": { "index": 5, "kind": "expression", "displayName": "Completion Timeout Expression", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", [...]
+    "completionSizeExpression": { "index": 6, "kind": "expression", "displayName": "Completion Size Expression", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xque [...]
     "optimisticLockRetryPolicy": { "index": 7, "kind": "element", "displayName": "Optimistic Lock Retry Policy", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.OptimisticLockRetryPolicyDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "Allows to configure retry settings when using optimistic locking." },
     "parallelProcessing": { "index": 8, "kind": "attribute", "displayName": "Parallel Processing", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "When aggregated are completed they are being send out of the aggregator. This option indicates whether or not Camel should use a thread pool with multiple threads for concurrency. If no custom thread pool has been specified t [...]
     "optimisticLocking": { "index": 9, "kind": "attribute", "displayName": "Optimistic Locking", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Turns on using optimistic locking, which requires the aggregationRepository being used, is supporting this by implementing org.apache.camel.spi.OptimisticLockingAggregationRepository ." },
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/delay.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/delay.json
index 4bea1637852..cd76ab10920 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/delay.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/delay.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "asyncDelayed": { "index": 4, "kind": "attribute", "displayName": "Async Delayed", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Enables asynchronous delay which means the thread will not block while delaying." },
     "callerRunsWhenRejected": { "index": 5, "kind": "attribute", "displayName": "Caller Runs When Rejected", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether or not the caller should run the task when it was rejected by the thread pool. Is by default true" },
     "executorService": { "index": 6, "kind": "attribute", "displayName": "Executor Service", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Thread Pool if asyncDelay has been enabled." }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/dynamicRouter.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/dynamicRouter.json
index 20a80eb709a..75315b7fce5 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/dynamicRouter.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/dynamicRouter.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "uriDelimiter": { "index": 4, "kind": "attribute", "displayName": "Uri Delimiter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": ",", "description": "Sets the uri delimiter to use" },
     "ignoreInvalidEndpoints": { "index": 5, "kind": "attribute", "displayName": "Ignore Invalid Endpoints", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
     "cacheSize": { "index": 6, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this dynamic router, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can [...]
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/enrich.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/enrich.json
index e11f1e6a6be..bf05d0bef3a 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/enrich.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/enrich.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
     "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
     "aggregationStrategy": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
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 208e188fee3..5d5ebe12c54 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
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "statusPropertyName": { "index": 4, "kind": "attribute", "displayName": "Status Property Name", "label": "advanced", "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." },
     "outputs": { "index": 5, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.model.ProcessorDefinition<java.lang.Object>>", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "k [...]
   }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/idempotentConsumer.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/idempotentConsumer.json
index b6925b0a6a9..cda1aea40aa 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/idempotentConsumer.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/idempotentConsumer.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "idempotentRepository": { "index": 4, "kind": "attribute", "displayName": "Idempotent Repository", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the reference name of the message id repository" },
     "eager": { "index": 5, "kind": "attribute", "displayName": "Eager", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Sets whether to eagerly add the key to the idempotent repository or wait until the exchange is complete. Eager is default enabled." },
     "completionEager": { "index": 6, "kind": "attribute", "displayName": "Completion Eager", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Sets whether to complete the idempotent consumer eager or when the exchange is done. If this option is true to complete eager, then the idempotent consumer will trigger its completion when the exchange reached  [...]
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/jaxb.index b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/jaxb.index
index 27225bc9c20..b697988828d 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/jaxb.index
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/jaxb.index
@@ -21,6 +21,7 @@ RefExpression
 SimpleExpression
 SpELExpression
 TokenizerExpression
+VariableExpression
 XMLTokenizerExpression
 XPathExpression
 XQueryExpression
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/variable.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/variable.json
new file mode 100644
index 00000000000..459bc626869
--- /dev/null
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/language/variable.json
@@ -0,0 +1,20 @@
+{
+  "model": {
+    "kind": "model",
+    "name": "variable",
+    "title": "Variable",
+    "description": "Gets a variable",
+    "deprecated": false,
+    "firstVersion": "4.4.0",
+    "label": "language,core",
+    "javaType": "org.apache.camel.model.language.VariableExpression",
+    "abstract": false,
+    "input": false,
+    "output": false
+  },
+  "properties": {
+    "id": { "index": 0, "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" },
+    "expression": { "index": 1, "kind": "value", "displayName": "Expression", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The expression value in your chosen language syntax" },
+    "trim": { "index": 2, "kind": "attribute", "displayName": "Trim", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to trim the value to remove leading and trailing whitespaces and line breaks" }
+  }
+}
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/loadbalancer/sticky.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/loadbalancer/sticky.json
index ea297d88e47..28d5b9d01d1 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/loadbalancer/sticky.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/loadbalancer/sticky.json
@@ -13,6 +13,6 @@
   },
   "properties": {
     "id": { "index": 0, "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The id of this node" },
-    "correlationExpression": { "index": 1, "kind": "expression", "displayName": "Correlation Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false,  [...]
+    "correlationExpression": { "index": 1, "kind": "expression", "displayName": "Correlation Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecat [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/loop.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/loop.json
index 530b30462ca..f24ae6b0489 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/loop.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/loop.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "copy": { "index": 4, "kind": "attribute", "displayName": "Copy", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If the copy attribute is true, a copy of the input Exchange is used for each iteration. That means each iteration will start from a copy of the same message. By default loop will loop the same exchange all over, so each iteration may [...]
     "doWhile": { "index": 5, "kind": "attribute", "displayName": "Do While", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enables the while loop that loops until the predicate evaluates to false or null." },
     "breakOnShutdown": { "index": 6, "kind": "attribute", "displayName": "Break On Shutdown", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If the breakOnShutdown attribute is true, then the loop will not iterate until it reaches the end when Camel is shut down." },
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/onException.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/onException.json
index 2da14d671a7..b4f40c86db1 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/onException.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/onException.json
@@ -17,11 +17,11 @@
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "exception": { "index": 3, "kind": "element", "displayName": "Exception", "required": true, "type": "array", "javaType": "java.util.List<java.lang.String>", "deprecated": false, "autowired": false, "secret": false, "description": "A set of exceptions to react upon." },
     "onWhen": { "index": 4, "kind": "element", "displayName": "On When", "required": false, "type": "object", "javaType": "org.apache.camel.model.WhenDefinition", "deprecated": false, "autowired": false, "secret": false, "asPredicate": true, "description": "Sets an additional predicate that should be true before the onException is triggered. To be used for fine grained controlling whether a thrown exception should be intercepted by this exception type or not." },
-    "retryWhile": { "index": 5, "kind": "expression", "displayName": "Retry While", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false,  [...]
+    "retryWhile": { "index": 5, "kind": "expression", "displayName": "Retry While", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecat [...]
     "redeliveryPolicy": { "index": 6, "kind": "element", "displayName": "Redelivery Policy", "required": false, "type": "object", "javaType": "org.apache.camel.model.RedeliveryPolicyDefinition", "deprecated": false, "autowired": false, "secret": false, "description": "Used for configuring redelivery options" },
     "redeliveryPolicyRef": { "index": 7, "kind": "attribute", "displayName": "Redelivery Policy Ref", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a reference to a redelivery policy to lookup in the org.apache.camel.spi.Registry to be used." },
-    "handled": { "index": 8, "kind": "expression", "displayName": "Handled", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "secret" [...]
-    "continued": { "index": 9, "kind": "expression", "displayName": "Continued", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "au [...]
+    "handled": { "index": 8, "kind": "expression", "displayName": "Handled", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": fal [...]
+    "continued": { "index": 9, "kind": "expression", "displayName": "Continued", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated" [...]
     "onRedeliveryRef": { "index": 10, "kind": "attribute", "displayName": "On Redelivery Ref", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a reference to a processor that should be processed before a redelivery attempt. Can be used to change the org.apache.camel.Exchange before its being redelivered." },
     "onExceptionOccurredRef": { "index": 11, "kind": "attribute", "displayName": "On Exception Occurred Ref", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a reference to a processor that should be processed just after an exception occurred. Can be used to perform custom logging about the occurred exception at the exact time it happened. Important: Any exception thro [...]
     "useOriginalMessage": { "index": 12, "kind": "attribute", "displayName": "Use Original Message", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Will use the original input org.apache.camel.Message (original body and headers) when an org.apache.camel.Exchange is moved to the dead letter queue. Notice: this only applies when all redeliveries atte [...]
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/pollEnrich.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/pollEnrich.json
index 9f4fb8bafc5..ae0e0259ac0 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/pollEnrich.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/pollEnrich.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "variableReceive": { "index": 4, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
     "aggregationStrategy": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
     "aggregationStrategyMethodName": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/propertyExpression.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/propertyExpression.json
index ab1f52b9f9e..b72f1b7e093 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/propertyExpression.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/propertyExpression.json
@@ -13,6 +13,6 @@
   },
   "properties": {
     "key": { "index": 0, "kind": "attribute", "displayName": "Key", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Property key" },
-    "expression": { "index": 1, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 1, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/recipientList.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/recipientList.json
index f2ef0bf2eac..c40db279178 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/recipientList.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/recipientList.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "delimiter": { "index": 4, "kind": "attribute", "displayName": "Delimiter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": ",", "description": "Delimiter used if the Expression returned multiple endpoints. Can be turned off using the value false. The default value is ," },
     "aggregationStrategy": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to assemble the replies from the recipients, into a single outgoing message from the RecipientList. By default Camel will use the last reply as the outgoing message. You can also use a POJ [...]
     "aggregationStrategyMethodName": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/resequence.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/resequence.json
index d35481f201c..69cfc9eb88b 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/resequence.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/resequence.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "resequencerConfig": { "index": 4, "kind": "element", "displayName": "Resequencer Config", "required": true, "type": "object", "javaType": "org.apache.camel.model.config.ResequencerConfig", "oneOf": [ "batchConfig", "streamConfig" ], "deprecated": false, "autowired": false, "secret": false, "description": "To configure the resequencer in using either batch or stream configuration. Will by default use batch configuration." },
     "outputs": { "index": 5, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "kamelet", "loadBalance", "log", "loop", "marshal", "multicast", [...]
   }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/routingSlip.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/routingSlip.json
index d95ee6705c7..5042855089d 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/routingSlip.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/routingSlip.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "uriDelimiter": { "index": 4, "kind": "attribute", "displayName": "Uri Delimiter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": ",", "description": "Sets the uri delimiter to use" },
     "ignoreInvalidEndpoints": { "index": 5, "kind": "attribute", "displayName": "Ignore Invalid Endpoints", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
     "cacheSize": { "index": 6, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ProducerCache which is used to cache and reuse producers when using this routing slip, when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can b [...]
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/script.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/script.json
index f07706008cc..0f139fc2f84 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/script.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/script.json
@@ -15,6 +15,6 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/setBody.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/setBody.json
index 92a4df5afd6..df51f413def 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/setBody.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/setBody.json
@@ -15,6 +15,6 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/setHeader.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/setHeader.json
index 1747eec86e6..17f06bfac6b 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/setHeader.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/setHeader.json
@@ -16,6 +16,6 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "name": { "index": 3, "kind": "attribute", "displayName": "Name", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of message header to set a new value The simple language can be used to define a dynamic evaluated header name to be used. Otherwise a constant name will be used." },
-    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/setProperty.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/setProperty.json
index 6ea025227cb..8c84ae538ad 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/setProperty.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/setProperty.json
@@ -16,6 +16,6 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "name": { "index": 3, "kind": "attribute", "displayName": "Name", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of exchange property to set a new value. The simple language can be used to define a dynamic evaluated exchange property name to be used. Otherwise a constant name will be used." },
-    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/setVariable.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/setVariable.json
index 727ca75bd3b..8228bfa223c 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/setVariable.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/setVariable.json
@@ -16,6 +16,6 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "name": { "index": 3, "kind": "attribute", "displayName": "Name", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Name of variable to set a new value The simple language can be used to define a dynamic evaluated variable name to be used. Otherwise a constant name will be used." },
-    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 4, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/sort.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/sort.json
index c6293eae018..835c12cdf28 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/sort.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/sort.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "comparator": { "index": 4, "kind": "attribute", "displayName": "Comparator", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.Comparator", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the comparator to use for sorting" }
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/split.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/split.json
index edf22cd176d..655a4e2c4c1 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/split.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/split.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "delimiter": { "index": 4, "kind": "attribute", "displayName": "Delimiter", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": ",", "description": "Delimiter used in splitting messages. Can be turned off using the value false. To force not splitting then the delimiter can be set to single to use the value as a single list, this can be needed in some special situations. The default value is comma." },
     "aggregationStrategy": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets a reference to the AggregationStrategy to be used to assemble the replies from the split messages, into a single outgoing message from the Splitter. By default Camel will use the original incoming message to the splitter (l [...]
     "aggregationStrategyMethodName": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/throttle.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/throttle.json
index 4c22a696ced..0290faa517f 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/throttle.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/throttle.json
@@ -15,8 +15,8 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
-    "correlationExpression": { "index": 4, "kind": "expression", "displayName": "Correlation Expression", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
+    "correlationExpression": { "index": 4, "kind": "expression", "displayName": "Correlation Expression", "required": false, "type": "object", "javaType": "org.apache.camel.model.ExpressionSubElementDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "depreca [...]
     "executorService": { "index": 5, "kind": "attribute", "displayName": "Executor Service", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom thread pool (ScheduledExecutorService) by the throttler." },
     "asyncDelayed": { "index": 6, "kind": "attribute", "displayName": "Async Delayed", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Enables asynchronous delay which means the thread will not block while delaying." },
     "callerRunsWhenRejected": { "index": 7, "kind": "attribute", "displayName": "Caller Runs When Rejected", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether or not the caller should run the task when it was rejected by the thread pool. Is by default true" },
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/transform.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/transform.json
index 423db060d0a..4c606557371 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/transform.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/transform.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "fromType": { "index": 4, "kind": "attribute", "displayName": "From Type", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "From type used in data type transformation." },
     "toType": { "index": 5, "kind": "attribute", "displayName": "To Type", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To type used as a target data type in the transformation." }
   }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/validate.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/validate.json
index 4b8205cc17f..5572af27a33 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/validate.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/validate.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "predicateExceptionFactory": { "index": 4, "kind": "attribute", "displayName": "Predicate Exception Factory", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.PredicateExceptionFactory", "deprecated": false, "autowired": false, "secret": false, "description": "The bean id of custom PredicateExceptionFactory to use for creating the exception when the validation fails. By default, Camel will throw PredicateValidationException. By using a custo [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/when.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/when.json
index 575173bbb67..c5594bd442a 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/when.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/when.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "outputs": { "index": 4, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.model.ProcessorDefinition<java.lang.Object>>", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "k [...]
   }
 }
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/whenSkipSendToEndpoint.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/whenSkipSendToEndpoint.json
index e343ba7a734..9bf421232e1 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/whenSkipSendToEndpoint.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/whenSkipSendToEndpoint.json
@@ -15,7 +15,7 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
+    "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "variable", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": [...]
     "outputs": { "index": 4, "kind": "element", "displayName": "Outputs", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.model.ProcessorDefinition<java.lang.Object>>", "oneOf": [ "aggregate", "bean", "choice", "circuitBreaker", "claimCheck", "convertBodyTo", "convertHeaderTo", "convertVariableTo", "delay", "doCatch", "doFinally", "doTry", "dynamicRouter", "enrich", "filter", "idempotentConsumer", "intercept", "interceptFrom", "interceptSendToEndpoint", "k [...]
   }
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/Builder.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/Builder.java
index 2da14f1acb4..ee7ffbacc14 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/Builder.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/Builder.java
@@ -28,6 +28,7 @@ import org.apache.camel.model.language.JsonPathExpression;
 import org.apache.camel.model.language.LanguageExpression;
 import org.apache.camel.model.language.MethodCallExpression;
 import org.apache.camel.model.language.SimpleExpression;
+import org.apache.camel.model.language.VariableExpression;
 import org.apache.camel.util.ObjectHelper;
 
 /**
@@ -275,6 +276,14 @@ public final class Builder {
         return new ValueBuilder(exp);
     }
 
+    /**
+     * Returns a predicate and value builder for variable
+     */
+    public static ValueBuilder variable(String name) {
+        Expression exp = new VariableExpression(name);
+        return new ValueBuilder(exp);
+    }
+
     /**
      * Returns an expression for the given system property
      */
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/BuilderSupport.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/BuilderSupport.java
index 1fabeee1813..4567c61cc62 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/BuilderSupport.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/BuilderSupport.java
@@ -27,6 +27,7 @@ import org.apache.camel.model.language.DatasonnetExpression;
 import org.apache.camel.model.language.ExchangePropertyExpression;
 import org.apache.camel.model.language.HeaderExpression;
 import org.apache.camel.model.language.SimpleExpression;
+import org.apache.camel.model.language.VariableExpression;
 import org.apache.camel.model.language.XPathExpression;
 import org.apache.camel.spi.TransactedPolicy;
 import org.apache.camel.support.builder.Namespaces;
@@ -87,6 +88,14 @@ public abstract class BuilderSupport implements CamelContextAware {
         return Builder.bodyAs(type);
     }
 
+    /**
+     * Returns a value builder for the given variable
+     */
+    public ValueBuilder variable(String name) {
+        Expression exp = new VariableExpression(name);
+        return new ValueBuilder(exp);
+    }
+
     /**
      * Returns a value builder for the given system property
      */
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
index ff3b81c421b..197f12d5b19 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/ExpressionClauseSupport.java
@@ -44,6 +44,7 @@ import org.apache.camel.model.language.RefExpression;
 import org.apache.camel.model.language.SimpleExpression;
 import org.apache.camel.model.language.SpELExpression;
 import org.apache.camel.model.language.TokenizerExpression;
+import org.apache.camel.model.language.VariableExpression;
 import org.apache.camel.model.language.XMLTokenizerExpression;
 import org.apache.camel.model.language.XPathExpression;
 import org.apache.camel.model.language.XQueryExpression;
@@ -200,6 +201,13 @@ public class ExpressionClauseSupport<T> implements ExpressionFactoryAware, Predi
         return expression(ExpressionBuilder.exchangePropertiesExpression());
     }
 
+    /**
+     * An expression of a variable with the given name
+     */
+    public T variable(String name) {
+        return expression(new VariableExpression(name));
+    }
+
     // Languages
     // -------------------------------------------------------------------------
 
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/LanguageBuilderFactory.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/LanguageBuilderFactory.java
index a70a584f942..6b54955a182 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/LanguageBuilderFactory.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/LanguageBuilderFactory.java
@@ -37,6 +37,7 @@ import org.apache.camel.model.language.RefExpression;
 import org.apache.camel.model.language.SimpleExpression;
 import org.apache.camel.model.language.SpELExpression;
 import org.apache.camel.model.language.TokenizerExpression;
+import org.apache.camel.model.language.VariableExpression;
 import org.apache.camel.model.language.XMLTokenizerExpression;
 import org.apache.camel.model.language.XPathExpression;
 import org.apache.camel.model.language.XQueryExpression;
@@ -194,6 +195,13 @@ public final class LanguageBuilderFactory {
         return new TokenizerExpression.Builder();
     }
 
+    /**
+     * Uses the Variable language
+     */
+    public VariableExpression.Builder variable() {
+        return new VariableExpression.Builder();
+    }
+
     /**
      * Uses the XMLTokenizer language
      */
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/language/VariableExpression.java b/core/camel-core-model/src/main/java/org/apache/camel/model/language/VariableExpression.java
new file mode 100644
index 00000000000..5ce38b6a3d3
--- /dev/null
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/language/VariableExpression.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.model.language;
+
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlTransient;
+
+import org.apache.camel.spi.Metadata;
+
+/**
+ * Gets a variable
+ */
+@Metadata(firstVersion = "4.4.0", label = "language,core", title = "Variable")
+@XmlRootElement(name = "variable")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class VariableExpression extends ExpressionDefinition {
+
+    public VariableExpression() {
+    }
+
+    public VariableExpression(String expression) {
+        super(expression);
+    }
+
+    private VariableExpression(Builder builder) {
+        super(builder);
+    }
+
+    @Override
+    public String getLanguage() {
+        return "variable";
+    }
+
+    /**
+     * {@code Builder} is a specific builder for {@link VariableExpression}.
+     */
+    @XmlTransient
+    public static class Builder extends AbstractBuilder<Builder, VariableExpression> {
+
+        @Override
+        public VariableExpression end() {
+            return new VariableExpression(this);
+        }
+    }
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/language/VariableTest.java b/core/camel-core/src/test/java/org/apache/camel/language/VariableTest.java
new file mode 100644
index 00000000000..95486d2a77d
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/language/VariableTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.language;
+
+import org.apache.camel.LanguageTestSupport;
+import org.apache.camel.language.variable.VariableLanguage;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class VariableTest extends LanguageTestSupport {
+
+    @Test
+    public void testVariableExpressions() throws Exception {
+        exchange.setVariable("varFoo", "abc");
+        assertExpression("varFoo", "abc");
+    }
+
+    @Test
+    public void testPredicates() throws Exception {
+        context.setVariable("varFoo", true);
+        assertPredicate("global:varFoo");
+
+        exchange.setVariable("varLocalFoo", false);
+        assertPredicate("varLocal", false);
+    }
+
+    @Test
+    public void testSingleton() {
+        VariableLanguage prop = new VariableLanguage();
+        assertTrue(prop.isSingleton());
+    }
+
+    @Override
+    protected String getLanguageName() {
+        return "variable";
+    }
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java
index b5fbc29565b..ee7d913bb33 100644
--- a/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java
@@ -72,7 +72,7 @@ public class ToVariableTest extends ContextTestSupport {
                 from("direct:receive")
                         .toV("direct:foo", null, "bye")
                         .to("mock:after")
-                        .setBody(simple("${variable:bye}"))
+                        .setBody(variable("bye"))
                         .to("mock:result");
 
                 from("direct:sendAndReceive")
diff --git a/core/camel-main/src/generated/resources/org/apache/camel/main/languages.properties b/core/camel-main/src/generated/resources/org/apache/camel/main/languages.properties
index 86fcea79981..c478818f78c 100644
--- a/core/camel-main/src/generated/resources/org/apache/camel/main/languages.properties
+++ b/core/camel-main/src/generated/resources/org/apache/camel/main/languages.properties
@@ -19,6 +19,7 @@ ref
 simple
 spel
 tokenize
+variable
 xpath
 xquery
 xtokenize
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 8d9af8a709d..912733fde48 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
@@ -2999,6 +2999,10 @@ public class ModelParser extends BaseParser {
             return true;
         };
     }
+    protected VariableExpression doParseVariableExpression() throws IOException, XmlPullParserException {
+        return doParse(new VariableExpression(),
+            expressionDefinitionAttributeHandler(), noElementHandler(), expressionDefinitionValueHandler());
+    }
     protected XMLTokenizerExpression doParseXMLTokenizerExpression() throws IOException, XmlPullParserException {
         return doParse(new XMLTokenizerExpression(), (def, key, val) -> {
             switch (key) {
@@ -3576,6 +3580,7 @@ public class ModelParser extends BaseParser {
             case "simple": return doParseSimpleExpression();
             case "spel": return doParseSpELExpression();
             case "tokenize": return doParseTokenizerExpression();
+            case "variable": return doParseVariableExpression();
             case "xtokenize": return doParseXMLTokenizerExpression();
             case "xpath": return doParseXPathExpression();
             case "xquery": return doParseXQueryExpression();
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index f87189f2681..e1d0b43431e 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -894,6 +894,11 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         doWriteTokenizerExpression("tokenize", def);
     }
+    public void writeVariableExpression(
+            VariableExpression def)
+            throws IOException {
+        doWriteVariableExpression("variable", def);
+    }
     public void writeXMLTokenizerExpression(
             XMLTokenizerExpression def)
             throws IOException {
@@ -4184,6 +4189,15 @@ public class ModelWriter extends BaseWriter {
         doWriteValue(def.getExpression());
         endElement(name);
     }
+    protected void doWriteVariableExpression(
+            String name,
+            VariableExpression def)
+            throws IOException {
+        startElement(name);
+        doWriteExpressionDefinitionAttributes(def);
+        doWriteValue(def.getExpression());
+        endElement(name);
+    }
     protected void doWriteXMLTokenizerExpression(
             String name,
             XMLTokenizerExpression def)
@@ -5093,6 +5107,7 @@ public class ModelWriter extends BaseWriter {
                 case "SimpleExpression" -> doWriteSimpleExpression("simple", (SimpleExpression) v);
                 case "SpELExpression" -> doWriteSpELExpression("spel", (SpELExpression) v);
                 case "TokenizerExpression" -> doWriteTokenizerExpression("tokenize", (TokenizerExpression) v);
+                case "VariableExpression" -> doWriteVariableExpression("variable", (VariableExpression) v);
                 case "XMLTokenizerExpression" -> doWriteXMLTokenizerExpression("xtokenize", (XMLTokenizerExpression) v);
                 case "XPathExpression" -> doWriteXPathExpression("xpath", (XPathExpression) v);
                 case "XQueryExpression" -> doWriteXQueryExpression("xquery", (XQueryExpression) v);
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index fc9f63d4dfe..3991d301d87 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -894,6 +894,11 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         doWriteTokenizerExpression("tokenize", def);
     }
+    public void writeVariableExpression(
+            VariableExpression def)
+            throws IOException {
+        doWriteVariableExpression("variable", def);
+    }
     public void writeXMLTokenizerExpression(
             XMLTokenizerExpression def)
             throws IOException {
@@ -4184,6 +4189,15 @@ public class ModelWriter extends BaseWriter {
         doWriteValue(def.getExpression());
         endElement(name);
     }
+    protected void doWriteVariableExpression(
+            String name,
+            VariableExpression def)
+            throws IOException {
+        startElement(name);
+        doWriteExpressionDefinitionAttributes(def);
+        doWriteValue(def.getExpression());
+        endElement(name);
+    }
     protected void doWriteXMLTokenizerExpression(
             String name,
             XMLTokenizerExpression def)
@@ -5093,6 +5107,7 @@ public class ModelWriter extends BaseWriter {
                 case "SimpleExpression" -> doWriteSimpleExpression("simple", (SimpleExpression) v);
                 case "SpELExpression" -> doWriteSpELExpression("spel", (SpELExpression) v);
                 case "TokenizerExpression" -> doWriteTokenizerExpression("tokenize", (TokenizerExpression) v);
+                case "VariableExpression" -> doWriteVariableExpression("variable", (VariableExpression) v);
                 case "XMLTokenizerExpression" -> doWriteXMLTokenizerExpression("xtokenize", (XMLTokenizerExpression) v);
                 case "XPathExpression" -> doWriteXPathExpression("xpath", (XPathExpression) v);
                 case "XQueryExpression" -> doWriteXQueryExpression("xquery", (XQueryExpression) v);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
index 1dec6a7d4ce..01f7e8a5539 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
@@ -101,6 +101,9 @@ public final class ExpressionDeserializers extends YamlDeserializerSupport {
             case "tokenize": {
                 return asType(node, org.apache.camel.model.language.TokenizerExpression.class);
             }
+            case "variable": {
+                return asType(node, org.apache.camel.model.language.VariableExpression.class);
+            }
             case "xpath": {
                 return asType(node, org.apache.camel.model.language.XPathExpression.class);
             }
@@ -148,6 +151,7 @@ public final class ExpressionDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "simple", type = "object:org.apache.camel.model.language.SimpleExpression", oneOf = "expression"),
                     @YamlProperty(name = "spel", type = "object:org.apache.camel.model.language.SpELExpression", oneOf = "expression"),
                     @YamlProperty(name = "tokenize", type = "object:org.apache.camel.model.language.TokenizerExpression", oneOf = "expression"),
+                    @YamlProperty(name = "variable", type = "object:org.apache.camel.model.language.VariableExpression", oneOf = "expression"),
                     @YamlProperty(name = "xpath", type = "object:org.apache.camel.model.language.XPathExpression", oneOf = "expression"),
                     @YamlProperty(name = "xquery", type = "object:org.apache.camel.model.language.XQueryExpression", oneOf = "expression"),
                     @YamlProperty(name = "xtokenize", type = "object:org.apache.camel.model.language.XMLTokenizerExpression", oneOf = "expression")
@@ -185,6 +189,7 @@ public final class ExpressionDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "simple", type = "object:org.apache.camel.model.language.SimpleExpression", oneOf = "expression"),
                     @YamlProperty(name = "spel", type = "object:org.apache.camel.model.language.SpELExpression", oneOf = "expression"),
                     @YamlProperty(name = "tokenize", type = "object:org.apache.camel.model.language.TokenizerExpression", oneOf = "expression"),
+                    @YamlProperty(name = "variable", type = "object:org.apache.camel.model.language.VariableExpression", oneOf = "expression"),
                     @YamlProperty(name = "xpath", type = "object:org.apache.camel.model.language.XPathExpression", oneOf = "expression"),
                     @YamlProperty(name = "xquery", type = "object:org.apache.camel.model.language.XQueryExpression", oneOf = "expression"),
                     @YamlProperty(name = "xtokenize", type = "object:org.apache.camel.model.language.XMLTokenizerExpression", oneOf = "expression")
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 940c91db3e9..d91e5e3798d 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
@@ -202,6 +202,7 @@ import org.apache.camel.model.language.RefExpression;
 import org.apache.camel.model.language.SimpleExpression;
 import org.apache.camel.model.language.SpELExpression;
 import org.apache.camel.model.language.TokenizerExpression;
+import org.apache.camel.model.language.VariableExpression;
 import org.apache.camel.model.language.XMLTokenizerExpression;
 import org.apache.camel.model.language.XPathExpression;
 import org.apache.camel.model.language.XQueryExpression;
@@ -19742,6 +19743,72 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
         }
     }
 
+    @YamlType(
+            nodes = "variable",
+            inline = true,
+            types = org.apache.camel.model.language.VariableExpression.class,
+            order = org.apache.camel.dsl.yaml.common.YamlDeserializerResolver.ORDER_LOWEST - 1,
+            displayName = "Variable",
+            description = "Gets a variable",
+            deprecated = false,
+            properties = {
+                    @YamlProperty(name = "expression", type = "string", required = true, description = "The expression value in your chosen language syntax", displayName = "Expression"),
+                    @YamlProperty(name = "id", type = "string", description = "Sets the id of this node", displayName = "Id"),
+                    @YamlProperty(name = "trim", type = "boolean", description = "Whether to trim the value to remove leading and trailing whitespaces and line breaks", displayName = "Trim")
+            }
+    )
+    public static class VariableExpressionDeserializer extends YamlDeserializerBase<VariableExpression> {
+        public VariableExpressionDeserializer() {
+            super(VariableExpression.class);
+        }
+
+        @Override
+        protected VariableExpression newInstance() {
+            return new VariableExpression();
+        }
+
+        @Override
+        protected VariableExpression newInstance(String value) {
+            return new VariableExpression(value);
+        }
+
+        @Override
+        protected boolean setProperty(VariableExpression target, String propertyKey,
+                String propertyName, Node node) {
+            propertyKey = org.apache.camel.util.StringHelper.dashToCamelCase(propertyKey);
+            switch(propertyKey) {
+                case "expression": {
+                    String val = asText(node);
+                    target.setExpression(val);
+                    break;
+                }
+                case "id": {
+                    String val = asText(node);
+                    target.setId(val);
+                    break;
+                }
+                case "trim": {
+                    String val = asText(node);
+                    target.setTrim(val);
+                    break;
+                }
+                default: {
+                    ExpressionDefinition ed = target.getExpressionType();
+                    if (ed != null) {
+                        throw new org.apache.camel.dsl.yaml.common.exception.DuplicateFieldException(node, propertyName, "as an expression");
+                    }
+                    ed = ExpressionDeserializers.constructExpressionType(propertyKey, node);
+                    if (ed != null) {
+                        target.setExpressionType(ed);
+                    } else {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+    }
+
     @YamlType(
             nodes = "weighted",
             types = org.apache.camel.model.loadbalancer.WeightedLoadBalancerDefinition.class,
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java
index 77fa9f0cb16..30f67d67481 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java
@@ -545,6 +545,8 @@ public final class ModelDeserializersResolver implements YamlDeserializerResolve
             case "org.apache.camel.model.validator.ValidatorsDefinition": return new ModelDeserializers.ValidatorsDefinitionDeserializer();
             case "value": return new ModelDeserializers.ValueDefinitionDeserializer();
             case "org.apache.camel.model.ValueDefinition": return new ModelDeserializers.ValueDefinitionDeserializer();
+            case "variable": return new ModelDeserializers.VariableExpressionDeserializer();
+            case "org.apache.camel.model.language.VariableExpression": return new ModelDeserializers.VariableExpressionDeserializer();
             case "weighted": return new ModelDeserializers.WeightedLoadBalancerDefinitionDeserializer();
             case "org.apache.camel.model.loadbalancer.WeightedLoadBalancerDefinition": return new ModelDeserializers.WeightedLoadBalancerDefinitionDeserializer();
             case "when": return new ModelDeserializers.WhenDefinitionDeserializer();
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index ab37e0111dd..b0b668b9a7c 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -978,6 +978,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -1050,6 +1052,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -1110,6 +1113,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -1183,6 +1188,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -1243,6 +1249,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -1355,6 +1363,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -1492,6 +1501,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -1660,6 +1671,14 @@
                 "$ref" : "#/items/definitions/org.apache.camel.model.language.TokenizerExpression"
               }
             }
+          }, {
+            "type" : "object",
+            "required" : [ "variable" ],
+            "properties" : {
+              "variable" : {
+                "$ref" : "#/items/definitions/org.apache.camel.model.language.VariableExpression"
+              }
+            }
           }, {
             "type" : "object",
             "required" : [ "xpath" ],
@@ -1708,6 +1727,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { }
@@ -1853,6 +1873,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -1921,6 +1943,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -2073,6 +2096,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -2161,6 +2186,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -2563,6 +2589,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -2641,6 +2669,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -3702,6 +3731,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -3805,6 +3836,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -3913,6 +3945,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -3960,6 +3994,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -4021,6 +4056,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -4155,6 +4192,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -4530,6 +4568,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -4619,6 +4659,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -5201,6 +5242,8 @@
                   "required" : [ "spel" ]
                 }, {
                   "required" : [ "tokenize" ]
+                }, {
+                  "required" : [ "variable" ]
                 }, {
                   "required" : [ "xpath" ]
                 }, {
@@ -5274,6 +5317,7 @@
             "simple" : { },
             "spel" : { },
             "tokenize" : { },
+            "variable" : { },
             "xpath" : { },
             "xquery" : { },
             "xtokenize" : { },
@@ -5470,6 +5514,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -5527,6 +5573,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -5587,6 +5634,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -5644,6 +5693,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -5737,6 +5787,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -5799,6 +5851,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -5889,6 +5942,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -5951,6 +6006,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -6012,6 +6068,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -6074,6 +6132,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -6135,6 +6194,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -6197,6 +6258,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -6257,6 +6319,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -6387,6 +6451,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -6711,6 +6776,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -6793,6 +6860,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -7049,6 +7117,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -7116,6 +7186,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -7699,6 +7770,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -7761,6 +7834,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -7838,6 +7912,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -7901,6 +7977,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -7961,6 +8038,8 @@
                 "required" : [ "spel" ]
               }, {
                 "required" : [ "tokenize" ]
+              }, {
+                "required" : [ "variable" ]
               }, {
                 "required" : [ "xpath" ]
               }, {
@@ -8024,6 +8103,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { },
@@ -12654,6 +12734,14 @@
                 "$ref" : "#/items/definitions/org.apache.camel.model.language.TokenizerExpression"
               }
             }
+          }, {
+            "type" : "object",
+            "required" : [ "variable" ],
+            "properties" : {
+              "variable" : {
+                "$ref" : "#/items/definitions/org.apache.camel.model.language.VariableExpression"
+              }
+            }
           }, {
             "type" : "object",
             "required" : [ "xpath" ],
@@ -12702,6 +12790,7 @@
           "simple" : { },
           "spel" : { },
           "tokenize" : { },
+          "variable" : { },
           "xpath" : { },
           "xquery" : { },
           "xtokenize" : { }
@@ -13407,6 +13496,34 @@
         } ],
         "required" : [ "token" ]
       },
+      "org.apache.camel.model.language.VariableExpression" : {
+        "title" : "Variable",
+        "description" : "Gets a variable",
+        "oneOf" : [ {
+          "type" : "string"
+        }, {
+          "type" : "object",
+          "additionalProperties" : false,
+          "properties" : {
+            "expression" : {
+              "type" : "string",
+              "title" : "Expression",
+              "description" : "The expression value in your chosen language syntax"
+            },
+            "id" : {
+              "type" : "string",
+              "title" : "Id",
+              "description" : "Sets the id of this node"
+            },
+            "trim" : {
+              "type" : "boolean",
+              "title" : "Trim",
+              "description" : "Whether to trim the value to remove leading and trailing whitespaces and line breaks"
+            }
+          }
+        } ],
+        "required" : [ "expression" ]
+      },
       "org.apache.camel.model.language.XMLTokenizerExpression" : {
         "title" : "XML Tokenize",
         "description" : "Tokenize XML payloads.",
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableTest.groovy
index e348a488cc2..4965e0017ab 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableTest.groovy
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableTest.groovy
@@ -49,8 +49,7 @@ class ToVariableTest extends YamlTestSupport {
                         - to:
                             uri: mock:after
                         - setBody:
-                            simple:
-                              expression: "${variable:bye}"
+                            variable: bye
                         - to:
                             uri: mock:result
                 - route:
@@ -127,8 +126,7 @@ class ToVariableTest extends YamlTestSupport {
                         - to:
                             uri: mock:after
                         - setBody:
-                            simple:
-                              expression: "${variable:bye}"
+                            variable: bye
                         - to:
                             uri: mock:result
                 - route:
@@ -204,8 +202,7 @@ class ToVariableTest extends YamlTestSupport {
                         - to:
                             uri: mock:after
                         - setBody:
-                            simple:
-                              expression: "${variable:bye}"
+                            variable: bye
                         - to:
                             uri: mock:result
                 - route:


(camel) 16/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0fcf3c7d984cb4b75c2797f3a9196972c8ff8670
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Jan 23 13:44:06 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../org/apache/camel/catalog/models/marshal.json   |   4 +-
 .../org/apache/camel/catalog/models/unmarshal.json |   4 +-
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  44 ++++
 .../processor/SpringMarshalVariableTest.java       |  29 +++
 .../processor/SpringUnmarshalVariableTest.java     |  29 +++
 .../camel/spring/processor/MarshalVariableTest.xml |  68 ++++++
 .../spring/processor/UnmarshalVariableTest.xml     |  68 ++++++
 .../dsl/yaml/deserializers/ModelDeserializers.java |  24 ++
 .../generated/resources/schema/camelYamlDsl.json   |  20 ++
 .../camel/dsl/yaml/MarshalVariableTest.groovy      | 270 +++++++++++++++++++++
 .../camel/dsl/yaml/UnmarshalVariableTest.groovy    | 268 ++++++++++++++++++++
 11 files changed, 826 insertions(+), 2 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/marshal.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/marshal.json
index 178597ebd63..3f33b46e27e 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/marshal.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/marshal.json
@@ -15,6 +15,8 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "dataFormatType": { "index": 3, "kind": "element", "displayName": "Data Format Type", "required": true, "type": "object", "javaType": "org.apache.camel.model.DataFormatDefinition", "oneOf": [ "asn1", "avro", "barcode", "base64", "bindy", "cbor", "crypto", "csv", "custom", "fhirJson", "fhirXml", "flatpack", "grok", "gzipDeflater", "hl7", "ical", "jacksonXml", "jaxb", "json", "jsonApi", "lzf", "mimeMultipart", "parquetAvro", "pgp", "protobuf", "rss", "soap", "swiftMt", "swiftMx", "sysl [...]
+    "dataFormatType": { "index": 3, "kind": "element", "displayName": "Data Format Type", "required": true, "type": "object", "javaType": "org.apache.camel.model.DataFormatDefinition", "oneOf": [ "asn1", "avro", "barcode", "base64", "bindy", "cbor", "crypto", "csv", "custom", "fhirJson", "fhirXml", "flatpack", "grok", "gzipDeflater", "hl7", "ical", "jacksonXml", "jaxb", "json", "jsonApi", "lzf", "mimeMultipart", "parquetAvro", "pgp", "protobuf", "rss", "soap", "swiftMt", "swiftMx", "sysl [...]
+    "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
   }
 }
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/unmarshal.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/unmarshal.json
index fe21bf9f735..202b7a8f74b 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/unmarshal.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/unmarshal.json
@@ -16,6 +16,8 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "dataFormatType": { "index": 3, "kind": "element", "displayName": "Data Format Type", "required": true, "type": "object", "javaType": "org.apache.camel.model.DataFormatDefinition", "oneOf": [ "asn1", "avro", "barcode", "base64", "bindy", "cbor", "crypto", "csv", "custom", "fhirJson", "fhirXml", "flatpack", "grok", "gzipDeflater", "hl7", "ical", "jacksonXml", "jaxb", "json", "jsonApi", "lzf", "mimeMultipart", "parquetAvro", "pgp", "protobuf", "rss", "soap", "swiftMt", "swiftMx", "sysl [...]
-    "allowNullBody": { "index": 4, "kind": "attribute", "displayName": "Allow Null Body", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Indicates whether null is allowed as value of a body to unmarshall." }
+    "variableSend": { "index": 4, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the  [...]
+    "variableReceive": { "index": 5, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
+    "allowNullBody": { "index": 6, "kind": "attribute", "displayName": "Allow Null Body", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Indicates whether null is allowed as value of a body to unmarshall." }
   }
 }
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 0dc0cfd581e..37d14072f8a 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
@@ -7116,6 +7116,28 @@ down. Default value: false
             <xs:element ref="tns:zipFile"/>
           </xs:choice>
         </xs:sequence>
+        <xs:attribute name="variableSend" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="variableReceive" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
       </xs:extension>
     </xs:complexContent>
   </xs:complexType>
@@ -13952,6 +13974,28 @@ To type used as a target data type in the transformation.
             <xs:element ref="tns:zipFile"/>
           </xs:choice>
         </xs:sequence>
+        <xs:attribute name="variableSend" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="variableReceive" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
         <xs:attribute name="allowNullBody" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringMarshalVariableTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringMarshalVariableTest.java
new file mode 100644
index 00000000000..8f320040fa9
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringMarshalVariableTest.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.MarshalVariableTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringMarshalVariableTest extends MarshalVariableTest {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/MarshalVariableTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringUnmarshalVariableTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringUnmarshalVariableTest.java
new file mode 100644
index 00000000000..09918671cdb
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringUnmarshalVariableTest.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.UnmarshalVariableTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringUnmarshalVariableTest extends UnmarshalVariableTest {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/UnmarshalVariableTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/MarshalVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/MarshalVariableTest.xml
new file mode 100644
index 00000000000..9e1c7b0a510
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/MarshalVariableTest.xml
@@ -0,0 +1,68 @@
+<?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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <bean id="myDF" class="org.apache.camel.processor.MarshalVariableTest.MyByeDataFormat"/>
+
+  <!-- START SNIPPET: example -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+    <route>
+      <from uri="direct:send"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <marshal variableSend="hello">
+        <custom ref="myDF"/>
+      </marshal>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:receive"/>
+      <marshal variableReceive="bye">
+        <custom ref="myDF"/>
+      </marshal>
+      <to uri="mock:after"/>
+      <setBody>
+        <simple>${variable:bye}</simple>
+      </setBody>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:sendAndReceive"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <marshal variableSend="hello" variableReceive="bye">
+        <custom ref="myDF"/>
+      </marshal>
+      <to uri="mock:result"/>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: example -->
+
+</beans>
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/UnmarshalVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/UnmarshalVariableTest.xml
new file mode 100644
index 00000000000..845acabd1cf
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/UnmarshalVariableTest.xml
@@ -0,0 +1,68 @@
+<?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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <bean id="myDF" class="org.apache.camel.processor.UnmarshalVariableTest.MyByeDataFormat"/>
+
+  <!-- START SNIPPET: example -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+    <route>
+      <from uri="direct:send"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <unmarshal variableSend="hello">
+        <custom ref="myDF"/>
+      </unmarshal>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:receive"/>
+      <unmarshal variableReceive="bye">
+        <custom ref="myDF"/>
+      </unmarshal>
+      <to uri="mock:after"/>
+      <setBody>
+        <simple>${variable:bye}</simple>
+      </setBody>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:sendAndReceive"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <unmarshal variableSend="hello" variableReceive="bye">
+        <custom ref="myDF"/>
+      </unmarshal>
+      <to uri="mock:result"/>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: example -->
+
+</beans>
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 b6116965d2a..940c91db3e9 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
@@ -9058,6 +9058,8 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "univocityCsv", type = "object:org.apache.camel.model.dataformat.UniVocityCsvDataFormat", oneOf = "dataFormatType"),
                     @YamlProperty(name = "univocityFixed", type = "object:org.apache.camel.model.dataformat.UniVocityFixedDataFormat", oneOf = "dataFormatType"),
                     @YamlProperty(name = "univocityTsv", type = "object:org.apache.camel.model.dataformat.UniVocityTsvDataFormat", oneOf = "dataFormatType"),
+                    @YamlProperty(name = "variableReceive", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Receive"),
+                    @YamlProperty(name = "variableSend", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Send"),
                     @YamlProperty(name = "xmlSecurity", type = "object:org.apache.camel.model.dataformat.XMLSecurityDataFormat", oneOf = "dataFormatType"),
                     @YamlProperty(name = "yaml", type = "object:org.apache.camel.model.dataformat.YAMLDataFormat", oneOf = "dataFormatType"),
                     @YamlProperty(name = "zipDeflater", type = "object:org.apache.camel.model.dataformat.ZipDeflaterDataFormat", oneOf = "dataFormatType"),
@@ -9294,6 +9296,16 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     target.setInheritErrorHandler(java.lang.Boolean.valueOf(val));
                     break;
                 }
+                case "variableReceive": {
+                    String val = asText(node);
+                    target.setVariableReceive(val);
+                    break;
+                }
+                case "variableSend": {
+                    String val = asText(node);
+                    target.setVariableSend(val);
+                    break;
+                }
                 case "id": {
                     String val = asText(node);
                     target.setId(val);
@@ -19266,6 +19278,8 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "univocityCsv", type = "object:org.apache.camel.model.dataformat.UniVocityCsvDataFormat", oneOf = "dataFormatType"),
                     @YamlProperty(name = "univocityFixed", type = "object:org.apache.camel.model.dataformat.UniVocityFixedDataFormat", oneOf = "dataFormatType"),
                     @YamlProperty(name = "univocityTsv", type = "object:org.apache.camel.model.dataformat.UniVocityTsvDataFormat", oneOf = "dataFormatType"),
+                    @YamlProperty(name = "variableReceive", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Receive"),
+                    @YamlProperty(name = "variableSend", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Send"),
                     @YamlProperty(name = "xmlSecurity", type = "object:org.apache.camel.model.dataformat.XMLSecurityDataFormat", oneOf = "dataFormatType"),
                     @YamlProperty(name = "yaml", type = "object:org.apache.camel.model.dataformat.YAMLDataFormat", oneOf = "dataFormatType"),
                     @YamlProperty(name = "zipDeflater", type = "object:org.apache.camel.model.dataformat.ZipDeflaterDataFormat", oneOf = "dataFormatType"),
@@ -19507,6 +19521,16 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     target.setInheritErrorHandler(java.lang.Boolean.valueOf(val));
                     break;
                 }
+                case "variableReceive": {
+                    String val = asText(node);
+                    target.setVariableReceive(val);
+                    break;
+                }
+                case "variableSend": {
+                    String val = asText(node);
+                    target.setVariableSend(val);
+                    break;
+                }
                 case "id": {
                     String val = asText(node);
                     target.setId(val);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 8737486cc97..ab37e0111dd 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -3075,6 +3075,16 @@
             "title" : "Id",
             "description" : "Sets the id of this node"
           },
+          "variableReceive" : {
+            "type" : "string",
+            "title" : "Variable Receive",
+            "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
+          },
+          "variableSend" : {
+            "type" : "string",
+            "title" : "Variable Send",
+            "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
+          },
           "asn1" : { },
           "avro" : { },
           "barcode" : { },
@@ -7176,6 +7186,16 @@
             "title" : "Id",
             "description" : "Sets the id of this node"
           },
+          "variableReceive" : {
+            "type" : "string",
+            "title" : "Variable Receive",
+            "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
+          },
+          "variableSend" : {
+            "type" : "string",
+            "title" : "Variable Send",
+            "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
+          },
           "asn1" : { },
           "avro" : { },
           "barcode" : { },
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/MarshalVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/MarshalVariableTest.groovy
new file mode 100644
index 00000000000..05ba3cf1742
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/MarshalVariableTest.groovy
@@ -0,0 +1,270 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.Exchange
+import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+import org.apache.camel.spi.DataFormat
+import org.apache.camel.support.service.ServiceSupport
+
+class MarshalVariableTest extends YamlTestSupport {
+
+    def "marshalVariable send"() {
+        setup:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - marshal:
+                            custom: myDF
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - marshal:
+                            custom: myDF
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - marshal:
+                            custom: myDF
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+            '''
+
+            withMock('mock:before') {
+                expectedBodiesReceived 'World'
+                expectedVariableReceived("hello", "Camel")
+            }
+            withMock('mock:result') {
+                expectedBodiesReceived 'Bye Camel'
+                expectedVariableReceived("hello", "Camel")
+            }
+
+        when:
+            context.registry.bind("myDF", new MyByeDataFormat())
+            context.start()
+
+            withTemplate {
+                to('direct:send').withBody('World').send()
+            }
+
+        then:
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
+
+    def "marshalVariable receive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - marshal:
+                            custom: myDF
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - marshal:
+                            custom: myDF
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - marshal:
+                            custom: myDF
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:after') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'Bye World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+
+        when:
+        context.registry.bind("myDF", new MyByeDataFormat())
+        context.start()
+
+        withTemplate {
+            to('direct:receive').withBody('World').send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+
+    def "marshalVariable sendAndReceive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - marshal:
+                            custom: myDF
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - marshal:
+                            custom: myDF
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - marshal:
+                            custom: myDF
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:before') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("hello", "Camel")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye Camel")
+        }
+
+        when:
+        context.registry.bind("myDF", new MyByeDataFormat())
+        context.start()
+
+        withTemplate {
+            to('direct:sendAndReceive').withBody('World').send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+
+    static class MyByeDataFormat extends ServiceSupport implements DataFormat {
+
+        @Override
+        void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
+            String line = "Bye " + graph.toString();
+            stream.write(line.getBytes());
+        }
+
+        @Override
+        Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
+            // noop
+            return null;
+        }
+    }
+
+}
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/UnmarshalVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/UnmarshalVariableTest.groovy
new file mode 100644
index 00000000000..a03879a6186
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/UnmarshalVariableTest.groovy
@@ -0,0 +1,268 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.Exchange
+import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+import org.apache.camel.spi.DataFormat
+import org.apache.camel.support.service.ServiceSupport
+
+class UnmarshalVariableTest extends YamlTestSupport {
+
+    def "unmarshalVariable send"() {
+        setup:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - unmarshal:
+                            custom: myDF
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - unmarshal:
+                            custom: myDF
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - unmarshal:
+                            custom: myDF
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+            '''
+
+            withMock('mock:before') {
+                expectedBodiesReceived 'World'
+                expectedVariableReceived("hello", "Camel")
+            }
+            withMock('mock:result') {
+                expectedBodiesReceived 'Bye Camel'
+                expectedVariableReceived("hello", "Camel")
+            }
+
+        when:
+            context.registry.bind("myDF", new MyByeDataFormat())
+            context.start()
+
+            withTemplate {
+                to('direct:send').withBody('World').send()
+            }
+
+        then:
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
+
+    def "unmarshalVariable receive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - unmarshal:
+                            custom: myDF
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - unmarshal:
+                            custom: myDF
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - unmarshal:
+                            custom: myDF
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:after') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'Bye World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+
+        when:
+        context.registry.bind("myDF", new MyByeDataFormat())
+        context.start()
+
+        withTemplate {
+            to('direct:receive').withBody('World').send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+
+    def "unmarshalVariable sendAndReceive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - unmarshal:
+                            custom: myDF
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - unmarshal:
+                            custom: myDF
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - unmarshal:
+                            custom: myDF
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:before') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("hello", "Camel")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye Camel")
+        }
+
+        when:
+        context.registry.bind("myDF", new MyByeDataFormat())
+        context.start()
+
+        withTemplate {
+            to('direct:sendAndReceive').withBody('World').send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+
+    static class MyByeDataFormat extends ServiceSupport implements DataFormat {
+
+        @Override
+        void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {
+            // noop
+        }
+
+        @Override
+        Object unmarshal(Exchange exchange, InputStream stream) throws Exception {
+            return "Bye " + exchange.getContext().getTypeConverter().convertTo(String.class, exchange, stream);
+        }
+    }
+
+}


(camel) 18/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a5f85f6b473953ab697c048bc132a4b548e9feca
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Jan 23 14:16:44 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 docs/components/modules/languages/examples/json/variable.json  | 1 +
 docs/components/modules/languages/nav.adoc                     | 1 +
 docs/components/modules/languages/pages/variable-language.adoc | 1 +
 3 files changed, 3 insertions(+)

diff --git a/docs/components/modules/languages/examples/json/variable.json b/docs/components/modules/languages/examples/json/variable.json
new file mode 120000
index 00000000000..4837e31d37e
--- /dev/null
+++ b/docs/components/modules/languages/examples/json/variable.json
@@ -0,0 +1 @@
+../../../../../../core/camel-core-languages/src/generated/resources/org/apache/camel/language/variable/variable.json
\ No newline at end of file
diff --git a/docs/components/modules/languages/nav.adoc b/docs/components/modules/languages/nav.adoc
index fa140f5e631..b085a11a290 100644
--- a/docs/components/modules/languages/nav.adoc
+++ b/docs/components/modules/languages/nav.adoc
@@ -23,6 +23,7 @@
 ** xref:simple-language.adoc[Simple]
 ** xref:spel-language.adoc[SpEL]
 ** xref:tokenize-language.adoc[Tokenize]
+** xref:variable-language.adoc[Variable]
 ** xref:xtokenize-language.adoc[XML Tokenize]
 ** xref:xpath-language.adoc[XPath]
 ** xref:xquery-language.adoc[XQuery]
diff --git a/docs/components/modules/languages/pages/variable-language.adoc b/docs/components/modules/languages/pages/variable-language.adoc
new file mode 120000
index 00000000000..7e300b990f4
--- /dev/null
+++ b/docs/components/modules/languages/pages/variable-language.adoc
@@ -0,0 +1 @@
+../../../../../core/camel-core-languages/src/main/docs/modules/languages/pages/variable-language.adoc
\ No newline at end of file


(camel) 13/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 6a783e47ea2c13bab908719bf1efcd758af6804f
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue Jan 23 10:51:27 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../apache/camel/catalog/models/pollEnrich.json    | 17 +++---
 .../apache/camel/catalog/schemas/camel-spring.xsd  | 11 ++++
 .../processor/SpringPollEnrichVariableTest.java    | 29 ++++++++++
 .../spring/processor/PollEnrichVariableTest.xml    | 44 +++++++++++++++
 .../org/apache/camel/model/pollEnrich.json         | 17 +++---
 .../apache/camel/model/PollEnrichDefinition.java   | 22 ++++++++
 .../org/apache/camel/processor/PollEnricher.java   | 28 ++++++++++
 .../apache/camel/reifier/PollEnrichReifier.java    |  1 +
 .../org/apache/camel/reifier/RouteReifier.java     |  2 +-
 .../java/org/apache/camel/reifier/SendReifier.java |  4 +-
 .../org/apache/camel/reifier/ToDynamicReifier.java |  4 +-
 .../org/apache/camel/reifier/WireTapReifier.java   |  4 +-
 .../camel/processor/PollEnrichVariableTest.java    | 52 ++++++++++++++++++
 .../management/mbean/ManagedPollEnricherMBean.java |  3 +
 .../management/mbean/ManagedPollEnricher.java      |  5 ++
 .../java/org/apache/camel/xml/in/ModelParser.java  |  1 +
 .../java/org/apache/camel/xml/out/ModelWriter.java |  1 +
 .../org/apache/camel/yaml/out/ModelWriter.java     |  1 +
 .../dsl/yaml/deserializers/ModelDeserializers.java |  8 ++-
 .../generated/resources/schema/camelYamlDsl.json   |  5 ++
 .../camel/dsl/yaml/PollEnrichVariableTest.groovy   | 64 ++++++++++++++++++++++
 21 files changed, 299 insertions(+), 24 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pollEnrich.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pollEnrich.json
index e3ec0d66eb8..9f4fb8bafc5 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pollEnrich.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pollEnrich.json
@@ -16,13 +16,14 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
-    "aggregationStrategy": { "index": 4, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
-    "aggregationStrategyMethodName": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
-    "aggregationStrategyMethodAllowNull": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Allow Null", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "If this option is false then the aggregate method is not used if there was no data to enrich. If this option is true then null values is used as the oldExchange (when no data to enrich), when us [...]
-    "aggregateOnException": { "index": 7, "kind": "attribute", "displayName": "Aggregate On Exception", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If this option is false then the aggregate method is not used if there was an exception thrown while trying to retrieve the data to enrich from the resource. Setting this option to true allows end us [...]
-    "timeout": { "index": 8, "kind": "attribute", "displayName": "Timeout", "required": false, "type": "duration", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "-1", "description": "Timeout in millis when polling from the external service. The timeout has influence about the poll enrich behavior. It basically operations in three different modes: negative value - Waits until a message is available and then returns it. Warning th [...]
-    "cacheSize": { "index": 9, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ConsumerCache which is used to cache and reuse consumers when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can be utilized. If each dynamic en [...]
-    "ignoreInvalidEndpoint": { "index": 10, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
-    "autoStartComponents": { "index": 11, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when poll enricher is starting up." }
+    "variableReceive": { "index": 4, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
+    "aggregationStrategy": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
+    "aggregationStrategyMethodName": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
+    "aggregationStrategyMethodAllowNull": { "index": 7, "kind": "attribute", "displayName": "Aggregation Strategy Method Allow Null", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "If this option is false then the aggregate method is not used if there was no data to enrich. If this option is true then null values is used as the oldExchange (when no data to enrich), when us [...]
+    "aggregateOnException": { "index": 8, "kind": "attribute", "displayName": "Aggregate On Exception", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If this option is false then the aggregate method is not used if there was an exception thrown while trying to retrieve the data to enrich from the resource. Setting this option to true allows end us [...]
+    "timeout": { "index": 9, "kind": "attribute", "displayName": "Timeout", "required": false, "type": "duration", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "-1", "description": "Timeout in millis when polling from the external service. The timeout has influence about the poll enrich behavior. It basically operations in three different modes: negative value - Waits until a message is available and then returns it. Warning th [...]
+    "cacheSize": { "index": 10, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ConsumerCache which is used to cache and reuse consumers when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can be utilized. If each dynamic e [...]
+    "ignoreInvalidEndpoint": { "index": 11, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
+    "autoStartComponents": { "index": 12, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when poll enricher is starting up." }
   }
 }
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 9360b3518d2..be712c5bf41 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
@@ -11115,6 +11115,17 @@ Sets a reference to use for lookup the policy in the registry.
           <xs:element ref="tns:xpath"/>
           <xs:element ref="tns:xquery"/>
         </xs:choice>
+        <xs:attribute name="variableReceive" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
         <xs:attribute name="aggregationStrategy" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringPollEnrichVariableTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringPollEnrichVariableTest.java
new file mode 100644
index 00000000000..71278e79a04
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringPollEnrichVariableTest.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.PollEnrichVariableTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringPollEnrichVariableTest extends PollEnrichVariableTest {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/PollEnrichVariableTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/PollEnrichVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/PollEnrichVariableTest.xml
new file mode 100644
index 00000000000..cbe82854d8c
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/PollEnrichVariableTest.xml
@@ -0,0 +1,44 @@
+<?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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <!-- START SNIPPET: example -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+    <route>
+      <from uri="direct:receive"/>
+      <pollEnrich timeout="1000" variableReceive="bye">
+        <constant>seda:foo</constant>
+      </pollEnrich>
+      <to uri="mock:after"/>
+      <setBody>
+        <simple>${variable:bye}</simple>
+      </setBody>
+      <to uri="mock:result"/>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: example -->
+
+</beans>
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/pollEnrich.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/pollEnrich.json
index e3ec0d66eb8..9f4fb8bafc5 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/pollEnrich.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/pollEnrich.json
@@ -16,13 +16,14 @@
     "description": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
     "expression": { "index": 3, "kind": "expression", "displayName": "Expression", "required": true, "type": "object", "javaType": "org.apache.camel.model.language.ExpressionDefinition", "oneOf": [ "constant", "csimple", "datasonnet", "exchangeProperty", "groovy", "header", "hl7terser", "java", "joor", "jq", "js", "jsonpath", "language", "method", "mvel", "ognl", "python", "ref", "simple", "spel", "tokenize", "xpath", "xquery", "xtokenize" ], "deprecated": false, "autowired": false, "sec [...]
-    "aggregationStrategy": { "index": 4, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
-    "aggregationStrategyMethodName": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
-    "aggregationStrategyMethodAllowNull": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Allow Null", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "If this option is false then the aggregate method is not used if there was no data to enrich. If this option is true then null values is used as the oldExchange (when no data to enrich), when us [...]
-    "aggregateOnException": { "index": 7, "kind": "attribute", "displayName": "Aggregate On Exception", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If this option is false then the aggregate method is not used if there was an exception thrown while trying to retrieve the data to enrich from the resource. Setting this option to true allows end us [...]
-    "timeout": { "index": 8, "kind": "attribute", "displayName": "Timeout", "required": false, "type": "duration", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "-1", "description": "Timeout in millis when polling from the external service. The timeout has influence about the poll enrich behavior. It basically operations in three different modes: negative value - Waits until a message is available and then returns it. Warning th [...]
-    "cacheSize": { "index": 9, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ConsumerCache which is used to cache and reuse consumers when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can be utilized. If each dynamic en [...]
-    "ignoreInvalidEndpoint": { "index": 10, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
-    "autoStartComponents": { "index": 11, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when poll enricher is starting up." }
+    "variableReceive": { "index": 4, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
+    "aggregationStrategy": { "index": 5, "kind": "attribute", "displayName": "Aggregation Strategy", "required": false, "type": "object", "javaType": "org.apache.camel.AggregationStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing message. By default Camel will use the reply from the external service as outgoing message." },
+    "aggregationStrategyMethodName": { "index": 6, "kind": "attribute", "displayName": "Aggregation Strategy Method Name", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "This option can be used to explicit declare the method name to use, when using POJOs as the AggregationStrategy." },
+    "aggregationStrategyMethodAllowNull": { "index": 7, "kind": "attribute", "displayName": "Aggregation Strategy Method Allow Null", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "If this option is false then the aggregate method is not used if there was no data to enrich. If this option is true then null values is used as the oldExchange (when no data to enrich), when us [...]
+    "aggregateOnException": { "index": 8, "kind": "attribute", "displayName": "Aggregate On Exception", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If this option is false then the aggregate method is not used if there was an exception thrown while trying to retrieve the data to enrich from the resource. Setting this option to true allows end us [...]
+    "timeout": { "index": 9, "kind": "attribute", "displayName": "Timeout", "required": false, "type": "duration", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "-1", "description": "Timeout in millis when polling from the external service. The timeout has influence about the poll enrich behavior. It basically operations in three different modes: negative value - Waits until a message is available and then returns it. Warning th [...]
+    "cacheSize": { "index": 10, "kind": "attribute", "displayName": "Cache Size", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the maximum size used by the org.apache.camel.spi.ConsumerCache which is used to cache and reuse consumers when uris are reused. Beware that when using dynamic endpoints then it affects how well the cache can be utilized. If each dynamic e [...]
+    "ignoreInvalidEndpoint": { "index": 11, "kind": "attribute", "displayName": "Ignore Invalid Endpoint", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Ignore the invalidate endpoint exception when try to create a producer with that endpoint" },
+    "autoStartComponents": { "index": 12, "kind": "attribute", "displayName": "Auto Start Components", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether to auto startup components when poll enricher is starting up." }
   }
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/PollEnrichDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/PollEnrichDefinition.java
index b61e9604c68..c471bdbb73d 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/PollEnrichDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/PollEnrichDefinition.java
@@ -39,6 +39,8 @@ public class PollEnrichDefinition extends ExpressionNode implements AggregationS
     @XmlTransient
     private AggregationStrategy aggregationStrategyBean;
 
+    @XmlAttribute
+    private String variableReceive;
     @XmlAttribute
     @Metadata(javaType = "org.apache.camel.AggregationStrategy")
     private String aggregationStrategy;
@@ -130,6 +132,18 @@ public class PollEnrichDefinition extends ExpressionNode implements AggregationS
         return this;
     }
 
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public PollEnrichDefinition variableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+        return this;
+    }
+
     /**
      * Sets the AggregationStrategy to be used to merge the reply from the external service, into a single outgoing
      * message. By default Camel will use the reply from the external service as outgoing message.
@@ -277,6 +291,14 @@ public class PollEnrichDefinition extends ExpressionNode implements AggregationS
         this.timeout = timeout;
     }
 
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     public String getAggregationStrategy() {
         return aggregationStrategy;
     }
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/PollEnricher.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/PollEnricher.java
index 109266a521b..ca029a6525a 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/PollEnricher.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/PollEnricher.java
@@ -68,6 +68,7 @@ public class PollEnricher extends AsyncProcessorSupport implements IdAware, Rout
     private String id;
     private String routeId;
     private AggregationStrategy aggregationStrategy;
+    private String variableReceive;
     private final Expression expression;
     private final String uri;
     private long timeout;
@@ -152,6 +153,14 @@ public class PollEnricher extends AsyncProcessorSupport implements IdAware, Rout
         this.aggregationStrategy = aggregationStrategy;
     }
 
+    public String getVariableReceive() {
+        return variableReceive;
+    }
+
+    public void setVariableReceive(String variableReceive) {
+        this.variableReceive = variableReceive;
+    }
+
     public long getTimeout() {
         return timeout;
     }
@@ -308,6 +317,19 @@ public class PollEnricher extends AsyncProcessorSupport implements IdAware, Rout
             cause = resourceExchange.getException();
         }
 
+        // if we should store the received message body in a variable,
+        // then we need to preserve the original message body
+        Object originalBody = null;
+        if (variableReceive != null) {
+            try {
+                originalBody = exchange.getMessage().getBody();
+            } catch (Exception throwable) {
+                exchange.setException(throwable);
+                callback.done(true);
+                return true;
+            }
+        }
+
         try {
             if (!isAggregateOnException() && resourceExchange != null && resourceExchange.isFailed()) {
                 // copy resource exchange onto original exchange (preserving pattern)
@@ -321,6 +343,12 @@ public class PollEnricher extends AsyncProcessorSupport implements IdAware, Rout
                 // must catch any exception from aggregation
                 Exchange aggregatedExchange = aggregationStrategy.aggregate(exchange, resourceExchange);
                 if (aggregatedExchange != null) {
+                    if (variableReceive != null) {
+                        // result should be stored in variable instead of message body
+                        Object value = aggregatedExchange.getMessage().getBody();
+                        ExchangeHelper.setVariable(exchange, variableReceive, value);
+                        aggregatedExchange.getMessage().setBody(originalBody);
+                    }
                     // copy aggregation result onto original exchange (preserving pattern)
                     copyResultsPreservePattern(exchange, aggregatedExchange);
                     // handover any synchronization
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 4ddcbcc6ed8..2fb2f72a281 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
@@ -67,6 +67,7 @@ public class PollEnrichReifier extends ProcessorReifier<PollEnrichDefinition> {
         if (num != null) {
             enricher.setCacheSize(num);
         }
+        enricher.setVariableReceive(parseString(definition.getVariableReceive()));
         enricher.setIgnoreInvalidEndpoint(parseBoolean(definition.getIgnoreInvalidEndpoint(), false));
         enricher.setAggregateOnException(parseBoolean(definition.getAggregateOnException(), false));
         if (definition.getAutoStartComponents() != null) {
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
index 97a8b686a02..d53870207a7 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
@@ -111,7 +111,7 @@ public class RouteReifier extends ProcessorReifier<RouteDefinition> {
         route.setErrorHandlerFactory(definition.getErrorHandlerFactory());
 
         // configure variable
-        String variable = definition.getInput().getVariableReceive();
+        String variable = parseString(definition.getInput().getVariableReceive());
         if (variable != null) {
             // when using variable we need to turn on original message
             route.setAllowUseOriginalMessage(true);
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SendReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SendReifier.java
index 4eaa13a4f17..f01407d6f87 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SendReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/SendReifier.java
@@ -35,8 +35,8 @@ public class SendReifier extends ProcessorReifier<ToDefinition> {
     @Override
     public Processor createProcessor() throws Exception {
         SendProcessor answer = new SendProcessor(resolveEndpoint(), parse(ExchangePattern.class, definition.getPattern()));
-        answer.setVariableSend(definition.getVariableSend());
-        answer.setVariableReceive(definition.getVariableReceive());
+        answer.setVariableSend(parseString(definition.getVariableSend()));
+        answer.setVariableReceive(parseString(definition.getVariableReceive()));
         return answer;
     }
 
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
index cfa004429fa..240d5e18e20 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/ToDynamicReifier.java
@@ -56,8 +56,8 @@ public class ToDynamicReifier<T extends ToDynamicDefinition> extends ProcessorRe
         SendDynamicProcessor processor = new SendDynamicProcessor(uri, exp);
         processor.setCamelContext(camelContext);
         processor.setPattern(parse(ExchangePattern.class, definition.getPattern()));
-        processor.setVariableSend(definition.getVariableSend());
-        processor.setVariableReceive(definition.getVariableReceive());
+        processor.setVariableSend(parseString(definition.getVariableSend()));
+        processor.setVariableReceive(parseString(definition.getVariableReceive()));
         Integer num = parseInt(definition.getCacheSize());
         if (num != null) {
             processor.setCacheSize(num);
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/WireTapReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/WireTapReifier.java
index 08942ac00d5..ac8b651c097 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/WireTapReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/WireTapReifier.java
@@ -84,8 +84,8 @@ public class WireTapReifier extends ToDynamicReifier<WireTapDefinition<?>> {
             Endpoint endpoint = CamelContextHelper.resolveEndpoint(camelContext, uri, null);
             LineNumberAware.trySetLineNumberAware(endpoint, definition);
             sendProcessor = new SendProcessor(endpoint);
-            sendProcessor.setVariableSend(definition.getVariableSend());
-            sendProcessor.setVariableReceive(definition.getVariableReceive());
+            sendProcessor.setVariableSend(parseString(definition.getVariableSend()));
+            sendProcessor.setVariableReceive(parseString(definition.getVariableReceive()));
         }
 
         // create error handler we need to use for processing the wire tapped
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/PollEnrichVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/PollEnrichVariableTest.java
new file mode 100644
index 00000000000..43109571c6e
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/PollEnrichVariableTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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 PollEnrichVariableTest extends ContextTestSupport {
+
+    @Test
+    public void testReceive() throws Exception {
+        template.sendBody("seda:foo", "Bye World");
+
+        getMockEndpoint("mock:after").expectedBodiesReceived("World");
+        getMockEndpoint("mock:after").expectedVariableReceived("bye", "Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye World");
+
+        template.sendBody("direct:receive", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:receive")
+                        .pollEnrich().constant("seda:foo").timeout(1000).variableReceive("bye")
+                        .to("mock:after")
+                        .setBody(simple("${variable:bye}"))
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPollEnricherMBean.java b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPollEnricherMBean.java
index f0486f10014..9e677be2500 100644
--- a/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPollEnricherMBean.java
+++ b/core/camel-management-api/src/main/java/org/apache/camel/api/management/mbean/ManagedPollEnricherMBean.java
@@ -30,6 +30,9 @@ public interface ManagedPollEnricherMBean extends ManagedProcessorMBean, Managed
                       mask = true)
     String getExpression();
 
+    @ManagedAttribute(description = "Variable to store the received message body (only body, not headers)")
+    String getVariableReceive();
+
     @ManagedAttribute(description = "Timeout in millis when polling from the external service")
     Long getTimeout();
 
diff --git a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPollEnricher.java b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPollEnricher.java
index 703e145c827..09d93a1b9d6 100644
--- a/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPollEnricher.java
+++ b/core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedPollEnricher.java
@@ -89,6 +89,11 @@ public class ManagedPollEnricher extends ManagedProcessor implements ManagedPoll
         return uri;
     }
 
+    @Override
+    public String getVariableReceive() {
+        return processor.getVariableReceive();
+    }
+
     @Override
     public Long getTimeout() {
         return processor.getTimeout();
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 ccc33dcc6d1..6a4f5892c57 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
@@ -763,6 +763,7 @@ public class ModelParser extends BaseParser {
                 case "cacheSize": def.setCacheSize(val); break;
                 case "ignoreInvalidEndpoint": def.setIgnoreInvalidEndpoint(val); break;
                 case "timeout": def.setTimeout(val); break;
+                case "variableReceive": def.setVariableReceive(val); break;
                 default: return processorDefinitionAttributeHandler().accept(def, key, val);
             }
             return true;
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index b012cca083c..29a3ac921fe 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -1762,6 +1762,7 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
         doWriteAttribute("cacheSize", def.getCacheSize());
         doWriteAttribute("aggregationStrategy", def.getAggregationStrategy());
         doWriteAttribute("ignoreInvalidEndpoint", def.getIgnoreInvalidEndpoint());
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index cecac392932..974cebf1f05 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -1762,6 +1762,7 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteProcessorDefinitionAttributes(def);
+        doWriteAttribute("variableReceive", def.getVariableReceive());
         doWriteAttribute("cacheSize", def.getCacheSize());
         doWriteAttribute("aggregationStrategy", def.getAggregationStrategy());
         doWriteAttribute("ignoreInvalidEndpoint", def.getIgnoreInvalidEndpoint());
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 d0bf6091766..7ef2c0a270e 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
@@ -11283,7 +11283,8 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "id", type = "string", description = "Sets the id of this node", displayName = "Id"),
                     @YamlProperty(name = "ignoreInvalidEndpoint", type = "boolean", description = "Ignore the invalidate endpoint exception when try to create a producer with that endpoint", displayName = "Ignore Invalid Endpoint"),
                     @YamlProperty(name = "inheritErrorHandler", type = "boolean"),
-                    @YamlProperty(name = "timeout", type = "string", defaultValue = "-1", description = "Timeout in millis when polling from the external service. The timeout has influence about the poll enrich behavior. It basically operations in three different modes: negative value - Waits until a message is available and then returns it. Warning that this method could block indefinitely if no messages are available. 0 - Attempts to receive a message exchange immediately without waiti [...]
+                    @YamlProperty(name = "timeout", type = "string", defaultValue = "-1", description = "Timeout in millis when polling from the external service. The timeout has influence about the poll enrich behavior. It basically operations in three different modes: negative value - Waits until a message is available and then returns it. Warning that this method could block indefinitely if no messages are available. 0 - Attempts to receive a message exchange immediately without waiti [...]
+                    @YamlProperty(name = "variableReceive", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Receive")
             }
     )
     public static class PollEnrichDefinitionDeserializer extends YamlDeserializerBase<PollEnrichDefinition> {
@@ -11356,6 +11357,11 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     target.setTimeout(val);
                     break;
                 }
+                case "variableReceive": {
+                    String val = asText(node);
+                    target.setVariableReceive(val);
+                    break;
+                }
                 case "id": {
                     String val = asText(node);
                     target.setId(val);
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 4f7c733fd1c..bafd77a84db 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -3759,6 +3759,11 @@
             "description" : "Timeout in millis when polling from the external service. The timeout has influence about the poll enrich behavior. It basically operations in three different modes: negative value - Waits until a message is available and then returns it. Warning that this method could block indefinitely if no messages are available. 0 - Attempts to receive a message exchange immediately without waiting and returning null if a message exchange is not available yet. positive v [...]
             "default" : "-1"
           },
+          "variableReceive" : {
+            "type" : "string",
+            "title" : "Variable Receive",
+            "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
+          },
           "constant" : { },
           "csimple" : { },
           "datasonnet" : { },
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/PollEnrichVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/PollEnrichVariableTest.groovy
new file mode 100644
index 00000000000..1552af9c881
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/PollEnrichVariableTest.groovy
@@ -0,0 +1,64 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+
+class PollEnrichVariableTest extends YamlTestSupport {
+
+    def "pollEnrichVariable receive"() {
+        setup:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - pollEnrich:  
+                            constant: "seda:foo"
+                            timeout: "1000"
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple: ${variable:bye}
+                        - to:
+                            uri: mock:result
+            '''
+
+            withMock('mock:after') {
+                expectedBodiesReceived 'World'
+                expectedVariableReceived("bye", "Bye World")
+            }
+            withMock('mock:result') {
+                expectedBodiesReceived 'Bye World'
+                expectedVariableReceived("bye", "Bye World")
+            }
+
+        when:
+            context.start()
+
+            withTemplate {
+                to('seda:foo').withBody('Bye World').send()
+                to('direct:receive').withBody('World').send()
+            }
+
+        then:
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
+}


(camel) 12/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ab41934d2e43b6fde323611a268449c7702f03df
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Jan 22 20:56:22 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../processor/SpringWireTapVariableTest.java       | 29 ++++++++
 .../camel/spring/processor/WireTapVariableTest.xml | 49 ++++++++++++++
 .../apache/camel/model/ProcessorDefinition.java    | 34 ++++++++++
 .../org/apache/camel/model/WireTapDefinition.java  | 27 +++++++-
 .../org/apache/camel/reifier/WireTapReifier.java   |  6 ++
 .../camel/processor/WireTapVariableTest.java       | 56 ++++++++++++++++
 .../camel/dsl/yaml/WireTapVariableTest.groovy      | 77 ++++++++++++++++++++++
 7 files changed, 276 insertions(+), 2 deletions(-)

diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringWireTapVariableTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringWireTapVariableTest.java
new file mode 100644
index 00000000000..45f9b00ad36
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringWireTapVariableTest.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.WireTapVariableTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringWireTapVariableTest extends WireTapVariableTest {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/WireTapVariableTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/WireTapVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/WireTapVariableTest.xml
new file mode 100644
index 00000000000..9dac908cf36
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/WireTapVariableTest.xml
@@ -0,0 +1,49 @@
+<?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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <!-- START SNIPPET: example -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+    <route>
+      <from uri="direct:send"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <wireTap uri="direct:foo" variableSend="hello"/>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:foo"/>
+      <transform>
+        <simple>Bye ${body}</simple>
+      </transform>
+      <to uri="mock:tap"/>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: example -->
+
+</beans>
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
index 63a1cd5e5b8..f6a584ce6c9 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
@@ -2074,6 +2074,23 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
         return answer;
     }
 
+    /**
+     * <a href="http://camel.apache.org/wiretap.html">WireTap EIP:</a> Sends messages to all its child outputs; so that
+     * each processor and destination gets a copy of the original message to avoid the processors interfering with each
+     * other using {@link ExchangePattern#InOnly}.
+     *
+     * @param  endpoint     the endpoint to wiretap to
+     * @param  variableSend to use a variable as the source for the message body to send.
+     * @return              the builder
+     */
+    public WireTapDefinition<Type> wireTap(@AsEndpointUri EndpointProducerBuilder endpoint, String variableSend) {
+        WireTapDefinition answer = new WireTapDefinition();
+        answer.setEndpointProducerBuilder(endpoint);
+        answer.setVariableSend(variableSend);
+        addOutput(answer);
+        return answer;
+    }
+
     /**
      * <a href="http://camel.apache.org/wiretap.html">WireTap EIP:</a> Sends messages to all its child outputs; so that
      * each processor and destination gets a copy of the original message to avoid the processors interfering with each
@@ -2089,6 +2106,23 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
         return answer;
     }
 
+    /**
+     * <a href="http://camel.apache.org/wiretap.html">WireTap EIP:</a> Sends messages to all its child outputs; so that
+     * each processor and destination gets a copy of the original message to avoid the processors interfering with each
+     * other using {@link ExchangePattern#InOnly}.
+     *
+     * @param  uri          the dynamic endpoint to wiretap to (resolved using simple language by default)
+     * @param  variableSend to use a variable as the source for the message body to send.
+     * @return              the builder
+     */
+    public WireTapDefinition<Type> wireTap(@AsEndpointUri String uri, String variableSend) {
+        WireTapDefinition answer = new WireTapDefinition();
+        answer.setUri(uri);
+        answer.setVariableSend(variableSend);
+        addOutput(answer);
+        return answer;
+    }
+
     /**
      * Pushes the given block on the stack as current block
      *
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/WireTapDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/WireTapDefinition.java
index 5ea132c8068..adbe410f64b 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/WireTapDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/WireTapDefinition.java
@@ -25,6 +25,7 @@ import jakarta.xml.bind.annotation.XmlRootElement;
 import jakarta.xml.bind.annotation.XmlTransient;
 
 import org.apache.camel.ExchangePattern;
+import org.apache.camel.Message;
 import org.apache.camel.Processor;
 import org.apache.camel.spi.Metadata;
 
@@ -38,8 +39,6 @@ import org.apache.camel.spi.Metadata;
 public class WireTapDefinition<Type extends ProcessorDefinition<Type>> extends ToDynamicDefinition
         implements ExecutorServiceAwareDefinition<WireTapDefinition<Type>> {
 
-    // TODO: variables due to extending ToDynamic
-
     @XmlTransient
     private ExecutorService executorServiceBean;
     @XmlTransient
@@ -262,6 +261,30 @@ public class WireTapDefinition<Type extends ProcessorDefinition<Type>> extends T
         return this;
     }
 
+    /**
+     * To use a variable as the source for the message body to send. This makes it handy to use variables for user data
+     * and to easily control what data to use for sending and receiving.
+     *
+     * Important: When using send variable then the message body is taken from this variable instead of the current
+     * {@link Message}, however the headers from the {@link Message} will still be used as well. In other words, the
+     * variable is used instead of the message body, but everything else is as usual.
+     */
+    public WireTapDefinition<Type> variableReceive(String variableReceive) {
+        throw new IllegalArgumentException("WireTap does not support variableReceive");
+    }
+
+    /**
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
+     *
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
+     */
+    public WireTapDefinition<Type> variableSend(String variableSend) {
+        setVariableSend(variableSend);
+        return this;
+    }
+
     // Properties
     // -------------------------------------------------------------------------
 
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/WireTapReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/WireTapReifier.java
index d8562141286..08942ac00d5 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/WireTapReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/WireTapReifier.java
@@ -46,6 +46,10 @@ public class WireTapReifier extends ToDynamicReifier<WireTapDefinition<?>> {
 
     @Override
     public Processor createProcessor() throws Exception {
+        if (definition.getVariableReceive() != null) {
+            throw new IllegalArgumentException("WireTap does not support variableReceive");
+        }
+
         // must use InOnly for WireTap
         definition.setPattern(ExchangePattern.InOnly.name());
 
@@ -80,6 +84,8 @@ public class WireTapReifier extends ToDynamicReifier<WireTapDefinition<?>> {
             Endpoint endpoint = CamelContextHelper.resolveEndpoint(camelContext, uri, null);
             LineNumberAware.trySetLineNumberAware(endpoint, definition);
             sendProcessor = new SendProcessor(endpoint);
+            sendProcessor.setVariableSend(definition.getVariableSend());
+            sendProcessor.setVariableReceive(definition.getVariableReceive());
         }
 
         // create error handler we need to use for processing the wire tapped
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/WireTapVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/WireTapVariableTest.java
new file mode 100644
index 00000000000..af0857056ca
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/WireTapVariableTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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 WireTapVariableTest extends ContextTestSupport {
+
+    @Test
+    public void testSend() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:tap").expectedBodiesReceived("Bye Camel");
+        getMockEndpoint("mock:tap").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("World");
+        getMockEndpoint("mock:result").expectedVariableReceived("hello", "Camel");
+
+        template.sendBody("direct:send", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:send")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .wireTap("direct:foo", "hello")
+                        .to("mock:result");
+
+                from("direct:foo")
+                        .transform().simple("Bye ${body}")
+                        .to("mock:tap");
+            }
+        };
+    }
+}
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/WireTapVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/WireTapVariableTest.groovy
new file mode 100644
index 00000000000..0b226c234b9
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/WireTapVariableTest.groovy
@@ -0,0 +1,77 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+
+class WireTapVariableTest extends YamlTestSupport {
+
+    def "wireTapVariable send"() {
+        setup:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - wireTap:
+                            uri: direct:foo
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+                        - to:
+                            uri: mock:tap
+            '''
+
+            withMock('mock:before') {
+                expectedBodiesReceived 'World'
+                expectedVariableReceived("hello", "Camel")
+            }
+            withMock('mock:result') {
+                expectedBodiesReceived 'World'
+                expectedVariableReceived("hello", "Camel")
+            }
+            withMock('mock:tap') {
+                expectedBodiesReceived 'Bye Camel'
+                expectedVariableReceived("hello", "Camel")
+            }
+
+        when:
+            context.start()
+
+            withTemplate {
+                to('direct:send').withBody('World').send()
+            }
+
+        then:
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
+}


(camel) 07/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit aeb54da09abce4eb25311ab995a0577130ce3fbc
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Jan 20 15:47:53 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../org/apache/camel/catalog/models/to.json        |   6 +-
 .../apache/camel/catalog/schemas/camel-spring.xsd  |  29 ++-
 .../spring/processor/SpringToVariableTest.java     |  29 +++
 .../camel/spring/processor/ToVariableTest.xml      |  66 ++++++
 .../org/apache/camel/model/FromDefinition.java     |   4 +-
 .../apache/camel/model/ProcessorDefinition.java    |   8 +-
 .../org/apache/camel/model/RouteDefinition.java    |  20 +-
 .../org/apache/camel/model/RoutesDefinition.java   |  20 +-
 .../java/org/apache/camel/model/ToDefinition.java  |  19 +-
 .../org/apache/camel/processor/SendProcessor.java  |  43 +++-
 .../camel/processor/SetVariableProcessor.java      |  25 +-
 .../org/apache/camel/reifier/RouteReifier.java     |   3 +-
 .../bean/BeanCreateBodyExceptionTest.java          |   4 +-
 .../org/apache/camel/processor/ToVariableTest.java |  44 +++-
 .../org/apache/camel/support/ExchangeHelper.java   |  45 ++++
 .../dsl/yaml/deserializers/ModelDeserializers.java |   4 +-
 .../generated/resources/schema/camelYamlDsl.json   |   8 +-
 .../apache/camel/dsl/yaml/ToVariableTest.groovy    | 255 +++++++++++++++++++++
 18 files changed, 557 insertions(+), 75 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/to.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/to.json
index 053e6c95045..8f8698291c4 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/to.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/to.json
@@ -15,7 +15,9 @@
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
     "disabled": { "index": 2, "kind": "attribute", "displayName": "Disabled", "label": "advanced", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to disable this EIP from the route during build time. Once an EIP has been disabled then it cannot be enabled later at runtime." },
-    "uri": { "index": 3, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the uri of the endpoint to send to." },
-    "pattern": { "index": 4, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" }
+    "variableSend": { "index": 3, "kind": "attribute", "displayName": "Variable Send", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this variab [...]
+    "variableReceive": { "index": 4, "kind": "attribute", "displayName": "Variable Receive", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not o [...]
+    "uri": { "index": 5, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the uri of the endpoint to send to." },
+    "pattern": { "index": 6, "kind": "attribute", "displayName": "Pattern", "label": "advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the optional ExchangePattern used to invoke this endpoint" }
   }
 }
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 868efc3a0f3..a465a963fc8 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
@@ -6096,12 +6096,12 @@ Sets the URI of the endpoint to use.
             </xs:documentation>
           </xs:annotation>
         </xs:attribute>
-        <xs:attribute name="variable" type="xs:string">
+        <xs:attribute name="variableReceive" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
 <![CDATA[
-To use a variable to store a copy of the incoming message body (only body, not headers). This is handy for easy access
-to the incoming message body via variables.
+To use a variable to store a copy of the received message body (only body, not headers). This is handy for easy access
+to the received message body via variables.
 ]]>
             </xs:documentation>
           </xs:annotation>
@@ -13524,6 +13524,29 @@ Reference to the exception instance to lookup from the registry to throw.
     <xs:complexContent>
       <xs:extension base="tns:sendDefinition">
         <xs:sequence/>
+        <xs:attribute name="variableSend" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to
+easily control what data to use for sending and receiving. Important: When using send variable then the message body is
+taken from this variable instead of the current Message , however the headers from the Message will still be used as
+well. In other words, the variable is used instead of the message body, but everything else is as usual.
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="variableReceive" type="xs:string">
+          <xs:annotation>
+            <xs:documentation xml:lang="en">
+<![CDATA[
+To use a variable to store the received message body (only body, not headers). This is handy for easy access to the
+received message body via variables. Important: When using receive variable then the received body is stored only in
+this variable and not on the current org.apache.camel.Message .
+]]>
+            </xs:documentation>
+          </xs:annotation>
+        </xs:attribute>
         <xs:attribute name="pattern" type="xs:string">
           <xs:annotation>
             <xs:documentation xml:lang="en">
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringToVariableTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringToVariableTest.java
new file mode 100644
index 00000000000..644fc0894e0
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringToVariableTest.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.ToVariableTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringToVariableTest extends ToVariableTest {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/ToVariableTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToVariableTest.xml
new file mode 100644
index 00000000000..0c23f7592b3
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/ToVariableTest.xml
@@ -0,0 +1,66 @@
+<?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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <!-- START SNIPPET: example -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+    <route>
+      <from uri="direct:send"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <to uri="direct:foo" variableSend="hello"/>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:receive"/>
+      <to uri="direct:foo" variableReceive="bye"/>
+      <to uri="mock:after"/>
+      <setBody>
+        <simple>${variable:bye}</simple>
+      </setBody>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:sendAndReceive"/>
+      <setVariable name="hello">
+        <simple>Camel</simple>
+      </setVariable>
+      <to uri="mock:before"/>
+      <to uri="direct:foo" variableSend="hello" variableReceive="bye"/>
+      <to uri="mock:result"/>
+    </route>
+    <route>
+      <from uri="direct:foo"/>
+      <transform>
+        <simple>Bye ${body}</simple>
+      </transform>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: example -->
+
+</beans>
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
index c23893241cf..dc5d4b95665 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
@@ -126,8 +126,8 @@ public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition>
     }
 
     /**
-     * To use a variable to store a copy of the received message body (only body, not headers).
-     * This is handy for easy access to the received message body via variables.
+     * To use a variable to store a copy of the received message body (only body, not headers). This is handy for easy
+     * access to the received message body via variables.
      */
     public void setVariableReceive(String variableReceive) {
         this.variableReceive = variableReceive;
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
index b4bf7f5605f..5d6e933a95b 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java
@@ -258,10 +258,10 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type>
     /**
      * Sends the exchange to the given endpoint
      *
-     * @param  uri the endpoint to send to
-     * @param variableSend to use a variable as the source for the message body to send.
-     * @param variableReceive to use a variable to store the received message body (only body, not headers).
-     * @return     the builder
+     * @param  uri             the endpoint to send to
+     * @param  variableSend    to use a variable as the source for the message body to send.
+     * @param  variableReceive to use a variable to store the received message body (only body, not headers).
+     * @return                 the builder
      */
     public Type toV(@AsEndpointUri String uri, String variableSend, String variableReceive) {
         ToDefinition to = new ToDefinition(uri);
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 c62560e9259..7b274a5ef1c 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
@@ -218,12 +218,12 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition>
     }
 
     /**
-     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not headers).
-     * This is handy for easy access to the received message body via variables.
+     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not
+     * headers). This is handy for easy access to the received message body via variables.
      *
-     * @param  uri            the from uri
-     * @param variableReceive the name of the variable
-     * @return                the builder
+     * @param  uri             the from uri
+     * @param  variableReceive the name of the variable
+     * @return                 the builder
      */
     public RouteDefinition fromV(@AsEndpointUri String uri, String variableReceive) {
         FromDefinition from = new FromDefinition(uri);
@@ -233,12 +233,12 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition>
     }
 
     /**
-     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not headers).
-     * This is handy for easy access to the received message body via variables.
+     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not
+     * headers). This is handy for easy access to the received message body via variables.
      *
-     * @param  endpoint       the from endpoint
-     * @param variableReceive the name of the variable
-     * @return                the builder
+     * @param  endpoint        the from endpoint
+     * @param  variableReceive the name of the variable
+     * @return                 the builder
      */
     public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variableReceive) {
         FromDefinition from = new FromDefinition(endpoint);
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
index 81b013e4d0b..f67b702dabb 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
@@ -197,12 +197,12 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
     }
 
     /**
-     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not headers).
-     * This is handy for easy access to the received message body via variables.
+     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not
+     * headers). This is handy for easy access to the received message body via variables.
      *
-     * @param uri             the from uri
-     * @param variableReceive the name of the variable
-     * @return                the builder
+     * @param  uri             the from uri
+     * @param  variableReceive the name of the variable
+     * @return                 the builder
      */
     public RouteDefinition fromV(@AsEndpointUri String uri, String variableReceive) {
         RouteDefinition route = createRoute();
@@ -235,12 +235,12 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
     }
 
     /**
-     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not headers).
-     * This is handy for easy access to the received message body via variables.
+     * Creates an input to the route, and uses a variable to store a copy of the received message body (only body, not
+     * headers). This is handy for easy access to the received message body via variables.
      *
-     * @param  endpoint       the from endpoint
-     * @param variableReceive the name of the variable
-     * @return                the builder
+     * @param  endpoint        the from endpoint
+     * @param  variableReceive the name of the variable
+     * @return                 the builder
      */
     public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variableReceive) {
         RouteDefinition route = createRoute();
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ToDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ToDefinition.java
index 4b4864e59c8..fed74435cd4 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/ToDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ToDefinition.java
@@ -103,13 +103,12 @@ public class ToDefinition extends SendDefinition<ToDefinition> {
     }
 
     /**
-     * To use a variable as the source for the message body to send.
-     * This makes it handy to use variables for user data and to easily control what data to use for sending and receiving.
+     * To use a variable as the source for the message body to send. This makes it handy to use variables for user data
+     * and to easily control what data to use for sending and receiving.
      *
-     * Important: When using send variable then the message body
-     * is taken from this variable instead of the current {@link Message}, however
-     * the headers from the {@link Message} will still be used as well. In other words,
-     * the variable is used instead of the message body, but everything else is as usual.
+     * Important: When using send variable then the message body is taken from this variable instead of the current
+     * {@link Message}, however the headers from the {@link Message} will still be used as well. In other words, the
+     * variable is used instead of the message body, but everything else is as usual.
      */
     public void setVariableSend(String variableSend) {
         this.variableSend = variableSend;
@@ -120,11 +119,11 @@ public class ToDefinition extends SendDefinition<ToDefinition> {
     }
 
     /**
-     * To use a variable to store the received message body (only body, not headers).
-     * This is handy for easy access to the received message body via variables.
+     * To use a variable to store the received message body (only body, not headers). This is handy for easy access to
+     * the received message body via variables.
      *
-     * Important: When using receive variable then the received body is stored
-     * only in this variable and <b>not</b> on the current {@link org.apache.camel.Message}.
+     * Important: When using receive variable then the received body is stored only in this variable and <b>not</b> on
+     * the current {@link org.apache.camel.Message}.
      */
     public void setVariableReceive(String variableReceive) {
         this.variableReceive = variableReceive;
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
index 326f103a4cc..960450d8d44 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SendProcessor.java
@@ -33,6 +33,7 @@ import org.apache.camel.spi.RouteIdAware;
 import org.apache.camel.support.AsyncProcessorSupport;
 import org.apache.camel.support.EndpointHelper;
 import org.apache.camel.support.EventHelper;
+import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.cache.DefaultProducerCache;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.ObjectHelper;
@@ -127,6 +128,20 @@ public class SendProcessor extends AsyncProcessorSupport implements Traceable, E
         // if you want to permanently to change the MEP then use .setExchangePattern in the DSL
         final ExchangePattern existingPattern = exchange.getPattern();
 
+        // if we should store the received message body in a variable,
+        // then we need to preserve the original message body
+        Object body = null;
+        if (variableReceive != null) {
+            try {
+                body = exchange.getMessage().getBody();
+            } catch (Exception throwable) {
+                exchange.setException(throwable);
+                callback.done(true);
+                return true;
+            }
+        }
+        final Object originalBody = body;
+
         if (extendedStatistics) {
             counter.incrementAndGet();
         }
@@ -153,10 +168,16 @@ public class SendProcessor extends AsyncProcessorSupport implements Traceable, E
 
             // optimize to only create a new callback if really needed, otherwise we can use the provided callback as-is
             AsyncCallback ac = callback;
-            boolean newCallback = watch != null || existingPattern != target.getPattern();
+            boolean newCallback = watch != null || existingPattern != target.getPattern() || variableReceive != null;
             if (newCallback) {
                 ac = doneSync -> {
                     try {
+                        // result should be stored in variable instead of message body
+                        if (variableReceive != null) {
+                            Object value = exchange.getMessage().getBody();
+                            ExchangeHelper.setVariable(exchange, variableReceive, value);
+                            exchange.getMessage().setBody(originalBody);
+                        }
                         // restore previous MEP
                         target.setPattern(existingPattern);
                         // emit event that the exchange was sent to the endpoint
@@ -170,6 +191,13 @@ public class SendProcessor extends AsyncProcessorSupport implements Traceable, E
                 };
             }
             try {
+                // replace message body with variable
+                if (variableSend != null) {
+                    // it may be a global variable
+                    Object value = ExchangeHelper.getVariable(exchange, variableSend);
+                    exchange.getMessage().setBody(value);
+                }
+
                 LOG.debug(">>>> {} {}", destination, exchange);
                 boolean sync = producer.process(exchange, ac);
                 if (!sync) {
@@ -190,6 +218,13 @@ public class SendProcessor extends AsyncProcessorSupport implements Traceable, E
             // set property which endpoint we send to
             exchange.setProperty(ExchangePropertyKey.TO_ENDPOINT, destination.getEndpointUri());
 
+            // replace message body with variable
+            if (variableSend != null) {
+                // it may be a global variable
+                Object value = ExchangeHelper.getVariable(exchange, variableSend);
+                exchange.getMessage().setBody(value);
+            }
+
             LOG.debug(">>>> {} {}", destination, exchange);
 
             // send the exchange to the destination using the producer cache for the non optimized producers
@@ -197,6 +232,12 @@ public class SendProcessor extends AsyncProcessorSupport implements Traceable, E
                     (producer, ex, cb) -> producer.process(ex, doneSync -> {
                         // restore previous MEP
                         exchange.setPattern(existingPattern);
+                        // result should be stored in variable instead of message body
+                        if (variableReceive != null) {
+                            Object value = exchange.getMessage().getBody();
+                            ExchangeHelper.setVariable(exchange, variableReceive, value);
+                            exchange.getMessage().setBody(originalBody);
+                        }
                         // signal we are done
                         cb.done(doneSync);
                     }));
diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SetVariableProcessor.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SetVariableProcessor.java
index 85acea646bf..b0e73f75617 100644
--- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/SetVariableProcessor.java
+++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/SetVariableProcessor.java
@@ -24,11 +24,9 @@ import org.apache.camel.Expression;
 import org.apache.camel.Traceable;
 import org.apache.camel.spi.IdAware;
 import org.apache.camel.spi.RouteIdAware;
-import org.apache.camel.spi.VariableRepository;
-import org.apache.camel.spi.VariableRepositoryFactory;
 import org.apache.camel.support.AsyncProcessorSupport;
+import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.StringHelper;
 
 /**
  * A processor which sets the variable with an {@link Expression}
@@ -40,7 +38,6 @@ public class SetVariableProcessor extends AsyncProcessorSupport implements Trace
     private String routeId;
     private final Expression variableName;
     private final Expression expression;
-    private VariableRepositoryFactory factory;
 
     public SetVariableProcessor(Expression variableName, Expression expression) {
         this.variableName = variableName;
@@ -71,19 +68,7 @@ public class SetVariableProcessor extends AsyncProcessorSupport implements Trace
             }
 
             String key = variableName.evaluate(exchange, String.class);
-            String id = StringHelper.before(key, ":");
-            if (id != null) {
-                VariableRepository repo = factory.getVariableRepository(id);
-                if (repo != null) {
-                    key = StringHelper.after(key, ":");
-                    repo.setVariable(key, newVariable);
-                } else {
-                    exchange.setException(
-                            new IllegalArgumentException("VariableRepository with id: " + id + " does not exist"));
-                }
-            } else {
-                exchange.setVariable(key, newVariable);
-            }
+            ExchangeHelper.setVariable(exchange, key, newVariable);
         } catch (Exception e) {
             exchange.setException(e);
         }
@@ -92,12 +77,6 @@ public class SetVariableProcessor extends AsyncProcessorSupport implements Trace
         return true;
     }
 
-    @Override
-    protected void doBuild() throws Exception {
-        super.doBuild();
-        factory = getCamelContext().getCamelContextExtension().getContextPlugin(VariableRepositoryFactory.class);
-    }
-
     @Override
     public String toString() {
         return id;
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
index 025497ace88..97a8b686a02 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
@@ -51,6 +51,7 @@ import org.apache.camel.spi.ManagementInterceptStrategy;
 import org.apache.camel.spi.NodeIdFactory;
 import org.apache.camel.spi.RoutePolicy;
 import org.apache.camel.spi.RoutePolicyFactory;
+import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.PluginHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -434,7 +435,7 @@ public class RouteReifier extends ProcessorReifier<RouteDefinition> {
         @Override
         public Object before(Exchange exchange) throws Exception {
             Object body = exchange.getMessage().getBody();
-            exchange.setVariable(name, body);
+            ExchangeHelper.setVariable(exchange, name, body);
             return null;
         }
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanCreateBodyExceptionTest.java b/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanCreateBodyExceptionTest.java
index 57d295e82ed..9ef9970c903 100644
--- a/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanCreateBodyExceptionTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanCreateBodyExceptionTest.java
@@ -118,14 +118,14 @@ public class BeanCreateBodyExceptionTest extends ContextTestSupport {
         fail.set(true);
 
         Exception e = assertThrows(Exception.class,
-                () -> consumer.receiveBody("seda:empty", 10000),
+                () -> consumer.receiveBody("seda:empty", 1000),
                 "Should throw exception");
 
         assertIsInstanceOf(IllegalArgumentException.class, e);
         assertEquals("Forced internal error", e.getMessage());
 
         fail.set(false);
-        assertDoesNotThrow(() -> consumer.receiveBody("seda:empty", 10000), "");
+        assertDoesNotThrow(() -> consumer.receiveBody("seda:empty", 1000), "");
     }
 
     @Override
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java
index 561ca0a06b0..b5fbc29565b 100644
--- a/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/ToVariableTest.java
@@ -23,11 +23,37 @@ import org.junit.jupiter.api.Test;
 public class ToVariableTest extends ContextTestSupport {
 
     @Test
-    public void testOriginalBody() throws Exception {
+    public void testSend() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye Camel");
+        getMockEndpoint("mock:result").expectedVariableReceived("hello", "Camel");
+
+        template.sendBody("direct:send", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testReceive() throws Exception {
         getMockEndpoint("mock:after").expectedBodiesReceived("World");
+        getMockEndpoint("mock:after").expectedVariableReceived("bye", "Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye World");
+
+        template.sendBody("direct:receive", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testSendAndReceive() throws Exception {
+        getMockEndpoint("mock:before").expectedBodiesReceived("World");
+        getMockEndpoint("mock:before").expectedVariableReceived("hello", "Camel");
         getMockEndpoint("mock:result").expectedBodiesReceived("World");
+        getMockEndpoint("mock:result").expectedVariableReceived("bye", "Bye Camel");
 
-        template.sendBody("direct:start", "World");
+        template.sendBody("direct:sendAndReceive", "World");
 
         assertMockEndpointsSatisfied();
     }
@@ -37,12 +63,24 @@ public class ToVariableTest extends ContextTestSupport {
         return new RouteBuilder() {
             @Override
             public void configure() throws Exception {
-                from("direct:start")
+                from("direct:send")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .toV("direct:foo", "hello", null)
+                        .to("mock:result");
+
+                from("direct:receive")
                         .toV("direct:foo", null, "bye")
                         .to("mock:after")
                         .setBody(simple("${variable:bye}"))
                         .to("mock:result");
 
+                from("direct:sendAndReceive")
+                        .setVariable("hello", simple("Camel"))
+                        .to("mock:before")
+                        .toV("direct:foo", "hello", "bye")
+                        .to("mock:result");
+
                 from("direct:foo")
                         .transform().simple("Bye ${body}");
             }
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java b/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
index b1097243892..f1bd96981c1 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/ExchangeHelper.java
@@ -1100,4 +1100,49 @@ public final class ExchangeHelper {
             exchange.setVariable(name, value);
         }
     }
+
+    /**
+     * Gets the variable
+     *
+     * @param  exchange the exchange
+     * @param  name     the variable name. Can be prefixed with repo-id:name to lookup the variable from a specific
+     *                  repository. If no repo-id is provided, then the variable is set on the exchange
+     * @return          the variable
+     */
+    public static Object getVariable(Exchange exchange, String name) {
+        Object answer;
+        String id = StringHelper.before(name, ":");
+        if (id != null) {
+            VariableRepositoryFactory factory
+                    = exchange.getContext().getCamelContextExtension().getContextPlugin(VariableRepositoryFactory.class);
+            VariableRepository repo = factory.getVariableRepository(id);
+            if (repo != null) {
+                name = StringHelper.after(name, ":");
+                answer = repo.getVariable(name);
+            } else {
+                throw new IllegalArgumentException("VariableRepository with id: " + id + " does not exist");
+            }
+        } else {
+            answer = exchange.getVariable(name);
+        }
+        return answer;
+    }
+
+    /**
+     * Gets the variable, converted to the given type
+     *
+     * @param  exchange the exchange
+     * @param  name     the variable name. Can be prefixed with repo-id:name to lookup the variable from a specific
+     *                  repository. If no repo-id is provided, then the variable is set on the exchange
+     * @param  type     the type to convert to
+     * @return          the variable
+     */
+    public static <T> T getVariable(Exchange exchange, String name, Class<T> type) {
+        Object answer = getVariable(exchange, name);
+        if (answer != null) {
+            return exchange.getContext().getTypeConverter().convertTo(type, exchange, answer);
+        }
+        return null;
+    }
+
 }
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 aea4c113504..cab2a31da16 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
@@ -18051,8 +18051,8 @@ public final class ModelDeserializers extends YamlDeserializerSupport {
                     @YamlProperty(name = "parameters", type = "object"),
                     @YamlProperty(name = "pattern", type = "enum:InOnly,InOut", description = "Sets the optional ExchangePattern used to invoke this endpoint", displayName = "Pattern"),
                     @YamlProperty(name = "uri", type = "string", required = true, description = "Sets the uri of the endpoint to send to.", displayName = "Uri"),
-                    @YamlProperty(name = "variableReceive", type = "string"),
-                    @YamlProperty(name = "variableSend", type = "string")
+                    @YamlProperty(name = "variableReceive", type = "string", description = "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message .", displayName = "Variable Receive"),
+                    @YamlProperty(name = "variableSend", type = "string", description = "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this variable instead of the current Message , however the headers from the Message will still be used as well. In other words, the variable is used ins [...]
             }
     )
     public static class ToDefinitionDeserializer extends YamlDeserializerEndpointAwareBase<ToDefinition> {
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 87797184ab2..6089a141ca4 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -6851,10 +6851,14 @@
               "description" : "Sets the uri of the endpoint to send to."
             },
             "variableReceive" : {
-              "type" : "string"
+              "type" : "string",
+              "title" : "Variable Receive",
+              "description" : "To use a variable to store the received message body (only body, not headers). This is handy for easy access to the received message body via variables. Important: When using receive variable then the received body is stored only in this variable and not on the current org.apache.camel.Message ."
             },
             "variableSend" : {
-              "type" : "string"
+              "type" : "string",
+              "title" : "Variable Send",
+              "description" : "To use a variable as the source for the message body to send. This makes it handy to use variables for user data and to easily control what data to use for sending and receiving. Important: When using send variable then the message body is taken from this variable instead of the current Message , however the headers from the Message will still be used as well. In other words, the variable is used instead of the message body, but everything else is as usual."
             }
           }
         } ],
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableTest.groovy
new file mode 100644
index 00000000000..e348a488cc2
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/ToVariableTest.groovy
@@ -0,0 +1,255 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.component.mock.MockEndpoint
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+
+class ToVariableTest extends YamlTestSupport {
+
+    def "toVariable send"() {
+        setup:
+            loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - to:
+                            uri: direct:foo
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - to:
+                            uri: direct:foo
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - to:
+                            uri: direct:foo
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+            withMock('mock:before') {
+                expectedBodiesReceived 'World'
+                expectedVariableReceived("hello", "Camel")
+            }
+            withMock('mock:result') {
+                expectedBodiesReceived 'Bye Camel'
+                expectedVariableReceived("hello", "Camel")
+            }
+
+        when:
+            context.start()
+
+            withTemplate {
+                to('direct:send').withBody('World').send()
+            }
+
+        then:
+            MockEndpoint.assertIsSatisfied(context)
+    }
+
+
+    def "toVariable receive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - to:
+                            uri: direct:foo
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - to:
+                            uri: direct:foo
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - to:
+                            uri: direct:foo
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:after') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'Bye World'
+            expectedVariableReceived("bye", "Bye World")
+        }
+
+        when:
+        context.start()
+
+        withTemplate {
+            to('direct:receive').withBody('World').send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+
+    def "toVariable sendAndReceive"() {
+        setup:
+        loadRoutes '''
+                - route:
+                    from:
+                      uri: direct:send
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - to:
+                            uri: direct:foo
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:receive
+                      steps:
+                        - to:
+                            uri: direct:foo
+                            variableReceive: bye
+                        - to:
+                            uri: mock:after
+                        - setBody:
+                            simple:
+                              expression: "${variable:bye}"
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:sendAndReceive
+                      steps:
+                        - setVariable:
+                            name: hello
+                            simple:
+                              expression: Camel
+                        - to:
+                            uri: mock:before
+                        - to:
+                            uri: direct:foo
+                            variableReceive: bye
+                            variableSend: hello
+                        - to:
+                            uri: mock:result
+                - route:
+                    from:
+                      uri: direct:foo
+                      steps:
+                        - transform:
+                            simple:
+                              expression: "Bye ${body}"
+            '''
+
+        withMock('mock:before') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("hello", "Camel")
+        }
+        withMock('mock:result') {
+            expectedBodiesReceived 'World'
+            expectedVariableReceived("bye", "Bye Camel")
+        }
+
+        when:
+        context.start()
+
+        withTemplate {
+            to('direct:sendAndReceive').withBody('World').send()
+        }
+
+        then:
+        MockEndpoint.assertIsSatisfied(context)
+    }
+}


(camel) 01/19: CAMEL-19749: EIPs should make it easy to use together with variables.

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 80784f015c624a3aceb6fc0d637a1b076e96d955
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Fri Jan 19 12:25:50 2024 +0100

    CAMEL-19749: EIPs should make it easy to use together with variables.
---
 .../org/apache/camel/catalog/models/from.json      |  3 +-
 .../spring/processor/SpringFromVariableTest.java   | 29 +++++++++++++
 .../camel/spring/processor/FromVariableTest.xml    | 42 +++++++++++++++++++
 .../resources/org/apache/camel/model/from.json     |  3 +-
 .../org/apache/camel/builder/RouteBuilder.java     | 46 ++++++++++++++++++++-
 .../org/apache/camel/model/FromDefinition.java     | 15 +++++++
 .../org/apache/camel/model/RouteDefinition.java    | 30 ++++++++++++++
 .../org/apache/camel/model/RoutesDefinition.java   | 34 +++++++++++++++
 .../org/apache/camel/reifier/RouteReifier.java     | 44 ++++++++++++++++++++
 .../apache/camel/processor/FromVariableTest.java   | 48 ++++++++++++++++++++++
 .../java/org/apache/camel/xml/in/ModelParser.java  |  9 ++--
 .../java/org/apache/camel/xml/out/ModelWriter.java |  1 +
 .../org/apache/camel/yaml/out/ModelWriter.java     |  1 +
 .../camel/builder/endpoint/FromVariableTest.java   | 48 ++++++++++++++++++++++
 14 files changed, 345 insertions(+), 8 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/from.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/from.json
index 6504f349019..b753ab0fa4f 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/from.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/from.json
@@ -14,6 +14,7 @@
   "properties": {
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
-    "uri": { "index": 2, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the URI of the endpoint to use" }
+    "uri": { "index": 2, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the URI of the endpoint to use" },
+    "variable": { "index": 3, "kind": "attribute", "displayName": "Variable", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store a copy of the incoming message body (only body, not headers). This is handy for easy access to the incoming message body via variables." }
   }
 }
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringFromVariableTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringFromVariableTest.java
new file mode 100644
index 00000000000..6d40ec5f7a0
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/processor/SpringFromVariableTest.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.spring.processor;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.processor.FromVariableTest;
+
+import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext;
+
+public class SpringFromVariableTest extends FromVariableTest {
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        return createSpringCamelContext(this, "org/apache/camel/spring/processor/FromVariableTest.xml");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/FromVariableTest.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/FromVariableTest.xml
new file mode 100644
index 00000000000..00e697e7f59
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/processor/FromVariableTest.xml
@@ -0,0 +1,42 @@
+<?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://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+    ">
+
+  <!-- START SNIPPET: example -->
+  <camelContext xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+    <route>
+      <from uri="direct:start" variable="myKey"/>
+      <transform><simple>Bye ${body}</simple></transform>
+      <to uri="mock:foo"/>
+      <setBody>
+        <simple>${variable:myKey}</simple>
+      </setBody>
+      <to uri="mock:result"/>
+    </route>
+  </camelContext>
+  <!-- END SNIPPET: example -->
+
+</beans>
diff --git a/core/camel-core-model/src/generated/resources/org/apache/camel/model/from.json b/core/camel-core-model/src/generated/resources/org/apache/camel/model/from.json
index 6504f349019..b753ab0fa4f 100644
--- a/core/camel-core-model/src/generated/resources/org/apache/camel/model/from.json
+++ b/core/camel-core-model/src/generated/resources/org/apache/camel/model/from.json
@@ -14,6 +14,7 @@
   "properties": {
     "id": { "index": 0, "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": { "index": 1, "kind": "element", "displayName": "Description", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the description of this node" },
-    "uri": { "index": 2, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the URI of the endpoint to use" }
+    "uri": { "index": 2, "kind": "attribute", "displayName": "Uri", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the URI of the endpoint to use" },
+    "variable": { "index": 3, "kind": "attribute", "displayName": "Variable", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a variable to store a copy of the incoming message body (only body, not headers). This is handy for easy access to the incoming message body via variables." }
   }
 }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
index 0be85d0471c..6c3d491ae99 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java
@@ -389,6 +389,24 @@ public abstract class RouteBuilder extends BuilderSupport implements RoutesBuild
         return answer;
     }
 
+    /**
+     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not
+     * headers). This is handy for easy access to the incoming message body via variables.
+     *
+     * @param  uri      the from uri
+     * @param  variable the name of the variable
+     * @return          the builder
+     */
+    public RouteDefinition fromV(String uri, String variable) {
+        getRouteCollection().setCamelContext(getContext());
+        if (resource != null) {
+            getRouteCollection().setResource(resource);
+        }
+        RouteDefinition answer = getRouteCollection().fromV(uri, variable);
+        configureRoute(answer);
+        return answer;
+    }
+
     /**
      * Creates a new route from the given endpoint
      *
@@ -405,12 +423,36 @@ public abstract class RouteBuilder extends BuilderSupport implements RoutesBuild
         return answer;
     }
 
-    public RouteDefinition from(EndpointConsumerBuilder endpointDefinition) {
+    /**
+     * Creates a new route from the given endpoint
+     *
+     * @param  endpoint the from endpoint
+     * @return          the builder
+     */
+    public RouteDefinition from(EndpointConsumerBuilder endpoint) {
+        getRouteCollection().setCamelContext(getContext());
+        if (resource != null) {
+            getRouteCollection().setResource(resource);
+        }
+        RouteDefinition answer = getRouteCollection().from(endpoint);
+        configureRoute(answer);
+        return answer;
+    }
+
+    /**
+     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not
+     * headers). This is handy for easy access to the incoming message body via variables.
+     *
+     * @param  endpoint the from endpoint
+     * @param  variable the name of the variable
+     * @return          the builder
+     */
+    public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variable) {
         getRouteCollection().setCamelContext(getContext());
         if (resource != null) {
             getRouteCollection().setResource(resource);
         }
-        RouteDefinition answer = getRouteCollection().from(endpointDefinition);
+        RouteDefinition answer = getRouteCollection().fromV(endpoint, variable);
         configureRoute(answer);
         return answer;
     }
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
index bde1beabd29..a0b29da52ee 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/FromDefinition.java
@@ -45,6 +45,8 @@ public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition>
     @XmlAttribute
     @Metadata(required = true)
     private String uri;
+    @XmlAttribute
+    private String variable;
 
     public FromDefinition() {
     }
@@ -119,6 +121,19 @@ public class FromDefinition extends OptionalIdentifiedDefinition<FromDefinition>
         this.uri = uri;
     }
 
+    public String getVariable() {
+        return variable;
+    }
+
+    /**
+     * To use a variable to store a copy of the incoming message body (only body, not headers).
+     *
+     * This is handy for easy access to the incoming message body via variables.
+     */
+    public void setVariable(String variable) {
+        this.variable = variable;
+    }
+
     /**
      * Gets tne endpoint if an {@link Endpoint} instance was set.
      * <p/>
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 1944ca9474e..dc4fc72c2cd 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
@@ -217,6 +217,36 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition>
         return this;
     }
 
+    /**
+     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not headers).
+     * This is handy for easy access to the incoming message body via variables.
+     *
+     * @param  uri the from uri
+     * @param variable the name of the variable
+     * @return     the builder
+     */
+    public RouteDefinition fromV(@AsEndpointUri String uri, String variable) {
+        FromDefinition from = new FromDefinition(uri);
+        from.setVariable(variable);
+        setInput(from);
+        return this;
+    }
+
+    /**
+     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not headers).
+     * This is handy for easy access to the incoming message body via variables.
+     *
+     * @param  endpoint the from endpoint
+     * @param variable the name of the variable
+     * @return          the builder
+     */
+    public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variable) {
+        FromDefinition from = new FromDefinition(endpoint);
+        from.setVariable(variable);
+        setInput(from);
+        return this;
+    }
+
     /**
      * The route configuration id or pattern this route should use for configuration. Multiple id/pattern can be
      * separated by comma.
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
index 3eca50291bb..d6dcb4832c0 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java
@@ -196,6 +196,20 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
         return route(route);
     }
 
+    /**
+     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not headers).
+     * This is handy for easy access to the incoming message body via variables.
+     *
+     * @param uri the from uri
+     * @param variable the name of the variable
+     * @return     the builder
+     */
+    public RouteDefinition fromV(@AsEndpointUri String uri, String variable) {
+        RouteDefinition route = createRoute();
+        route.fromV(uri, variable);
+        return route(route);
+    }
+
     /**
      * Creates a new route from the given endpoint
      *
@@ -208,12 +222,32 @@ public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinit
         return route(route);
     }
 
+    /**
+     * Creates a new route from the given endpoint
+     *
+     * @param  endpoint the from endpoint
+     * @return          the builder
+     */
     public RouteDefinition from(EndpointConsumerBuilder endpoint) {
         RouteDefinition route = createRoute();
         route.from(endpoint);
         return route(route);
     }
 
+    /**
+     * Creates an input to the route, and use a variable to store a copy of the incoming message body (only body, not headers).
+     * This is handy for easy access to the incoming message body via variables.
+     *
+     * @param  endpoint the from endpoint
+     * @param variable the name of the variable
+     * @return          the builder
+     */
+    public RouteDefinition fromV(EndpointConsumerBuilder endpoint, String variable) {
+        RouteDefinition route = createRoute();
+        route.fromV(endpoint, variable);
+        return route(route);
+    }
+
     /**
      * Creates a new route using the given route.
      * <p/>
diff --git a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
index ae12fef7187..8d1088b5e11 100644
--- a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
+++ b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/RouteReifier.java
@@ -26,6 +26,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.Endpoint;
 import org.apache.camel.EndpointConsumerResolver;
 import org.apache.camel.ErrorHandlerFactory;
+import org.apache.camel.Exchange;
 import org.apache.camel.FailedToCreateRouteException;
 import org.apache.camel.Processor;
 import org.apache.camel.Route;
@@ -41,6 +42,7 @@ import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.processor.ContractAdvice;
 import org.apache.camel.processor.RoutePipeline;
 import org.apache.camel.reifier.rest.RestBindingReifier;
+import org.apache.camel.spi.CamelInternalProcessorAdvice;
 import org.apache.camel.spi.Contract;
 import org.apache.camel.spi.ErrorHandlerAware;
 import org.apache.camel.spi.InternalProcessor;
@@ -100,12 +102,20 @@ public class RouteReifier extends ProcessorReifier<RouteDefinition> {
         // create route
         String id = definition.idOrCreate(camelContext.getCamelContextExtension().getContextPlugin(NodeIdFactory.class));
         String desc = definition.getDescriptionText();
+
         Route route = PluginHelper.getRouteFactory(camelContext).createRoute(camelContext, definition, id,
                 desc, endpoint, definition.getResource());
 
         // configure error handler
         route.setErrorHandlerFactory(definition.getErrorHandlerFactory());
 
+        // configure variable
+        String variable = definition.getInput().getVariable();
+        if (variable != null) {
+            // when using variable we need to turn on original message
+            route.setAllowUseOriginalMessage(true);
+        }
+
         // configure tracing
         if (definition.getTrace() != null) {
             Boolean isTrace = parseBoolean(definition.getTrace());
@@ -318,6 +328,11 @@ public class RouteReifier extends ProcessorReifier<RouteDefinition> {
             camelContext.setUseDataType(true);
         }
 
+        // wrap with variable
+        if (variable != null) {
+            internal.addAdvice(new VariableAdvice(variable));
+        }
+
         // and create the route that wraps all of this
         route.setProcessor(internal);
         route.getProperties().putAll(routeProperties);
@@ -405,4 +420,33 @@ public class RouteReifier extends ProcessorReifier<RouteDefinition> {
         return routeProperties;
     }
 
+    /**
+     * Advice for copying the message body into a variable
+     */
+    private static class VariableAdvice implements CamelInternalProcessorAdvice<Object> {
+
+        private final String name;
+
+        public VariableAdvice(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public Object before(Exchange exchange) throws Exception {
+            Object body = exchange.getMessage().getBody();
+            exchange.setVariable(name, body);
+            return null;
+        }
+
+        @Override
+        public void after(Exchange exchange, Object data) throws Exception {
+            // noop
+        }
+
+        @Override
+        public boolean hasState() {
+            return false;
+        }
+    }
+
 }
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/FromVariableTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/FromVariableTest.java
new file mode 100644
index 00000000000..3dfc5800ee8
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/FromVariableTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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 FromVariableTest extends ContextTestSupport {
+
+    @Test
+    public void testOriginalBody() throws Exception {
+        getMockEndpoint("mock:foo").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("World");
+
+        template.sendBody("direct:start", "World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                fromV("direct:start", "myKey")
+                        .transform().simple("Bye ${body}")
+                        .to("mock:foo")
+                        .setBody(simple("${variable:myKey}"))
+                        .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 619765f2b6a..61c0e7c0174 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
@@ -476,11 +476,12 @@ public class ModelParser extends BaseParser {
     }
     protected FromDefinition doParseFromDefinition() throws IOException, XmlPullParserException {
         return doParse(new FromDefinition(), (def, key, val) -> {
-            if ("uri".equals(key)) {
-                def.setUri(val);
-                return true;
+            switch (key) {
+                case "uri": def.setUri(val); break;
+                case "variable": def.setVariable(val); break;
+                default: return optionalIdentifiedDefinitionAttributeHandler().accept(def, key, val);
             }
-            return optionalIdentifiedDefinitionAttributeHandler().accept(def, key, val);
+            return true;
         }, optionalIdentifiedDefinitionElementHandler(), noValueHandler());
     }
     protected GlobalOptionDefinition doParseGlobalOptionDefinition() throws IOException, XmlPullParserException {
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 3c09b466f42..023547e5bd8 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -1365,6 +1365,7 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteOptionalIdentifiedDefinitionAttributes(def);
+        doWriteAttribute("variable", def.getVariable());
         doWriteAttribute("uri", def.getUri());
         endElement(name);
     }
diff --git a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index 312d4b3fba2..4daf3bd618d 100644
--- a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -1365,6 +1365,7 @@ public class ModelWriter extends BaseWriter {
             throws IOException {
         startElement(name);
         doWriteOptionalIdentifiedDefinitionAttributes(def);
+        doWriteAttribute("variable", def.getVariable());
         doWriteAttribute("uri", def.getUri());
         endElement(name);
     }
diff --git a/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/FromVariableTest.java b/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/FromVariableTest.java
new file mode 100644
index 00000000000..d10b4de92f1
--- /dev/null
+++ b/dsl/camel-endpointdsl/src/test/java/org/apache/camel/builder/endpoint/FromVariableTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.builder.endpoint;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+
+public class FromVariableTest extends BaseEndpointDslTest {
+
+    @Test
+    public void testOriginalBody() throws Exception {
+        getMockEndpoint("mock:foo").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("World");
+
+        template.sendBody("direct:start", "World");
+
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new EndpointRouteBuilder() {
+            public void configure() throws Exception {
+                fromV(direct("start").advanced().synchronous(false), "myKey")
+                        .transform().simple("Bye ${body}")
+                        .to("mock:foo")
+                        .setBody(simple("${variable:myKey}"))
+                        .to("mock:result");
+            }
+        };
+    }
+
+}