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 6211b526cf81 CAMEL-25783: camel-jms - the docs show how to connect to
a broker, and the AI tools find jms for Artemis and IBM MQ (#28097)
6211b526cf81 is described below
commit 6211b526cf81b8c2202a3c63086bd53def628a0d
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Oct 19 19:57:04 2026 +0200
CAMEL-25783: camel-jms - the docs show how to connect to a broker, and the
AI tools find jms for Artemis and IBM MQ (#28097)
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../apache/camel/catalog/docs/jms-component.adoc | 115 ++++++++++++++++++---
.../camel-jms/src/main/docs/jms-component.adoc | 115 ++++++++++++++++++---
.../dsl/jbang/core/commands/ai/CatalogDocs.java | 12 +++
.../dsl/jbang/core/commands/ai/CatalogSamples.java | 8 +-
.../jbang/core/commands/ai/CatalogDocsTest.java | 12 +++
.../jbang/core/commands/ai/CatalogSamplesTest.java | 8 ++
6 files changed, 235 insertions(+), 35 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 8a6da1bbf46f..aaba25bf786a 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
@@ -35,11 +35,11 @@ for this component:
====
*Using ActiveMQ*
-If you are using http://activemq.apache.org/[Apache ActiveMQ], you
+If you are using http://activemq.apache.org/[Apache ActiveMQ] Classic, you
should prefer the ActiveMQ component as it has been
optimized for ActiveMQ. All the options and
samples on this page are also valid for the ActiveMQ
-component.
+component. For ActiveMQ Artemis, use this component, as shown in _Connecting
to a broker_ below.
====
[NOTE]
@@ -60,6 +60,78 @@ number of options to configure for performance, and clustered
environments.
====
+== Connecting to a broker
+
+The JMS component needs a `ConnectionFactory` from the client library of the
broker. Create it as a bean, and set it
+on the component with the `connectionFactory` option. Put a connection pool
around it: without a pool, the component
+opens a new connection for every message it sends.
+
+For example, for ActiveMQ Artemis (such as the broker that `camel infra run
artemis` starts), with the
+https://github.com/messaginghub/pooled-jms[pooled-jms] connection pool:
+
+[tabs]
+====
+Application properties::
++
+[source,properties]
+----
+camel.beans.artemisCF =
#class:org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory
+camel.beans.artemisCF.brokerURL = tcp://localhost:61616
+camel.beans.artemisCF.user = artemis
+camel.beans.artemisCF.password = artemis
+camel.beans.pooledCF =
#class:org.messaginghub.pooled.jms.JmsPoolConnectionFactory
+camel.beans.pooledCF.connectionFactory = #bean:artemisCF
+camel.beans.pooledCF.maxConnections = 8
+camel.component.jms.connection-factory = #bean:pooledCF
+----
+
+Java::
++
+[source,java]
+----
+ActiveMQConnectionFactory artemis = new
ActiveMQConnectionFactory("tcp://localhost:61616", "artemis", "artemis");
+JmsPoolConnectionFactory pool = new JmsPoolConnectionFactory();
+pool.setConnectionFactory(artemis);
+pool.setMaxConnections(8);
+context.getComponent("jms", JmsComponent.class).setConnectionFactory(pool);
+----
+
+Spring XML::
++
+[source,xml]
+----
+<bean id="artemisCF"
class="org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory">
+ <property name="brokerURL" value="tcp://localhost:61616"/>
+ <property name="user" value="artemis"/>
+ <property name="password" value="artemis"/>
+</bean>
+
+<bean id="pooledCF"
class="org.messaginghub.pooled.jms.JmsPoolConnectionFactory"
destroy-method="stop">
+ <property name="connectionFactory" ref="artemisCF"/>
+ <property name="maxConnections" value="8"/>
+</bean>
+
+<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
+ <property name="connectionFactory" ref="pooledCF"/>
+</bean>
+----
+====
+
+The application properties work with routes in any DSL (YAML, XML or Java) on
Camel Main and Camel JBang. Then the
+routes use the broker as usual, for example `jms:queue:orders`.
+
+When the registry has exactly one `ConnectionFactory`, the component uses it
without the `connectionFactory` option
+(`allowAutoWiredConnectionFactory`, which is on by default). The example has
two, the one of the client and the pool,
+so it sets the pool on the component.
+
+`camel run` downloads the client libraries of these classes. In a Maven
project, add
+`org.apache.artemis:artemis-jakarta-client` and `org.messaginghub:pooled-jms`.
+
+Other brokers work the same way, with the `ConnectionFactory` of their client
library, for example
+`com.ibm.mq.jakarta.jms.MQConnectionFactory` for IBM MQ (with `hostName`,
`port`, `channel`, `queueManager`, and
+`transportType` set to `1` for a client connection). For ActiveMQ Classic, the
xref:activemq-component.adoc[ActiveMQ]
+component sets up the connection factory from a `brokerURL`.
+
== URI format
----
@@ -887,24 +959,33 @@ another JMS endpoint).
=== Configuring different JMS providers
-You can configure your JMS provider in Spring XML as
-follows:
+You can configure as many JMS component instances as you wish, each with its
own connection factory, and give each a
+unique name. For example, a component named `artemis`, with the `pooledCF`
connection factory from _Connecting to a
+broker_ above:
-You can configure as many JMS component instances as you wish
-and give them *a unique name using the* `id` **attribute**. The
-preceding example configures an `activemq` component. You could do the
-same to configure MQSeries, TibCo, BEA, Sonic and so on.
+[tabs]
+====
+Application properties::
++
+[source,properties]
+----
+camel.beans.artemis = #class:org.apache.camel.component.jms.JmsComponent
+camel.beans.artemis.connectionFactory = #bean:pooledCF
+----
-Once you have a named JMS component, you can then refer to endpoints
-within that component using URIs. For example, for the component name,
-`activemq`, you can then refer to destinations using the URI format,
-`activemq:[queue:|topic:]destinationName`. You can use the same approach
-for all other JMS providers.
+Spring XML::
++
+[source,xml]
+----
+<bean id="artemis" class="org.apache.camel.component.jms.JmsComponent">
+ <property name="connectionFactory" ref="pooledCF"/>
+</bean>
+----
+====
-This works by the SpringCamelContext lazily fetching components from the
-spring context for the scheme name you use for
-Endpoint URIs and having the
-Component resolve the endpoint URIs.
+Once you have a named JMS component, you can refer to destinations of that
component with its name as the scheme, for
+example `artemis:[queue:|topic:]destinationName`. Camel looks up the component
by that name in the registry. You can
+do the same for other JMS providers, such as IBM MQ or TIBCO EMS.
==== Using JNDI to find the ConnectionFactory
diff --git a/components/camel-jms/src/main/docs/jms-component.adoc
b/components/camel-jms/src/main/docs/jms-component.adoc
index 8a6da1bbf46f..aaba25bf786a 100644
--- a/components/camel-jms/src/main/docs/jms-component.adoc
+++ b/components/camel-jms/src/main/docs/jms-component.adoc
@@ -35,11 +35,11 @@ for this component:
====
*Using ActiveMQ*
-If you are using http://activemq.apache.org/[Apache ActiveMQ], you
+If you are using http://activemq.apache.org/[Apache ActiveMQ] Classic, you
should prefer the ActiveMQ component as it has been
optimized for ActiveMQ. All the options and
samples on this page are also valid for the ActiveMQ
-component.
+component. For ActiveMQ Artemis, use this component, as shown in _Connecting
to a broker_ below.
====
[NOTE]
@@ -60,6 +60,78 @@ number of options to configure for performance, and clustered
environments.
====
+== Connecting to a broker
+
+The JMS component needs a `ConnectionFactory` from the client library of the
broker. Create it as a bean, and set it
+on the component with the `connectionFactory` option. Put a connection pool
around it: without a pool, the component
+opens a new connection for every message it sends.
+
+For example, for ActiveMQ Artemis (such as the broker that `camel infra run
artemis` starts), with the
+https://github.com/messaginghub/pooled-jms[pooled-jms] connection pool:
+
+[tabs]
+====
+Application properties::
++
+[source,properties]
+----
+camel.beans.artemisCF =
#class:org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory
+camel.beans.artemisCF.brokerURL = tcp://localhost:61616
+camel.beans.artemisCF.user = artemis
+camel.beans.artemisCF.password = artemis
+camel.beans.pooledCF =
#class:org.messaginghub.pooled.jms.JmsPoolConnectionFactory
+camel.beans.pooledCF.connectionFactory = #bean:artemisCF
+camel.beans.pooledCF.maxConnections = 8
+camel.component.jms.connection-factory = #bean:pooledCF
+----
+
+Java::
++
+[source,java]
+----
+ActiveMQConnectionFactory artemis = new
ActiveMQConnectionFactory("tcp://localhost:61616", "artemis", "artemis");
+JmsPoolConnectionFactory pool = new JmsPoolConnectionFactory();
+pool.setConnectionFactory(artemis);
+pool.setMaxConnections(8);
+context.getComponent("jms", JmsComponent.class).setConnectionFactory(pool);
+----
+
+Spring XML::
++
+[source,xml]
+----
+<bean id="artemisCF"
class="org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory">
+ <property name="brokerURL" value="tcp://localhost:61616"/>
+ <property name="user" value="artemis"/>
+ <property name="password" value="artemis"/>
+</bean>
+
+<bean id="pooledCF"
class="org.messaginghub.pooled.jms.JmsPoolConnectionFactory"
destroy-method="stop">
+ <property name="connectionFactory" ref="artemisCF"/>
+ <property name="maxConnections" value="8"/>
+</bean>
+
+<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
+ <property name="connectionFactory" ref="pooledCF"/>
+</bean>
+----
+====
+
+The application properties work with routes in any DSL (YAML, XML or Java) on
Camel Main and Camel JBang. Then the
+routes use the broker as usual, for example `jms:queue:orders`.
+
+When the registry has exactly one `ConnectionFactory`, the component uses it
without the `connectionFactory` option
+(`allowAutoWiredConnectionFactory`, which is on by default). The example has
two, the one of the client and the pool,
+so it sets the pool on the component.
+
+`camel run` downloads the client libraries of these classes. In a Maven
project, add
+`org.apache.artemis:artemis-jakarta-client` and `org.messaginghub:pooled-jms`.
+
+Other brokers work the same way, with the `ConnectionFactory` of their client
library, for example
+`com.ibm.mq.jakarta.jms.MQConnectionFactory` for IBM MQ (with `hostName`,
`port`, `channel`, `queueManager`, and
+`transportType` set to `1` for a client connection). For ActiveMQ Classic, the
xref:activemq-component.adoc[ActiveMQ]
+component sets up the connection factory from a `brokerURL`.
+
== URI format
----
@@ -887,24 +959,33 @@ another JMS endpoint).
=== Configuring different JMS providers
-You can configure your JMS provider in Spring XML as
-follows:
+You can configure as many JMS component instances as you wish, each with its
own connection factory, and give each a
+unique name. For example, a component named `artemis`, with the `pooledCF`
connection factory from _Connecting to a
+broker_ above:
-You can configure as many JMS component instances as you wish
-and give them *a unique name using the* `id` **attribute**. The
-preceding example configures an `activemq` component. You could do the
-same to configure MQSeries, TibCo, BEA, Sonic and so on.
+[tabs]
+====
+Application properties::
++
+[source,properties]
+----
+camel.beans.artemis = #class:org.apache.camel.component.jms.JmsComponent
+camel.beans.artemis.connectionFactory = #bean:pooledCF
+----
-Once you have a named JMS component, you can then refer to endpoints
-within that component using URIs. For example, for the component name,
-`activemq`, you can then refer to destinations using the URI format,
-`activemq:[queue:|topic:]destinationName`. You can use the same approach
-for all other JMS providers.
+Spring XML::
++
+[source,xml]
+----
+<bean id="artemis" class="org.apache.camel.component.jms.JmsComponent">
+ <property name="connectionFactory" ref="pooledCF"/>
+</bean>
+----
+====
-This works by the SpringCamelContext lazily fetching components from the
-spring context for the scheme name you use for
-Endpoint URIs and having the
-Component resolve the endpoint URIs.
+Once you have a named JMS component, you can refer to destinations of that
component with its name as the scheme, for
+example `artemis:[queue:|topic:]destinationName`. Camel looks up the component
by that name in the registry. You can
+do the same for other JMS providers, such as IBM MQ or TIBCO EMS.
==== Using JNDI to find the ConnectionFactory
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java
index 76074aa8e7a0..208883f172cc 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java
@@ -993,6 +993,18 @@ public final class CatalogDocs {
matches.add(o);
}
}
+ // what a component does, or a broker it reaches without a
component of its own, such as artemis with the
+ // jms component (CAMEL-25783)
+ String[] intent =
CatalogSamples.COMPONENT_INTENTS.get(term.trim().toLowerCase(Locale.ROOT));
+ if (intent != null && matches.stream().noneMatch(o ->
intent[0].equals(((JsonObject) o).getString("name")))) {
+ ComponentModel m = catalog.componentModel(intent[0]);
+ if (m != null) {
+ JsonObject o = summary("component", m.getScheme(),
m.getTitle(), m.getDescription(), m.getLabel());
+ o.put("syntax", m.getSyntax());
+ o.put("note", "'" + term.trim() + "' is done with " +
intent[1]);
+ matches.add(0, o);
+ }
+ }
}
if (kind == null || "dataformat".equals(kind)) {
for (String name : catalog.suggestDataFormatNames(term, max)) {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
index 0bb0209f06e3..fdc543f29d8b 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamples.java
@@ -109,6 +109,10 @@ public final class CatalogSamples {
private static final String[] HTTP_CALL = {
"http", "a to: with the http component (toD: when the URI is built
from the message, such as a path"
+ " parameter); the response becomes the message body" };
+ // a JMS broker without a component of its own (CAMEL-25783)
+ private static final String[] JMS_BROKER = {
+ "jms", "the jms component, with the ConnectionFactory of the
broker's client library as a bean on the"
+ + " component (the jms page shows it under Connecting to a
broker)" };
private static final String[] OPENAPI_CALL = {
"rest-openapi", "a to: with the rest-openapi component, its
specificationUri and operationId; path and"
+ " query parameters come from headers of the same
name, the body is the request body" };
@@ -138,7 +142,9 @@ public final class CatalogSamples {
Map.entry("call service", HTTP_CALL), Map.entry("call",
HTTP_CALL), Map.entry("http call", HTTP_CALL),
Map.entry("call api", HTTP_CALL), Map.entry("http client",
HTTP_CALL), Map.entry("call http", HTTP_CALL),
Map.entry("openapi client", OPENAPI_CALL), Map.entry("call by
contract", OPENAPI_CALL),
- Map.entry("call operation", OPENAPI_CALL));
+ Map.entry("call operation", OPENAPI_CALL),
+ Map.entry("artemis", JMS_BROKER), Map.entry("activemq artemis",
JMS_BROKER), Map.entry("ibm mq", JMS_BROKER),
+ Map.entry("ibmmq", JMS_BROKER), Map.entry("websphere mq",
JMS_BROKER));
private static volatile Map<String, List<Map<String, String>>> samples;
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
index b93f3d60f0a3..ae686c0d8080 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
@@ -256,6 +256,18 @@ class CatalogDocsTest {
assertEquals("==", ((JsonObject)
eq.getCollection("operators").iterator().next()).getString("name"));
}
+ @Test
+ void aBrokerWithoutAComponentOfItsOwnFindsJms() {
+ // CAMEL-25783: nothing in the catalog is named artemis, so a model
asking for it found nothing
+ DefaultCamelCatalog catalog = new DefaultCamelCatalog();
+ for (String term : List.of("artemis", "ibm mq")) {
+ JsonArray matches = (JsonArray) CatalogDocs.find(catalog, term,
null, 10).get("matches");
+ JsonObject first = (JsonObject) matches.get(0);
+ assertEquals("jms", first.getString("name"), term);
+ assertTrue(first.getString("note").contains("Connecting to a
broker"), first.toJson());
+ }
+ }
+
@Test
void anInterfaceNameFindsTheBuiltInBeans() {
org.apache.camel.catalog.CamelCatalog catalog = new
org.apache.camel.catalog.DefaultCamelCatalog();
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
index 16ca551d75ee..9ae56ca3e31d 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
@@ -177,6 +177,14 @@ public class CatalogSamplesTest {
assertThat(CatalogSamples.sample(catalog, "lookup",
1).getString("name")).isEqualTo("enrich");
}
+ @Test
+ void artemisIsTheJmsComponentWithItsConnectionFactory() {
+ // CAMEL-25783: a broker without a component of its own
+ JsonObject o = CatalogSamples.sample(new DefaultCamelCatalog(),
"artemis", 1);
+ assertThat(o.getString("name")).isEqualTo("jms");
+ assertThat(o.getString("note")).contains("ConnectionFactory",
"Connecting to a broker");
+ }
+
@Test
void callingAServiceIsTheHttpComponentNotEnrich() {
CamelCatalog catalog = new DefaultCamelCatalog();