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 41db1dd34e6c CAMEL-25901: camel-jms - docs: a request-reply example in 
Java, XML and YAML, and the AI tools say to use InOut instead of setting 
JMSReplyTo (#28211)
41db1dd34e6c is described below

commit 41db1dd34e6c29b45c841c887f1f4d6e1b11d269
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Oct 21 21:30:27 2026 +0200

    CAMEL-25901: camel-jms - docs: a request-reply example in Java, XML and 
YAML, and the AI tools say to use InOut instead of setting JMSReplyTo (#28211)
    
    * CAMEL-25901: camel-jms - docs: a request-reply example in Java, XML and 
YAML, and the AI tools say to use InOut instead of setting JMSReplyTo
    
    - jms docs: "Sending a request and waiting for the reply" (Java, XML, YAML) 
after the first example: the caller sends
      with pattern InOut and variableReceive, the service route's last body is 
the reply, and Camel sets JMSReplyTo; it is
      the second jms sample. The filter example moves to "Routing JMS 
messages"; "Request-reply over JMS" points to it.
    - The header check: a header name with Reply that is not set 
(CamelJmsReplyTo) gets the request-reply hint (pattern:
      InOut on the to:, do not set JMSReplyTo), not "did you mean JMSReplyTo?".
    
    Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
    
    * CAMEL-25901: Limit the request-reply hint to JMS and reword the reply 
queue docs (address review)
    
    - The request-reply hint is given only when the closest header is 
JMSReplyTo, or for a Reply name with nothing
      close when the file's components set JMSReplyTo; other reply headers 
(CamelFtpReplyCode) keep "did you mean"
    - The hint and the docs say "you do not need to set" replyTo or JMSReplyTo, 
and point to a fixed reply queue
    - Note in the docs that setExchangePattern in Java keeps InOut for the rest 
of the route
    - REPLY_NAME moved above the INTENT_HINTS javadoc
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../apache/camel/catalog/docs/jms-component.adoc   | 92 +++++++++++++++++++++-
 .../camel-jms/src/main/docs/jms-component.adoc     | 92 +++++++++++++++++++++-
 .../dsl/jbang/core/commands/ai/HeaderChecks.java   | 20 ++++-
 .../commands/ai/SourceValidatorEndpointTest.java   | 36 +++++++++
 4 files changed, 237 insertions(+), 3 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jms-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jms-component.adoc
index aaba25bf786a..4c3ffef63385 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jms-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jms-component.adoc
@@ -323,6 +323,94 @@ YAML::
 ----
 ====
 
+=== Sending a request and waiting for the reply
+
+To send a request and wait for its reply, the caller sends with the `InOut` 
exchange pattern, and the service is a
+route that consumes the queue: the body at the end of its route is the reply. 
You do not need to set `replyTo` or a
+`JMSReplyTo` header: Camel uses a temporary reply queue by default and sets 
`JMSReplyTo` itself. Set `replyTo` for a
+fixed (shared or exclusive) reply queue, see <<Request-reply over JMS>>. With 
`variableReceive`, the reply goes into a
+variable, and the body is kept:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("timer:order?repeatCount=1")
+  .setBody(constant("{\"orderId\": \"ORD-1001\", \"sku\": \"CAMEL-MUG\"}"))
+  .setExchangePattern(ExchangePattern.InOut)
+  .toV("jms:queue:stock", null, "stockReply")
+  .log("Order ${body} got the reply ${variable.stockReply}");
+
+from("jms:queue:stock")
+  .log("Stock service got ${body}")
+  .setBody(constant("{\"reserved\": true}"));
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="timer:order?repeatCount=1"/>
+  <setBody>
+    <constant>{"orderId": "ORD-1001", "sku": "CAMEL-MUG"}</constant>
+  </setBody>
+  <to uri="jms:queue:stock" pattern="InOut" variableReceive="stockReply"/>
+  <log message="Order ${body} got the reply ${variable.stockReply}"/>
+</route>
+<route>
+  <from uri="jms:queue:stock"/>
+  <log message="Stock service got ${body}"/>
+  <setBody>
+    <constant>{"reserved": true}</constant>
+  </setBody>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: timer:order
+      parameters:
+        repeatCount: 1
+      steps:
+        - setBody:
+            expression:
+              constant:
+                expression: '{"orderId": "ORD-1001", "sku": "CAMEL-MUG"}'
+        - to:
+            uri: jms:queue:stock
+            pattern: InOut
+            variableReceive: stockReply
+        - log:
+            message: "Order ${body} got the reply ${variable.stockReply}"
+- route:
+    from:
+      uri: jms:queue:stock
+      steps:
+        - log:
+            message: "Stock service got ${body}"
+        - setBody:
+            expression:
+              constant:
+                expression: '{"reserved": true}'
+----
+====
+
+NOTE: In Java, `setExchangePattern(ExchangePattern.InOut)` keeps the exchange 
`InOut` for the rest of the route, while
+`pattern` on the XML and YAML `to` applies to that send only. Here it makes no 
difference, as only a `log` follows; with
+another `to("jms:...")` later in the route, set the pattern back with 
`setExchangePattern(ExchangePattern.InOnly)`, or
+use `to(ExchangePattern.InOut, "jms:...")`, which applies to that send only.
+
+The reply queue can also be shared or exclusive: see <<Request-reply over 
JMS>> below.
+
+=== Routing JMS messages
+
 You can use any of the EIP patterns so the route can be
 context based. For example, here's how to filter an order topic for the
 big spenders:
@@ -1107,7 +1195,9 @@ YAML::
 
 Camel supports Request Reply over JMS. In
 essence the MEP of the Exchange should be `InOut` when you send a
-message to a JMS queue.
+message to a JMS queue. The example in <<Sending a request and waiting for the 
reply>> shows the caller and the service.
+Camel sets the `JMSReplyTo` header and waits for the reply. You do not need to 
set `replyTo` or a `JMSReplyTo` header:
+Camel uses a temporary reply queue by default. Set `replyTo` for a fixed 
(shared or exclusive) reply queue, see below.
 
 Camel offers a number of options to configure request/reply over JMS
 that influence performance and clustered environments. The table below
diff --git a/components/camel-jms/src/main/docs/jms-component.adoc 
b/components/camel-jms/src/main/docs/jms-component.adoc
index aaba25bf786a..4c3ffef63385 100644
--- a/components/camel-jms/src/main/docs/jms-component.adoc
+++ b/components/camel-jms/src/main/docs/jms-component.adoc
@@ -323,6 +323,94 @@ YAML::
 ----
 ====
 
+=== Sending a request and waiting for the reply
+
+To send a request and wait for its reply, the caller sends with the `InOut` 
exchange pattern, and the service is a
+route that consumes the queue: the body at the end of its route is the reply. 
You do not need to set `replyTo` or a
+`JMSReplyTo` header: Camel uses a temporary reply queue by default and sets 
`JMSReplyTo` itself. Set `replyTo` for a
+fixed (shared or exclusive) reply queue, see <<Request-reply over JMS>>. With 
`variableReceive`, the reply goes into a
+variable, and the body is kept:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("timer:order?repeatCount=1")
+  .setBody(constant("{\"orderId\": \"ORD-1001\", \"sku\": \"CAMEL-MUG\"}"))
+  .setExchangePattern(ExchangePattern.InOut)
+  .toV("jms:queue:stock", null, "stockReply")
+  .log("Order ${body} got the reply ${variable.stockReply}");
+
+from("jms:queue:stock")
+  .log("Stock service got ${body}")
+  .setBody(constant("{\"reserved\": true}"));
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+  <from uri="timer:order?repeatCount=1"/>
+  <setBody>
+    <constant>{"orderId": "ORD-1001", "sku": "CAMEL-MUG"}</constant>
+  </setBody>
+  <to uri="jms:queue:stock" pattern="InOut" variableReceive="stockReply"/>
+  <log message="Order ${body} got the reply ${variable.stockReply}"/>
+</route>
+<route>
+  <from uri="jms:queue:stock"/>
+  <log message="Stock service got ${body}"/>
+  <setBody>
+    <constant>{"reserved": true}</constant>
+  </setBody>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: timer:order
+      parameters:
+        repeatCount: 1
+      steps:
+        - setBody:
+            expression:
+              constant:
+                expression: '{"orderId": "ORD-1001", "sku": "CAMEL-MUG"}'
+        - to:
+            uri: jms:queue:stock
+            pattern: InOut
+            variableReceive: stockReply
+        - log:
+            message: "Order ${body} got the reply ${variable.stockReply}"
+- route:
+    from:
+      uri: jms:queue:stock
+      steps:
+        - log:
+            message: "Stock service got ${body}"
+        - setBody:
+            expression:
+              constant:
+                expression: '{"reserved": true}'
+----
+====
+
+NOTE: In Java, `setExchangePattern(ExchangePattern.InOut)` keeps the exchange 
`InOut` for the rest of the route, while
+`pattern` on the XML and YAML `to` applies to that send only. Here it makes no 
difference, as only a `log` follows; with
+another `to("jms:...")` later in the route, set the pattern back with 
`setExchangePattern(ExchangePattern.InOnly)`, or
+use `to(ExchangePattern.InOut, "jms:...")`, which applies to that send only.
+
+The reply queue can also be shared or exclusive: see <<Request-reply over 
JMS>> below.
+
+=== Routing JMS messages
+
 You can use any of the EIP patterns so the route can be
 context based. For example, here's how to filter an order topic for the
 big spenders:
@@ -1107,7 +1195,9 @@ YAML::
 
 Camel supports Request Reply over JMS. In
 essence the MEP of the Exchange should be `InOut` when you send a
-message to a JMS queue.
+message to a JMS queue. The example in <<Sending a request and waiting for the 
reply>> shows the caller and the service.
+Camel sets the `JMSReplyTo` header and waits for the reply. You do not need to 
set `replyTo` or a `JMSReplyTo` header:
+Camel uses a temporary reply queue by default. Set `replyTo` for a fixed 
(shared or exclusive) reply queue, see below.
 
 Camel offers a number of options to configure request/reply over JMS
 that influence performance and clustered environments. The table below
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/HeaderChecks.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/HeaderChecks.java
index 47b8bd33f75a..e8294076ad79 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/HeaderChecks.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/HeaderChecks.java
@@ -102,6 +102,19 @@ final class HeaderChecks {
                + "=true on the endpoint, as the exchange property 
${exchangeProperty." + name + "}";
     }
 
+    /** A name that reaches for the reply of a request-reply, such as 
CamelJmsReplyTo. */
+    private static final Pattern REPLY_NAME = Pattern.compile("Reply");
+
+    /**
+     * For a reply header read or set by hand on a JMS route: Camel does 
request-reply itself (r20 runs built replies by
+     * hand after "did you mean JMSReplyTo?"). Only given when the file's 
components set JMSReplyTo, so the reply
+     * headers of other components (CamelFtpReplyCode, CamelNatsReplyTo) keep 
their "did you mean".
+     */
+    static final String JMS_REQUEST_REPLY_HINT
+            = "For request-reply over JMS, send with pattern: InOut on the to: 
and Camel sets JMSReplyTo and waits for the"
+              + " reply (the body at the end of the service route); you do not 
need to set JMSReplyTo or replyTo"
+              + " yourself.";
+
     /**
      * What an invented Camel* header is usually reaching for, keyed on a 
fragment of its name, and the expression that
      * answers it. Tried when no real header is close: in a timer-triggered 
route there is no message source to name, so
@@ -293,8 +306,13 @@ final class HeaderChecks {
                 String scheme = best != null && 
owner.containsKey(best.split(", ")[0]) ? owner.get(best.split(", ")[0]) : null;
                 StringBuilder sb = new StringBuilder("Line ").append(i + 
1).append(": header ").append(name)
                         .append(" is not set by ").append(String.join(", ", 
schemes)).append(" (the value would be null)");
-                if (best != null) {
+                if (best != null && "JMSReplyTo".equals(best.split(", ")[0])) {
+                    // "did you mean JMSReplyTo?" led to replies set by hand: 
request-reply is done by Camel
+                    sb.append(" ").append(JMS_REQUEST_REPLY_HINT);
+                } else if (best != null) {
                     sb.append(": did you mean ").append(best).append("?");
+                } else if (known.contains("JMSReplyTo") && 
REPLY_NAME.matcher(name).find()) {
+                    sb.append(" ").append(JMS_REQUEST_REPLY_HINT);
                 } else {
                     String hint = intentHint(name);
                     if (hint != null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointTest.java
index 5ffd282fe596..d3325f4a0527 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorEndpointTest.java
@@ -587,6 +587,42 @@ class SourceValidatorEndpointTest {
                 .contains("did you mean Exchange.FILE_NAME");
     }
 
+    /** "did you mean JMSReplyTo?" led to replies set by hand: Camel does 
request-reply with pattern InOut (r20). */
+    @Test
+    void aReplyHeaderGetsTheRequestReplyHint() {
+        List<String> msgs = SourceValidator.validateKnownHeaders("""
+                - from:
+                    uri: jms:queue:orders
+                    steps:
+                      - setHeader:
+                          name: CamelJmsReplyTo
+                          constant: stock-reply
+                      - to:
+                          uri: jms:queue:stock
+                """, catalog);
+        assertThat(msgs).singleElement().satisfies(m -> assertThat(m)
+                .contains("header CamelJmsReplyTo is not set by jms")
+                .contains("send with pattern: InOut on the to: and Camel sets 
JMSReplyTo and waits for the reply")
+                .contains("you do not need to set JMSReplyTo or replyTo 
yourself")
+                .doesNotContain("did you mean"));
+    }
+
+    /** The JMS request-reply hint is for JMS only: the reply headers of other 
components keep their "did you mean". */
+    @Test
+    void aReplyHeaderOfAnotherComponentGetsDidYouMean() {
+        List<String> msgs = SourceValidator.validateKnownHeaders("""
+                - from:
+                    uri: ftp:localhost/inbox
+                    steps:
+                      - log:
+                          message: "${header.CamelFtpReplycode}"
+                """, catalog);
+        assertThat(msgs).singleElement().satisfies(m -> assertThat(m)
+                .contains("header CamelFtpReplycode is not set by ftp")
+                .contains("did you mean CamelFtpReplyCode")
+                .doesNotContain("request-reply over JMS"));
+    }
+
     @Test
     void withIncludeMetadataAHeaderLookupFindsTheExchangeProperty() {
         // Simple's ${header.x} falls back to the exchange properties, so it 
reads the counter

Reply via email to