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 2023/12/30 15:15:05 UTC

(camel) 08/25: CAMEL-19749: Add variables as concept to Camel

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 fe894f1815e1702bfb21e65e173e8c34df660cfc
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Dec 28 18:56:25 2023 +0100

    CAMEL-19749: Add variables as concept to Camel
---
 .../apache/camel/catalog/components/thymeleaf.json |  14 +-
 .../camel-chunk/src/main/docs/chunk-component.adoc |   2 +
 .../src/main/docs/freemarker-component.adoc        |   2 +
 .../camel-jslt/src/main/docs/jslt-component.adoc   |   1 +
 .../apache/camel/component/jslt/JsltEndpoint.java  |   3 +
 .../src/main/docs/mustache-component.adoc          |   2 +
 .../camel-mvel/src/main/docs/mvel-component.adoc   |   1 +
 .../robotframework/RobotFrameworkCamelUtils.java   |   6 +
 .../src/main/docs/string-template-component.adoc   |  26 +++
 .../camel/component/thymeleaf/thymeleaf.json       |  14 +-
 .../component/thymeleaf/ThymeleafEndpoint.java     |  78 +--------
 .../src/main/docs/velocity-component.adoc          |   2 +
 .../org/apache/camel/util/ExchangeHelperTest.java  |  13 +-
 .../org/apache/camel/support/ExchangeHelper.java   |   1 +
 .../dsl/ThymeleafEndpointBuilderFactory.java       | 177 +++++++++++----------
 15 files changed, 164 insertions(+), 178 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/thymeleaf.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/thymeleaf.json
index ca54a5ade49..bcd8df3ac89 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/thymeleaf.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/thymeleaf.json
@@ -39,12 +39,12 @@
     "cacheTimeToLive": { "index": 3, "kind": "parameter", "displayName": "Cache Time To Live", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "java.lang.Long", "deprecated": false, "autowired": false, "secret": false, "description": "The cache Time To Live for templates, expressed in milliseconds." },
     "checkExistence": { "index": 4, "kind": "parameter", "displayName": "Check Existence", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether a template resources will be checked for existence before being returned." },
     "contentCache": { "index": 5, "kind": "parameter", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Sets whether to use resource content cache or not" },
-    "encoding": { "index": 6, "kind": "parameter", "displayName": "Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The character encoding to be used for reading template resources." },
-    "order": { "index": 7, "kind": "parameter", "displayName": "Order", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The order in which this template will be resolved as part of the resolver chain." },
-    "prefix": { "index": 8, "kind": "parameter", "displayName": "Prefix", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An optional prefix added to template names to convert them into resource names." },
-    "resolver": { "index": 9, "kind": "parameter", "displayName": "Resolver", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.thymeleaf.ThymeleafResolverType", "enum": [ "CLASS_LOADER", "DEFAULT", "FILE", "STRING", "URL", "WEB_APP" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "CLASS_LOADER", "description": "The type of resolver to be used by the template engine." },
-    "suffix": { "index": 10, "kind": "parameter", "displayName": "Suffix", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An optional suffix added to template names to convert them into resource names." },
-    "templateMode": { "index": 11, "kind": "parameter", "displayName": "Template Mode", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The template mode to be applied to templates." },
-    "lazyStartProducer": { "index": 12, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...]
+    "templateMode": { "index": 6, "kind": "parameter", "displayName": "Template Mode", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "HTML", "XML", "TEXT", "JAVASCRIPT", "CSS", "RAW" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "HTML", "description": "The template mode to be applied to templates." },
+    "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+    "encoding": { "index": 8, "kind": "parameter", "displayName": "Encoding", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The character encoding to be used for reading template resources." },
+    "order": { "index": 9, "kind": "parameter", "displayName": "Order", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The order in which this template will be resolved as part of the resolver chain." },
+    "prefix": { "index": 10, "kind": "parameter", "displayName": "Prefix", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An optional prefix added to template names to convert them into resource names." },
+    "resolver": { "index": 11, "kind": "parameter", "displayName": "Resolver", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.thymeleaf.ThymeleafResolverType", "enum": [ "CLASS_LOADER", "DEFAULT", "FILE", "STRING", "URL", "WEB_APP" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "CLASS_LOADER", "description": "The type of resolver to be used by the template engine." },
+    "suffix": { "index": 12, "kind": "parameter", "displayName": "Suffix", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An optional suffix added to template names to convert them into resource names." }
   }
 }
diff --git a/components/camel-chunk/src/main/docs/chunk-component.adoc b/components/camel-chunk/src/main/docs/chunk-component.adoc
index 811fdec8bec..56a056fdeec 100644
--- a/components/camel-chunk/src/main/docs/chunk-component.adoc
+++ b/components/camel-chunk/src/main/docs/chunk-component.adoc
@@ -66,6 +66,8 @@ a `Map`). The `Exchange` is transferred as:
 
 |`exchange.properties` |The `Exchange` properties.
 
+|`variables` |The variables
+
 |`headers` |The headers of the In message.
 
 |`camelContext` |The Camel Context.
diff --git a/components/camel-freemarker/src/main/docs/freemarker-component.adoc b/components/camel-freemarker/src/main/docs/freemarker-component.adoc
index f7e9a5fbf83..675b5ed4c78 100644
--- a/components/camel-freemarker/src/main/docs/freemarker-component.adoc
+++ b/components/camel-freemarker/src/main/docs/freemarker-component.adoc
@@ -83,6 +83,8 @@ a `Map`). The `Exchange` is transferred as:
 
 |`exchange.properties` |The `Exchange` properties.
 
+|`variables` |The variables
+
 |`headers` |The headers of the In message.
 
 |`camelContext` |The Camel Context.
diff --git a/components/camel-jslt/src/main/docs/jslt-component.adoc b/components/camel-jslt/src/main/docs/jslt-component.adoc
index c848ce745e4..78cd3e0b397 100644
--- a/components/camel-jslt/src/main/docs/jslt-component.adoc
+++ b/components/camel-jslt/src/main/docs/jslt-component.adoc
@@ -67,6 +67,7 @@ Camel can supply exchange information as variables when applying a JSLT expressi
 |===
 | name | value
 | headers | The headers of the In message as a json object
+| variables |The variables
 | exchange.properties | The *Exchange* properties as a json object. _exchange_ is the name of the variable and _properties_ is the path to the exchange properties. Available if _allowContextMapAll_ option is true.
 |===
 
diff --git a/components/camel-jslt/src/main/java/org/apache/camel/component/jslt/JsltEndpoint.java b/components/camel-jslt/src/main/java/org/apache/camel/component/jslt/JsltEndpoint.java
index 281710c173c..0c7e99f1e15 100644
--- a/components/camel-jslt/src/main/java/org/apache/camel/component/jslt/JsltEndpoint.java
+++ b/components/camel-jslt/src/main/java/org/apache/camel/component/jslt/JsltEndpoint.java
@@ -231,6 +231,9 @@ public class JsltEndpoint extends ResourceEndpoint {
         if (variableMap.containsKey("headers")) {
             serializedVariableMap.put("headers", serializeMapToJsonNode((Map<String, Object>) variableMap.get("headers")));
         }
+        if (variableMap.containsKey("variables")) {
+            serializedVariableMap.put("variables", serializeMapToJsonNode((Map<String, Object>) variableMap.get("variables")));
+        }
         if (variableMap.containsKey("exchange")) {
             Exchange ex = (Exchange) variableMap.get("exchange");
             ObjectNode exchangeNode = OBJECT_MAPPER.createObjectNode();
diff --git a/components/camel-mustache/src/main/docs/mustache-component.adoc b/components/camel-mustache/src/main/docs/mustache-component.adoc
index 211872f771b..6f4ec3d9c1a 100644
--- a/components/camel-mustache/src/main/docs/mustache-component.adoc
+++ b/components/camel-mustache/src/main/docs/mustache-component.adoc
@@ -69,6 +69,8 @@ Camel will provide exchange information in the Mustache context (just a
 
 |`exchange.properties` |The `Exchange` properties.
 
+|`variables` |The variables
+
 |`headers` |The headers of the In message.
 
 |`camelContext` |The Camel Context.
diff --git a/components/camel-mvel/src/main/docs/mvel-component.adoc b/components/camel-mvel/src/main/docs/mvel-component.adoc
index b9b078d5372..239b603b457 100644
--- a/components/camel-mvel/src/main/docs/mvel-component.adoc
+++ b/components/camel-mvel/src/main/docs/mvel-component.adoc
@@ -65,6 +65,7 @@ Camel will provide exchange information in the MVEL context (just a
 |key |value
 |exchange |The `Exchange` itself
 |exchange.properties |The `Exchange` properties
+|variables |The variables
 |headers |The headers of the message
 |camelContext |The `CamelContext`
 |request |The message
diff --git a/components/camel-robotframework/src/main/java/org/apache/camel/component/robotframework/RobotFrameworkCamelUtils.java b/components/camel-robotframework/src/main/java/org/apache/camel/component/robotframework/RobotFrameworkCamelUtils.java
index 350927f7ab7..8998d1446a3 100644
--- a/components/camel-robotframework/src/main/java/org/apache/camel/component/robotframework/RobotFrameworkCamelUtils.java
+++ b/components/camel-robotframework/src/main/java/org/apache/camel/component/robotframework/RobotFrameworkCamelUtils.java
@@ -31,6 +31,7 @@ public final class RobotFrameworkCamelUtils {
     private static final String ROBOT_CAMEL_EXCHANGE_NAME = "exchange";
     private static final String ROBOT_VAR_CAMEL_BODY = "body";
     private static final String ROBOT_VAR_CAMEL_HEADERS = "headers";
+    private static final String ROBOT_VAR_CAMEL_VARIABLES = "variables";
     private static final String ROBOT_VAR_CAMEL_PROPERTIES = "properties";
     private static final String ROBOT_VAR_FIELD_SEPERATOR = ":";
     private static final String ROBOT_VAR_NESTING_SEPERATOR = ".";
@@ -57,6 +58,11 @@ public final class RobotFrameworkCamelUtils {
                 createStringValueOfVariablesFromMap(variableKeyValuePairList,
                         ObjectHelper.cast(Map.class, variableEntry.getValue()), exchange, new StringBuilder(),
                         ROBOT_VAR_CAMEL_HEADERS, true);
+            } else if (ROBOT_VAR_CAMEL_VARIABLES.equals(variableEntry.getKey())) {
+                // here the param is the headers map
+                createStringValueOfVariablesFromMap(variableKeyValuePairList, exchange.getVariables(),
+                        ObjectHelper.cast(Exchange.class, variableEntry.getValue()),
+                        new StringBuilder(), ROBOT_VAR_CAMEL_VARIABLES, true);
             } else if (ROBOT_CAMEL_EXCHANGE_NAME.equals(variableEntry.getKey())) {
                 // here the param is camel exchange
                 createStringValueOfVariablesFromMap(variableKeyValuePairList, exchange.getProperties(),
diff --git a/components/camel-stringtemplate/src/main/docs/string-template-component.adoc b/components/camel-stringtemplate/src/main/docs/string-template-component.adoc
index 68afe26140e..ad2dd46388d 100644
--- a/components/camel-stringtemplate/src/main/docs/string-template-component.adoc
+++ b/components/camel-stringtemplate/src/main/docs/string-template-component.adoc
@@ -65,6 +65,32 @@ Camel will store a reference to the resource in the message header with
 key, `org.apache.camel.stringtemplate.resource`. The Resource is an
 `org.springframework.core.io.Resource` object.
 
+== String Template Context
+
+Camel will provide exchange information in the String Template context (just a
+`Map`). The `Exchange` is transferred as:
+
+[width="100%",cols="10%,90%",options="header",]
+|=======================================================================
+|key |value
+
+|`exchange` |The `Exchange` itself.
+
+|`exchange.properties` |The `Exchange` properties.
+
+|`variables` |The variables
+
+|`headers` |The headers of the In message.
+
+|`camelContext` |The Camel Context.
+
+|`request` |The In message.
+
+|`body` |The In message body.
+
+|`response` |The Out message (only for InOut message exchange pattern).
+|=======================================================================
+
 == Hot reloading
 
 The string template resource is by default hot-reloadable for both file
diff --git a/components/camel-thymeleaf/src/generated/resources/org/apache/camel/component/thymeleaf/thymeleaf.json b/components/camel-thymeleaf/src/generated/resources/org/apache/camel/component/thymeleaf/thymeleaf.json
index ca54a5ade49..bcd8df3ac89 100644
--- a/components/camel-thymeleaf/src/generated/resources/org/apache/camel/component/thymeleaf/thymeleaf.json
+++ b/components/camel-thymeleaf/src/generated/resources/org/apache/camel/component/thymeleaf/thymeleaf.json
@@ -39,12 +39,12 @@
     "cacheTimeToLive": { "index": 3, "kind": "parameter", "displayName": "Cache Time To Live", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "java.lang.Long", "deprecated": false, "autowired": false, "secret": false, "description": "The cache Time To Live for templates, expressed in milliseconds." },
     "checkExistence": { "index": 4, "kind": "parameter", "displayName": "Check Existence", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "description": "Whether a template resources will be checked for existence before being returned." },
     "contentCache": { "index": 5, "kind": "parameter", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Sets whether to use resource content cache or not" },
-    "encoding": { "index": 6, "kind": "parameter", "displayName": "Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The character encoding to be used for reading template resources." },
-    "order": { "index": 7, "kind": "parameter", "displayName": "Order", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The order in which this template will be resolved as part of the resolver chain." },
-    "prefix": { "index": 8, "kind": "parameter", "displayName": "Prefix", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An optional prefix added to template names to convert them into resource names." },
-    "resolver": { "index": 9, "kind": "parameter", "displayName": "Resolver", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.thymeleaf.ThymeleafResolverType", "enum": [ "CLASS_LOADER", "DEFAULT", "FILE", "STRING", "URL", "WEB_APP" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "CLASS_LOADER", "description": "The type of resolver to be used by the template engine." },
-    "suffix": { "index": 10, "kind": "parameter", "displayName": "Suffix", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An optional suffix added to template names to convert them into resource names." },
-    "templateMode": { "index": 11, "kind": "parameter", "displayName": "Template Mode", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The template mode to be applied to templates." },
-    "lazyStartProducer": { "index": 12, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...]
+    "templateMode": { "index": 6, "kind": "parameter", "displayName": "Template Mode", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "enum": [ "HTML", "XML", "TEXT", "JAVASCRIPT", "CSS", "RAW" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "HTML", "description": "The template mode to be applied to templates." },
+    "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+    "encoding": { "index": 8, "kind": "parameter", "displayName": "Encoding", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The character encoding to be used for reading template resources." },
+    "order": { "index": 9, "kind": "parameter", "displayName": "Order", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "description": "The order in which this template will be resolved as part of the resolver chain." },
+    "prefix": { "index": 10, "kind": "parameter", "displayName": "Prefix", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An optional prefix added to template names to convert them into resource names." },
+    "resolver": { "index": 11, "kind": "parameter", "displayName": "Resolver", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.thymeleaf.ThymeleafResolverType", "enum": [ "CLASS_LOADER", "DEFAULT", "FILE", "STRING", "URL", "WEB_APP" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "CLASS_LOADER", "description": "The type of resolver to be used by the template engine." },
+    "suffix": { "index": 12, "kind": "parameter", "displayName": "Suffix", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An optional suffix added to template names to convert them into resource names." }
   }
 }
diff --git a/components/camel-thymeleaf/src/main/java/org/apache/camel/component/thymeleaf/ThymeleafEndpoint.java b/components/camel-thymeleaf/src/main/java/org/apache/camel/component/thymeleaf/ThymeleafEndpoint.java
index 865ff437f78..4dd4d1dae8f 100644
--- a/components/camel-thymeleaf/src/main/java/org/apache/camel/component/thymeleaf/ThymeleafEndpoint.java
+++ b/components/camel-thymeleaf/src/main/java/org/apache/camel/component/thymeleaf/ThymeleafEndpoint.java
@@ -47,62 +47,47 @@ import org.thymeleaf.web.servlet.JakartaServletWebApplication;
 public class ThymeleafEndpoint extends ResourceEndpoint {
 
     private TemplateEngine templateEngine;
-
     private String template;
-
     private JakartaServletWebApplication jakartaServletWebApplication;
 
-    @UriParam(defaultValue = "CLASS_LOADER", description = "The type of resolver to be used by the template engine.",
+    @UriParam(label = "advanced", defaultValue = "CLASS_LOADER", description = "The type of resolver to be used by the template engine.",
               javaType = "org.apache.camel.component.thymeleaf.ThymeleafResolverType")
     private ThymeleafResolverType resolver = ThymeleafResolverType.CLASS_LOADER;
-
-    @UriParam(description = "The template mode to be applied to templates.")
+    @UriParam(description = "The template mode to be applied to templates.", defaultValue = "HTML", enums = "HTML,XML,TEXT,JAVASCRIPT,CSS,RAW")
     private String templateMode;
-
-    @UriParam(description = "An optional prefix added to template names to convert them into resource names.")
+    @UriParam(label = "advanced", description = "An optional prefix added to template names to convert them into resource names.")
     private String prefix;
-
-    @UriParam(description = "An optional suffix added to template names to convert them into resource names.")
+    @UriParam(label = "advanced", description = "An optional suffix added to template names to convert them into resource names.")
     private String suffix;
-
-    @UriParam(description = "The character encoding to be used for reading template resources.")
+    @UriParam(label = "advanced", description = "The character encoding to be used for reading template resources.")
     private String encoding;
-
-    @UriParam(description = "The order in which this template will be resolved as part of the resolver chain.")
+    @UriParam(label = "advanced", description = "The order in which this template will be resolved as part of the resolver chain.")
     private Integer order;
-
     @UriParam(description = "Whether a template resources will be checked for existence before being returned.")
     private Boolean checkExistence;
-
     @UriParam(description = "The cache Time To Live for templates, expressed in milliseconds.")
     private Long cacheTimeToLive;
-
     @UriParam(description = "Whether templates have to be considered cacheable or not.")
     private Boolean cacheable;
 
     public ThymeleafEndpoint() {
-
     }
 
     public ThymeleafEndpoint(String endpointURI, Component component, String resourceURI) {
-
         super(endpointURI, component, resourceURI);
     }
 
     @Override
     public ExchangePattern getExchangePattern() {
-
         return ExchangePattern.InOut;
     }
 
     @Override
     protected String createEndpointUri() {
-
         return "thymeleaf:" + getResourceUri();
     }
 
     public String getTemplateMode() {
-
         return templateMode;
     }
 
@@ -122,12 +107,10 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
      * @param templateMode the template mode.
      */
     public void setTemplateMode(String templateMode) {
-
         this.templateMode = templateMode;
     }
 
     public ThymeleafResolverType getResolver() {
-
         return resolver;
     }
 
@@ -138,12 +121,10 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
      * </p>
      */
     public void setResolver(ThymeleafResolverType resolver) {
-
         this.resolver = resolver;
     }
 
     public String getPrefix() {
-
         return prefix;
     }
 
@@ -156,12 +137,10 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
      * @param prefix the prefix to be set.
      */
     public void setPrefix(String prefix) {
-
         this.prefix = prefix;
     }
 
     public String getSuffix() {
-
         return suffix;
     }
 
@@ -179,104 +158,79 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
      * @param suffix the suffix to be set.
      */
     public void setSuffix(String suffix) {
-
         this.suffix = suffix;
     }
 
     public String getEncoding() {
-
         return encoding;
     }
 
     /**
-     * <p>
      * Sets a new character encoding for reading template resources.
-     * </p>
      *
      * @param encoding the character encoding to be used.
      */
     public void setEncoding(String encoding) {
-
         this.encoding = encoding;
     }
 
     public Integer getOrder() {
-
         return order;
     }
 
     /**
-     * <p>
      * Sets a new order for the template engine in the chain. Order should start with 1.
-     * </p>
      *
      * @param order the new order.
      */
     public void setOrder(Integer order) {
-
         this.order = order;
     }
 
     public Boolean getCheckExistence() {
-
         return checkExistence;
     }
 
     /**
-     * <p>
      * Sets whether template resources will be checked for existence before being returned or not.
-     * </p>
-     * <p>
      * Default value is {@code FALSE}.
-     * </p>
      *
      * @param checkExistence {@code true} if resource existence should be checked, {@code false} if not
      */
     public void setCheckExistence(Boolean checkExistence) {
-
         this.checkExistence = checkExistence;
     }
 
     public Long getCacheTimeToLive() {
-
         return cacheTimeToLive;
     }
 
     /**
-     * <p>
      * Sets a new value for the cache TTL for resolved templates.
-     * </p>
-     * <p>
+     *
      * If a template is resolved as <i>cacheable</i> but cache TTL is null, this means the template will live in cache
      * until evicted by LRU (Least Recently Used) algorithm for being the oldest entry in cache.
-     * </p>
      *
      * @param cacheTimeToLive the new cache TTL in milliseconds, or null for using natural LRU eviction.
      */
     public void setCacheTimeToLive(Long cacheTimeToLive) {
-
         this.cacheTimeToLive = cacheTimeToLive;
     }
 
     public Boolean getCacheable() {
-
         return cacheable;
     }
 
     /**
-     * <p>
      * Sets a new value for the <i>cacheable</i> flag.
-     * </p>
      *
      * @param cacheable whether resolved patterns should be considered cacheable or not.
      */
     public void setCacheable(Boolean cacheable) {
-
         this.cacheable = cacheable;
     }
 
     protected synchronized TemplateEngine getTemplateEngine() {
-
         if (templateEngine == null) {
             ITemplateResolver templateResolver;
 
@@ -284,27 +238,21 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
                 case CLASS_LOADER -> {
                     templateResolver = classLoaderTemplateResolver();
                 }
-
                 case DEFAULT -> {
                     templateResolver = defaultTemplateResolver();
                 }
-
                 case FILE -> {
                     templateResolver = fileTemplateResolver();
                 }
-
                 case STRING -> {
                     templateResolver = stringTemplateResolver();
                 }
-
                 case URL -> {
                     templateResolver = urlTemplateResolver();
                 }
-
                 case WEB_APP -> {
                     templateResolver = webApplicationTemplateResolver();
                 }
-
                 default -> {
                     throw new RuntimeCamelException("cannot determine TemplateResolver for type " + resolver);
                 }
@@ -321,30 +269,25 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
      * To use the {@link TemplateEngine} otherwise a new engine is created
      */
     public void setTemplateEngine(TemplateEngine templateEngine) {
-
         this.templateEngine = templateEngine;
     }
 
     public void setJakartaServletWebApplication(JakartaServletWebApplication jakartaServletWebApplication) {
-
         this.jakartaServletWebApplication = jakartaServletWebApplication;
     }
 
     public void setTemplate(String template) {
-
         this.template = template;
     }
 
     @Override
     public void clearContentCache() {
-
         if (templateEngine != null) {
             templateEngine.clearTemplateCache();
         }
     }
 
     public ThymeleafEndpoint findOrCreateEndpoint(String uri, String newResourceUri) {
-
         String newUri = uri.replace(getResourceUri(), newResourceUri);
         log.debug("Getting endpoint with URI: {}", newUri);
         return getCamelContext().getEndpoint(newUri, ThymeleafEndpoint.class);
@@ -352,7 +295,6 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
 
     @Override
     protected void onExchange(Exchange exchange) throws Exception {
-
         String path = getResourceUri();
         ObjectHelper.notNull(path, "resourceUri");
 
@@ -401,7 +343,6 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
     }
 
     private ITemplateResolver classLoaderTemplateResolver() {
-
         ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
         if (cacheable != null) {
             resolver.setCacheable(cacheable);
@@ -435,7 +376,6 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
     }
 
     private ITemplateResolver defaultTemplateResolver() {
-
         DefaultTemplateResolver resolver = new DefaultTemplateResolver();
         if (checkExistence != null) {
             resolver.setCheckExistence(checkExistence);
@@ -456,7 +396,6 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
     }
 
     private ITemplateResolver fileTemplateResolver() {
-
         FileTemplateResolver resolver = new FileTemplateResolver();
         if (cacheable != null) {
             resolver.setCacheable(cacheable);
@@ -490,7 +429,6 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
     }
 
     private ITemplateResolver stringTemplateResolver() {
-
         StringTemplateResolver resolver = new StringTemplateResolver();
         if (cacheable != null) {
             resolver.setCacheable(cacheable);
@@ -515,7 +453,6 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
     }
 
     private ITemplateResolver urlTemplateResolver() {
-
         UrlTemplateResolver resolver = new UrlTemplateResolver();
         if (cacheable != null) {
             resolver.setCacheable(cacheable);
@@ -546,7 +483,6 @@ public class ThymeleafEndpoint extends ResourceEndpoint {
     }
 
     private ITemplateResolver webApplicationTemplateResolver() {
-
         WebApplicationTemplateResolver resolver = new WebApplicationTemplateResolver(jakartaServletWebApplication);
 
         if (cacheable != null) {
diff --git a/components/camel-velocity/src/main/docs/velocity-component.adoc b/components/camel-velocity/src/main/docs/velocity-component.adoc
index 6a4aa0c9931..aed0345e900 100644
--- a/components/camel-velocity/src/main/docs/velocity-component.adoc
+++ b/components/camel-velocity/src/main/docs/velocity-component.adoc
@@ -88,6 +88,8 @@ Camel will provide exchange information in the Velocity context (just a
 
 |`exchange.properties` |The `Exchange` properties.
 
+|`variables` |The variables
+
 |`headers` |The headers of the In message.
 
 |`camelContext` |The Camel Context instance.
diff --git a/core/camel-core/src/test/java/org/apache/camel/util/ExchangeHelperTest.java b/core/camel-core/src/test/java/org/apache/camel/util/ExchangeHelperTest.java
index cf4331c12a2..ab3fd5f157d 100644
--- a/core/camel-core/src/test/java/org/apache/camel/util/ExchangeHelperTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/util/ExchangeHelperTest.java
@@ -116,7 +116,7 @@ public class ExchangeHelperTest extends ContextTestSupport {
         Map<String, Object> map = new HashMap<>();
         ExchangeHelper.populateVariableMap(exchange, map, false);
 
-        assertEquals(2, map.size());
+        assertEquals(3, map.size());
         assertNull(map.get("exchange"));
         assertNull(map.get("in"));
         assertNull(map.get("request"));
@@ -124,6 +124,7 @@ public class ExchangeHelperTest extends ContextTestSupport {
         assertNull(map.get("response"));
         assertSame(exchange.getIn().getHeaders(), map.get("headers"));
         assertSame(exchange.getIn().getBody(), map.get("body"));
+        assertSame(exchange.getVariable("cheese"), map.get("cheese"));
         assertNull(map.get("camelContext"));
     }
 
@@ -136,7 +137,7 @@ public class ExchangeHelperTest extends ContextTestSupport {
         Map<String, Object> map = new HashMap<>();
         ExchangeHelper.populateVariableMap(exchange, map, true);
 
-        assertEquals(8, map.size());
+        assertEquals(9, map.size());
         assertSame(exchange, map.get("exchange"));
         assertSame(exchange.getIn(), map.get("in"));
         assertSame(exchange.getIn(), map.get("request"));
@@ -152,10 +153,11 @@ public class ExchangeHelperTest extends ContextTestSupport {
         exchange.setPattern(ExchangePattern.InOut);
         exchange.getMessage().setBody("bar");
         exchange.getMessage().setHeader("quote", "Camel rocks");
+        exchange.setVariable("cheese", "gauda");
 
         Map<?, ?> map = ExchangeHelper.createVariableMap(exchange, true);
 
-        assertEquals(8, map.size());
+        assertEquals(9, map.size());
         assertSame(exchange, map.get("exchange"));
         assertSame(exchange.getIn(), map.get("in"));
         assertSame(exchange.getIn(), map.get("request"));
@@ -175,8 +177,8 @@ public class ExchangeHelperTest extends ContextTestSupport {
 
         Map<?, ?> map = ExchangeHelper.createVariableMap(exchange, true);
 
-        // there should still be 8 in the map
-        assertEquals(8, map.size());
+        // there should still be 9 in the map
+        assertEquals(9, map.size());
         assertSame(exchange, map.get("exchange"));
         assertSame(exchange.getIn(), map.get("in"));
         assertSame(exchange.getIn(), map.get("request"));
@@ -184,6 +186,7 @@ public class ExchangeHelperTest extends ContextTestSupport {
         assertSame(exchange.getIn(), map.get("response"));
         assertSame(exchange.getIn().getHeaders(), map.get("headers"));
         assertSame(exchange.getIn().getBody(), map.get("body"));
+        assertSame(exchange.getVariable("cheese"), map.get("cheese"));
         assertSame(exchange.getContext(), map.get("camelContext"));
 
         // but the Exchange does still not have an OUT message to avoid
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 ca806a88a50..9f45cd44431 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
@@ -474,6 +474,7 @@ public final class ExchangeHelper {
         Message in = exchange.getIn();
         map.put("headers", in.getHeaders());
         map.put("body", in.getBody());
+        map.put("variables", exchange.getVariables());
         if (allowContextMapAll) {
             map.put("in", in);
             map.put("request", in);
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ThymeleafEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ThymeleafEndpointBuilderFactory.java
index 30266eb8b4e..8875df45c16 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ThymeleafEndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ThymeleafEndpointBuilderFactory.java
@@ -203,16 +203,91 @@ public interface ThymeleafEndpointBuilderFactory {
             return this;
         }
         /**
-         * The character encoding to be used for reading template resources.
+         * The template mode to be applied to templates.
          * 
          * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
          * 
+         * Default: HTML
          * Group: producer
          * 
+         * @param templateMode the value to set
+         * @return the dsl builder
+         */
+        default ThymeleafEndpointBuilder templateMode(String templateMode) {
+            doSetProperty("templateMode", templateMode);
+            return this;
+        }
+    }
+
+    /**
+     * Advanced builder for endpoint for the Thymeleaf component.
+     */
+    public interface AdvancedThymeleafEndpointBuilder
+            extends
+                EndpointProducerBuilder {
+        default ThymeleafEndpointBuilder basic() {
+            return (ThymeleafEndpointBuilder) this;
+        }
+        /**
+         * Whether the producer should be started lazy (on the first message).
+         * By starting lazy you can use this to allow CamelContext and routes to
+         * startup in situations where a producer may otherwise fail during
+         * starting and cause the route to fail being started. By deferring this
+         * startup to be lazy then the startup failure can be handled during
+         * routing messages via Camel's routing error handlers. Beware that when
+         * the first message is processed then creating and starting the
+         * producer may take a little time and prolong the total processing time
+         * of the processing.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: false
+         * Group: producer (advanced)
+         * 
+         * @param lazyStartProducer the value to set
+         * @return the dsl builder
+         */
+        default AdvancedThymeleafEndpointBuilder lazyStartProducer(
+                boolean lazyStartProducer) {
+            doSetProperty("lazyStartProducer", lazyStartProducer);
+            return this;
+        }
+        /**
+         * Whether the producer should be started lazy (on the first message).
+         * By starting lazy you can use this to allow CamelContext and routes to
+         * startup in situations where a producer may otherwise fail during
+         * starting and cause the route to fail being started. By deferring this
+         * startup to be lazy then the startup failure can be handled during
+         * routing messages via Camel's routing error handlers. Beware that when
+         * the first message is processed then creating and starting the
+         * producer may take a little time and prolong the total processing time
+         * of the processing.
+         * 
+         * The option will be converted to a &lt;code&gt;boolean&lt;/code&gt;
+         * type.
+         * 
+         * Default: false
+         * Group: producer (advanced)
+         * 
+         * @param lazyStartProducer the value to set
+         * @return the dsl builder
+         */
+        default AdvancedThymeleafEndpointBuilder lazyStartProducer(
+                String lazyStartProducer) {
+            doSetProperty("lazyStartProducer", lazyStartProducer);
+            return this;
+        }
+        /**
+         * The character encoding to be used for reading template resources.
+         * 
+         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
+         * 
+         * Group: advanced
+         * 
          * @param encoding the value to set
          * @return the dsl builder
          */
-        default ThymeleafEndpointBuilder encoding(String encoding) {
+        default AdvancedThymeleafEndpointBuilder encoding(String encoding) {
             doSetProperty("encoding", encoding);
             return this;
         }
@@ -222,12 +297,12 @@ public interface ThymeleafEndpointBuilderFactory {
          * 
          * The option is a: &lt;code&gt;java.lang.Integer&lt;/code&gt; type.
          * 
-         * Group: producer
+         * Group: advanced
          * 
          * @param order the value to set
          * @return the dsl builder
          */
-        default ThymeleafEndpointBuilder order(Integer order) {
+        default AdvancedThymeleafEndpointBuilder order(Integer order) {
             doSetProperty("order", order);
             return this;
         }
@@ -238,12 +313,12 @@ public interface ThymeleafEndpointBuilderFactory {
          * The option will be converted to a
          * &lt;code&gt;java.lang.Integer&lt;/code&gt; type.
          * 
-         * Group: producer
+         * Group: advanced
          * 
          * @param order the value to set
          * @return the dsl builder
          */
-        default ThymeleafEndpointBuilder order(String order) {
+        default AdvancedThymeleafEndpointBuilder order(String order) {
             doSetProperty("order", order);
             return this;
         }
@@ -253,12 +328,12 @@ public interface ThymeleafEndpointBuilderFactory {
          * 
          * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
          * 
-         * Group: producer
+         * Group: advanced
          * 
          * @param prefix the value to set
          * @return the dsl builder
          */
-        default ThymeleafEndpointBuilder prefix(String prefix) {
+        default AdvancedThymeleafEndpointBuilder prefix(String prefix) {
             doSetProperty("prefix", prefix);
             return this;
         }
@@ -269,12 +344,12 @@ public interface ThymeleafEndpointBuilderFactory {
          * &lt;code&gt;org.apache.camel.component.thymeleaf.ThymeleafResolverType&lt;/code&gt; type.
          * 
          * Default: CLASS_LOADER
-         * Group: producer
+         * Group: advanced
          * 
          * @param resolver the value to set
          * @return the dsl builder
          */
-        default ThymeleafEndpointBuilder resolver(
+        default AdvancedThymeleafEndpointBuilder resolver(
                 org.apache.camel.component.thymeleaf.ThymeleafResolverType resolver) {
             doSetProperty("resolver", resolver);
             return this;
@@ -286,12 +361,12 @@ public interface ThymeleafEndpointBuilderFactory {
          * &lt;code&gt;org.apache.camel.component.thymeleaf.ThymeleafResolverType&lt;/code&gt; type.
          * 
          * Default: CLASS_LOADER
-         * Group: producer
+         * Group: advanced
          * 
          * @param resolver the value to set
          * @return the dsl builder
          */
-        default ThymeleafEndpointBuilder resolver(String resolver) {
+        default AdvancedThymeleafEndpointBuilder resolver(String resolver) {
             doSetProperty("resolver", resolver);
             return this;
         }
@@ -301,89 +376,15 @@ public interface ThymeleafEndpointBuilderFactory {
          * 
          * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
          * 
-         * Group: producer
+         * Group: advanced
          * 
          * @param suffix the value to set
          * @return the dsl builder
          */
-        default ThymeleafEndpointBuilder suffix(String suffix) {
+        default AdvancedThymeleafEndpointBuilder suffix(String suffix) {
             doSetProperty("suffix", suffix);
             return this;
         }
-        /**
-         * The template mode to be applied to templates.
-         * 
-         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
-         * 
-         * Group: producer
-         * 
-         * @param templateMode the value to set
-         * @return the dsl builder
-         */
-        default ThymeleafEndpointBuilder templateMode(String templateMode) {
-            doSetProperty("templateMode", templateMode);
-            return this;
-        }
-    }
-
-    /**
-     * Advanced builder for endpoint for the Thymeleaf component.
-     */
-    public interface AdvancedThymeleafEndpointBuilder
-            extends
-                EndpointProducerBuilder {
-        default ThymeleafEndpointBuilder basic() {
-            return (ThymeleafEndpointBuilder) this;
-        }
-        /**
-         * Whether the producer should be started lazy (on the first message).
-         * By starting lazy you can use this to allow CamelContext and routes to
-         * startup in situations where a producer may otherwise fail during
-         * starting and cause the route to fail being started. By deferring this
-         * startup to be lazy then the startup failure can be handled during
-         * routing messages via Camel's routing error handlers. Beware that when
-         * the first message is processed then creating and starting the
-         * producer may take a little time and prolong the total processing time
-         * of the processing.
-         * 
-         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
-         * 
-         * Default: false
-         * Group: producer (advanced)
-         * 
-         * @param lazyStartProducer the value to set
-         * @return the dsl builder
-         */
-        default AdvancedThymeleafEndpointBuilder lazyStartProducer(
-                boolean lazyStartProducer) {
-            doSetProperty("lazyStartProducer", lazyStartProducer);
-            return this;
-        }
-        /**
-         * Whether the producer should be started lazy (on the first message).
-         * By starting lazy you can use this to allow CamelContext and routes to
-         * startup in situations where a producer may otherwise fail during
-         * starting and cause the route to fail being started. By deferring this
-         * startup to be lazy then the startup failure can be handled during
-         * routing messages via Camel's routing error handlers. Beware that when
-         * the first message is processed then creating and starting the
-         * producer may take a little time and prolong the total processing time
-         * of the processing.
-         * 
-         * The option will be converted to a &lt;code&gt;boolean&lt;/code&gt;
-         * type.
-         * 
-         * Default: false
-         * Group: producer (advanced)
-         * 
-         * @param lazyStartProducer the value to set
-         * @return the dsl builder
-         */
-        default AdvancedThymeleafEndpointBuilder lazyStartProducer(
-                String lazyStartProducer) {
-            doSetProperty("lazyStartProducer", lazyStartProducer);
-            return this;
-        }
     }
 
     public interface ThymeleafBuilders {