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

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


The following commit(s) were added to refs/heads/main by this push:
     new a97c88dccbd CAMEL-22136: camel-rest - Allow to use variables in {xxx} 
placeholder syntax for the producer
a97c88dccbd is described below

commit a97c88dccbd53cb9d9e4b88eabff04b568a2b5ce
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Jun 4 12:33:42 2025 +0200

    CAMEL-22136: camel-rest - Allow to use variables in {xxx} placeholder 
syntax for the producer
---
 .../rest/producer/HttpRestProducerGetTest.java     |  8 ++-
 .../camel-rest/src/main/docs/rest-component.adoc   |  9 +++-
 .../apache/camel/component/rest/RestProducer.java  | 57 ++++++++++++----------
 3 files changed, 46 insertions(+), 28 deletions(-)

diff --git 
a/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/rest/producer/HttpRestProducerGetTest.java
 
b/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/rest/producer/HttpRestProducerGetTest.java
index 3802aecf98b..210b2bce2da 100644
--- 
a/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/rest/producer/HttpRestProducerGetTest.java
+++ 
b/components/camel-jetty/src/test/java/org/apache/camel/component/jetty/rest/producer/HttpRestProducerGetTest.java
@@ -27,11 +27,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 public class HttpRestProducerGetTest extends BaseJettyTest {
 
     @Test
-    public void testHttpProducerGet() {
+    public void testHttpProducerGetWithHeader() {
         String out = fluentTemplate.withHeader("id", 
"123").to("direct:start").request(String.class);
         assertEquals("123;Donald Duck", out);
     }
 
+    @Test
+    public void testHttpProducerGetWithVariable() {
+        String out = fluentTemplate.withVariable("id", 
"456").to("direct:start").request(String.class);
+        assertEquals("456;Donald Duck", out);
+    }
+
     @Override
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
diff --git a/components/camel-rest/src/main/docs/rest-component.adoc 
b/components/camel-rest/src/main/docs/rest-component.adoc
index 29be9d030a4..053860fe189 100644
--- a/components/camel-rest/src/main/docs/rest-component.adoc
+++ b/components/camel-rest/src/main/docs/rest-component.adoc
@@ -119,7 +119,7 @@ from("direct:start")
   .to("rest:get:hello/{me}");
 ----
 
-And then the dynamic value `\{me}` is mapped to a Camel message with the same 
name.
+And then the dynamic value `\{me}` is mapped to a header or variable with the 
same name.
 So to call this REST service, you can send an empty message body and a header 
as shown:
 
 [source,java]
@@ -127,6 +127,13 @@ So to call this REST service, you can send an empty 
message body and a header as
 template.sendBodyAndHeader("direct:start", null, "me", "Donald Duck");
 ----
 
+Instead of a header you can also use exchange variable such as:
+
+[source,java]
+----
+String response = template.withVariable("me", "Donald 
Duck").to("direct:start").request(String.class);
+----
+
 The Rest producer needs to know the hostname and port of the REST service, 
which you can configure
 using the host option as shown:
 
diff --git 
a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
 
b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
index fbd78cdfb93..c588433ca5f 100644
--- 
a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
+++ 
b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
@@ -16,9 +16,9 @@
  */
 package org.apache.camel.component.rest;
 
-import java.io.UnsupportedEncodingException;
 import java.net.URISyntaxException;
 import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Locale;
@@ -30,7 +30,6 @@ import org.apache.camel.AsyncProcessor;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Exchange;
-import org.apache.camel.Message;
 import org.apache.camel.Producer;
 import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.PropertyConfigurer;
@@ -146,7 +145,6 @@ public class RestProducer extends DefaultAsyncProducer {
         String resolvedUriTemplate
                 = getEndpoint().getUriTemplate() != null ? 
getEndpoint().getUriTemplate() : getEndpoint().getPath();
 
-        Message inMessage = exchange.getIn();
         if (prepareUriTemplate) {
             if (resolvedUriTemplate.contains("{")) {
                 // resolve template and replace {key} with the values form the 
exchange
@@ -154,7 +152,7 @@ public class RestProducer extends DefaultAsyncProducer {
                 String[] arr = resolvedUriTemplate.split("\\/");
                 StringJoiner uriTemplateBuilder = new StringJoiner("/");
                 for (String a : arr) {
-                    String resolvedUriParam = resolveHeaderPlaceholders(a, 
inMessage);
+                    String resolvedUriParam = resolvePlaceholders(a, exchange);
 
                     // Backward compatibility: if one of the path params is 
fully resolved,
                     // then it is assumed that whole uri is resolved.
@@ -172,11 +170,11 @@ public class RestProducer extends DefaultAsyncProducer {
         }
 
         // resolve uri parameters
-        String query = 
createQueryParameters(getEndpoint().getQueryParameters(), inMessage);
+        String query = 
createQueryParameters(getEndpoint().getQueryParameters(), exchange);
 
         if (query != null) {
             // the query parameters for the rest call to be used
-            inMessage.setHeader(RestConstants.REST_HTTP_QUERY, query);
+            exchange.getMessage().setHeader(RestConstants.REST_HTTP_QUERY, 
query);
         }
 
         if (hasPath) {
@@ -193,14 +191,14 @@ public class RestProducer extends DefaultAsyncProducer {
                 overrideUri += "/" + resolvedUriTemplate;
             }
             // the http uri for the rest call to be used
-            inMessage.setHeader(RestConstants.REST_HTTP_URI, overrideUri);
+            exchange.getMessage().setHeader(RestConstants.REST_HTTP_URI, 
overrideUri);
 
             // when chaining RestConsumer with RestProducer, the
             // HTTP_PATH header will be present, we remove it here
             // as the REST_HTTP_URI contains the full URI for the
             // request and every other HTTP producer will concatenate
             // REST_HTTP_URI with HTTP_PATH resulting in incorrect URIs
-            inMessage.removeHeader(Exchange.HTTP_PATH);
+            exchange.getMessage().removeHeader(Exchange.HTTP_PATH);
         }
 
         // method
@@ -208,28 +206,28 @@ public class RestProducer extends DefaultAsyncProducer {
         if (method != null) {
             // the method should be in upper case
             String upper = method.toUpperCase(Locale.US);
-            inMessage.setHeader(RestConstants.HTTP_METHOD, upper);
+            exchange.getMessage().setHeader(RestConstants.HTTP_METHOD, upper);
         }
 
         final String produces = getEndpoint().getProduces();
-        if (isEmpty(inMessage.getHeader(RestConstants.CONTENT_TYPE)) && 
isNotEmpty(produces)) {
-            inMessage.setHeader(RestConstants.CONTENT_TYPE, produces);
+        if 
(isEmpty(exchange.getMessage().getHeader(RestConstants.CONTENT_TYPE)) && 
isNotEmpty(produces)) {
+            exchange.getMessage().setHeader(RestConstants.CONTENT_TYPE, 
produces);
         }
 
         final String consumes = getEndpoint().getConsumes();
-        if (isEmpty(inMessage.getHeader(RestConstants.ACCEPT)) && 
isNotEmpty(consumes)) {
-            inMessage.setHeader(RestConstants.ACCEPT, consumes);
+        if (isEmpty(exchange.getMessage().getHeader(RestConstants.ACCEPT)) && 
isNotEmpty(consumes)) {
+            exchange.getMessage().setHeader(RestConstants.ACCEPT, consumes);
         }
     }
 
     /**
-     * Replaces placeholders "{}" with message header values.
+     * Replaces placeholders "{}" with message header or exchange variable 
values.
      *
-     * @param  str string with placeholders
-     * @param  msg message with headers
-     * @return     filled string
+     * @param  str      string with placeholders
+     * @param  exchange the exchange
+     * @return          filled string
      */
-    private String resolveHeaderPlaceholders(String str, Message msg) {
+    private String resolvePlaceholders(String str, Exchange exchange) {
         int startIndex = -1;
         String res = str;
         while ((startIndex = res.indexOf('{', startIndex + 1)) >= 0) {
@@ -238,12 +236,15 @@ public class RestProducer extends DefaultAsyncProducer {
                 continue;
             }
             String key = res.substring(startIndex + 1, endIndex);
-            String headerValue = msg.getHeader(key, String.class);
-            if (headerValue != null) {
-                res = res.substring(0, startIndex) + headerValue + 
res.substring(endIndex + 1);
+            // try header first and then fallback to variable
+            String value = exchange.getMessage().getHeader(key, String.class);
+            if (value == null) {
+                value = exchange.getVariable(key, String.class);
+            }
+            if (value != null) {
+                res = res.substring(0, startIndex) + value + 
res.substring(endIndex + 1);
             }
         }
-
         return res;
     }
 
@@ -399,8 +400,8 @@ public class RestProducer extends DefaultAsyncProducer {
                 || key.startsWith("xml.out.");
     }
 
-    static String createQueryParameters(String query, Message inMessage)
-            throws URISyntaxException, UnsupportedEncodingException {
+    static String createQueryParameters(String query, Exchange exchange)
+            throws URISyntaxException {
         if (query != null) {
             final Map<String, Object> givenParams = 
URISupport.parseQuery(query);
             final Map<String, Object> params = new 
LinkedHashMap<>(givenParams.size());
@@ -409,7 +410,7 @@ public class RestProducer extends DefaultAsyncProducer {
                 if (v != null) {
                     String a = v.toString();
                     // decode the key as { may be decoded to %NN
-                    a = URLDecoder.decode(a, "UTF-8");
+                    a = URLDecoder.decode(a, StandardCharsets.UTF_8);
                     if (a.startsWith("{") && a.endsWith("}")) {
                         String key = a.substring(1, a.length() - 1);
                         boolean optional = false;
@@ -417,7 +418,11 @@ public class RestProducer extends DefaultAsyncProducer {
                             key = key.substring(0, key.length() - 1);
                             optional = true;
                         }
-                        Object value = inMessage.getHeader(key);
+                        // try header first and fallback to variable
+                        Object value = exchange.getMessage().getHeader(key);
+                        if (value == null) {
+                            value = exchange.getVariable(key);
+                        }
                         if (value != null) {
                             params.put(entry.getKey(), value);
                         } else if (!optional) {

Reply via email to