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 <claus.ib...@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)
+    }
+}

Reply via email to