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