Chapter 7. Using JMS connection factories
This chapter describes how to use JMS connection factories in OSGi. Fundamentally, you do it by using:
org.osgi.framework.BundleContext.registerService(javax.jms.ConnectionFactory.class, connectionFactoryObject, properties); org.osgi.framework.BundleContext.registerService(javax.jms.XAConnectionFactory.class, xaConnectionFactoryObject, properties);
There are two different methods to register such services:
-
Publishing connection factories by using the
jms:create
Karaf console command. This is the configuration method. -
Publishing connection factories by using methods such as Blueprint, OSGi Declarative Services (SCR) or just a
BundleContext.registerService()
API call. This method requires a dedicated OSGi bundle that contains the code and/or metadata. This is the deployment method.
Details are in the following topics:
7.1. About the OSGi JMS service
The OSGi way of handling JDBC data sources is related to two interfaces:
-
standard
org.osgi.service.jdbc.DataSourceFactory
-
proprietary
org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory
For JMS, consider these analogies:
-
proprietary
org.ops4j.pax.jms.service.ConnectionFactoryFactory
with the same purpose as standard OSGi JDBCorg.osgi.service.jdbc.DataSourceFactory
-
proprietary
org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
with the same purpose as proprietary pax-jdbcorg.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory
For the dedicated, broker-specific, org.ops4j.pax.jms.service.ConnectionFactoryFactory
implementations, there are bundles such as:
-
mvn:org.ops4j.pax.jms/pax-jms-artemis/1.0.0
-
mvn:org.ops4j.pax.jms/pax-jms-ibmmq/1.0.0
-
mvn:org.ops4j.pax.jms/pax-jms-activemq/1.0.0
These bundles register broker-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory
services that can return JMS factories such as javax.jms.ConnectionFactory
and javax.jms.XAConnectionFactory
. For example:
karaf@root()> feature:install pax-jms-artemis karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-config OPS4J Pax JMS Config (248) provides: ------------------------------------ objectClass = [org.osgi.service.cm.ManagedServiceFactory] service.bundleid = 248 service.id = 328 service.pid = org.ops4j.connectionfactory service.scope = singleton karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-artemis OPS4J Pax JMS Artemis Support (247) provides: --------------------------------------------- objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory] service.bundleid = 247 service.id = 327 service.scope = singleton type = artemis
7.2. About the PAX-JMS configuration service
The mvn:org.ops4j.pax.jms/pax-jms-config/1.0.0
bundle provides a Managed Service Factory that does three things:
Tracks
org.ops4j.pax.jms.service.ConnectionFactoryFactory
OSGi services to invoke its methods:public ConnectionFactory createConnectionFactory(Map<String, Object> properties); public XAConnectionFactory createXAConnectionFactory(Map<String, Object> properties);
-
Tracks
org.ops4j.connectionfactory
factory PIDs to collect properties that are required by the above methods. If you create a factory configuration by using any method available for Configuration Admin service, for example, by creating a${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg
file, you can perform the final step to expose a broker-specific connection factory. -
Tracks
javax.jms.ConnectionFactory
andjavax.jms.XAConnectionFactory
services to wrap them inside pooling JMS connection factories.
Details are in the following topics:
7.2.1. Creating a connection factory for AMQ 7.1
Following is the detailed, canonical, step-by-step guide for creating a connection factor for an Artemis broker.
Install the Artemis driver by using the
pax-jms-artemis
feature and thepax-jms-config
feature:karaf@root()> feature:install pax-jms-artemis karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-config OPS4J Pax JMS Config (248) provides: ------------------------------------ objectClass = [org.osgi.service.cm.ManagedServiceFactory] service.bundleid = 248 service.id = 328 service.pid = org.ops4j.connectionfactory service.scope = singleton karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-artemis OPS4J Pax JMS Artemis Support (247) provides: --------------------------------------------- objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory] service.bundleid = 247 service.id = 327 service.scope = singleton type = artemis
Create a factory configuration:
karaf@root()> config:edit --factory --alias artemis org.ops4j.connectionfactory karaf@root()> config:property-set type artemis karaf@root()> config:property-set osgi.jndi.service.name jms/artemis # "name" property may be used too karaf@root()> config:property-set connectionFactoryType ConnectionFactory # or XAConnectionFactory karaf@root()> config:property-set jms.url tcp://localhost:61616 karaf@root()> config:property-set jms.user admin karaf@root()> config:property-set jms.password admin karaf@root()> config:property-set jms.consumerMaxRate 1234 karaf@root()> config:update karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)' ---------------------------------------------------------------- Pid: org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703 FactoryPid: org.ops4j.connectionfactory BundleLocation: ? Properties: connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:${karar.etc}/org.ops4j.connectionfactory-artemis.cfg jms.consumerMaxRate = 1234 jms.password = admin jms.url = tcp://localhost:61616 jms.user = admin osgi.jndi.service.name = jms/artemis service.factoryPid = org.ops4j.connectionfactory service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703 type = artemis
Check if
pax-jms-config
processed the configuration into thejavax.jms.ConnectionFactory
service:karaf@root()> service:list javax.jms.ConnectionFactory [javax.jms.ConnectionFactory] ----------------------------- connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg jms.consumerMaxRate = 1234 jms.password = admin jms.url = tcp://localhost:61616 jms.user = admin osgi.jndi.service.name = jms/artemis pax.jms.managed = true service.bundleid = 248 service.factoryPid = org.ops4j.connectionfactory service.id = 342 service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703 service.scope = singleton type = artemis Provided by : OPS4J Pax JMS Config (248)
NoteIf you specify additional Artemis configuration, specifically
protocol=amqp
, the QPID JMS library would be used instead of the Artemis JMS client. Theamqp://
protocol has to be used then forjms.url
property.- Test the connection.
You now have a broker-specific (no pooling yet) connection factory that you can inject where needed. For example, you can use Karaf commands from the jms
feature:
karaf@root()> feature:install -v jms Adding features: jms/[4.2.0.fuse-000237-redhat-1,4.2.0.fuse-000237-redhat-1] ... karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/artemis karaf@root()> jms:info -u admin -p admin jms/artemis Property │ Value ─────────┼────────────────────────── product │ ActiveMQ version │ 2.4.0.amq-711002-redhat-1 karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis" karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1 Message ID │ Content │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp ────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼────────────────────────────┼────────────┼──────────┼─────────────┼─────────┼────────────────────────────── ID:2b6ea56d-574d-11e8-971a-7ee9ecc029d4 │ Hello Artemis │ UTF-8 │ │ │ Persistent │ ActiveMQQueue[DEV.QUEUE.1] │ Never │ 4 │ false │ │ Mon May 14 10:02:38 CEST 2018
The following listing shows what happens when you switch the protocol:
karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)' ---------------------------------------------------------------- Pid: org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703 FactoryPid: org.ops4j.connectionfactory BundleLocation: ? Properties: connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg jms.consumerMaxRate = 1234 jms.password = fuse jms.url = tcp://localhost:61616 jms.user = fuse osgi.jndi.service.name = jms/artemis service.factoryPid = org.ops4j.connectionfactory service.pid = org.ops4j.connectionfactory.965d4eac-f5a7-4f65-ba1a-15caa4c72703 type = artemis karaf@root()> config:edit org.ops4j.connectionfactory.312eb09a-d686-4229-b7e1-2ea38a77bb0f karaf@root()> config:property-set protocol amqp karaf@root()> config:property-delete user karaf@root()> config:property-set username admin # mind the difference between artemis-jms-client and qpid-jms-client karaf@root()> config:property-set jms.url amqp://localhost:61616 karaf@root()> config:update karaf@root()> jms:info -u admin -p admin jms/artemis Property │ Value ─────────┼──────────────── product │ QpidJMS version │ 0.30.0.redhat-1 karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1 Message ID │ Content │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp ───────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼─────────────┼────────────┼──────────┼─────────────┼─────────┼────────────────────────────── │ Hello Artemis │ UTF-8 │ │ │ Persistent │ DEV.QUEUE.1 │ Never │ 4 │ false │ │ Mon May 14 10:02:38 CEST 2018
7.2.2. Creating a connection factory for IBM MQ 8 or IBM MQ 9
This section shows how to connect to IBM MQ 8 and IBM MQ 9. Even though pax-jms-ibmmq
installs the relevant pax-jms
bundles, the IBM MQ driver is not installed due to licensing reasons.
- Go to https://developer.ibm.com/messaging/mq-downloads/
- Log in.
- Click the version that you want to install, for example, click IBM MQ 8.0 Client or IBM MQ 9.0 Client.
- In the page that appears, at the bottom, in the table of download versions, click the version that you want.
-
In the next page, select the latest version that has the suffix
IBM-MQ-Install-Java-All
. For example, download8.0.0.10-WS-MQ-Install-Java-All
or9.0.0.4-IBM-MQ-Install-Java-All
. - Extract the content of the downloaded JAR file.
Execute the
bundle:install
command. For example, if you extracted the content into your/home/Downloads
directory, you would enter a command such as the following:`bundle:install -s wrap:file:////home/Downloads/9.0.0.4-IBM-MQ-Install-Java-All/ibmmq9/wmq/JavaSE/com.ibm.mq.allclient.jar`.
Create the connection factory as follows:
Install
pax-jms-ibmmq
:karaf@root()> feature:install pax-jms-ibmmq karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-ibmmq OPS4J Pax JMS IBM MQ Support (239) provides: -------------------------------------------- objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory] service.bundleid = 239 service.id = 346 service.scope = singleton type = ibmmq
Create factory configuration:
karaf@root()> config:edit --factory --alias ibmmq org.ops4j.connectionfactory karaf@root()> config:property-set type ibmmq karaf@root()> config:property-set osgi.jndi.service.name jms/mq9 # "name" property may be used too karaf@root()> config:property-set connectionFactoryType ConnectionFactory # or XAConnectionFactory karaf@root()> config:property-set jms.queueManager FUSEQM karaf@root()> config:property-set jms.hostName localhost karaf@root()> config:property-set jms.port 1414 karaf@root()> config:property-set jms.transportType 1 # com.ibm.msg.client.wmq.WMQConstants.WMQ_CM_CLIENT karaf@root()> config:property-set jms.channel DEV.APP.SVRCONN karaf@root()> config:property-set jms.CCSID 1208 # com.ibm.msg.client.jms.JmsConstants.CCSID_UTF8 karaf@root()> config:update karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)' ---------------------------------------------------------------- Pid: org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863 FactoryPid: org.ops4j.connectionfactory BundleLocation: ? Properties: connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-ibmmq.cfg jms.CCSID = 1208 jms.channel = DEV.APP.SVRCONN jms.hostName = localhost jms.port = 1414 jms.queueManager = FUSEQM jms.transportType = 1 osgi.jndi.service.name = jms/mq9 service.factoryPid = org.ops4j.connectionfactory service.pid = org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863 type = ibmmq
Check if
pax-jms-config
processed the configuration intojavax.jms.ConnectionFactory
service:karaf@root()> service:list javax.jms.ConnectionFactory [javax.jms.ConnectionFactory] ----------------------------- connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:/data/servers/7.3.0.fuse-730079-redhat-00001/etc/org.ops4j.connectionfactory-ibmmq.cfg jms.CCSID = 1208 jms.channel = DEV.APP.SVRCONN jms.hostName = localhost jms.port = 1414 jms.queueManager = FUSEQM jms.transportType = 1 osgi.jndi.service.name = jms/mq9 pax.jms.managed = true service.bundleid = 237 service.factoryPid = org.ops4j.connectionfactory service.id = 347 service.pid = org.ops4j.connectionfactory.eee4a757-a95d-46b8-b8b6-19aa3977d863 service.scope = singleton type = ibmmq Provided by : OPS4J Pax JMS Config (237)
Test the connection:
karaf@root()> feature:install -v jms Adding features: jms/[4.2.0.fuse-000237-redhat-1,4.2.0.fuse-000237-redhat-1] ... karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/mq9 karaf@root()> jms:info -u app -p fuse jms/mq9 Property │ Value ─────────┼──────────────────── product │ IBM MQ JMS Provider version │ 8.0.0.0 karaf@root()> jms:send -u app -p fuse jms/mq9 DEV.QUEUE.1 "Hello IBM MQ 9" karaf@root()> jms:browse -u app -p fuse jms/mq9 DEV.QUEUE.1 Message ID │ Content │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp ────────────────────────────────────────────────────┼─────────────────────────────┼─────────┼──────┼────────────────┼───────────────┼──────────────────────┼────────────┼──────────┼─────────────┼─────────┼────────────────────────────── ID:414d512046555345514d202020202020c940f95a038b3220 │ Hello IBM MQ 9 │ UTF-8 │ │ │ Persistent │ queue:///DEV.QUEUE.1 │ Never │ 4 │ false │ │ Mon May 14 10:17:01 CEST 2018
You can also check if the message was sent from IBM MQ Explorer or from the web console.
7.2.3. Using JBoss A-MQ 6.3 Client in Fuse on Apache Karaf
This section explains how you can modify camel-jms quickstart so that it can use JBoss A-MQ 6.3 client to communicate with JBoss A-MQ 6.3 broker (the downloaded camel-jms quickstart is actually based on Red Hat AMQ 7, so some modifications must be made to the quickstart so that it can adapt to JBoss A-MQ 6.3). You can build and install the camel-jms example as an OSGi bundle. This bundle contains a Blueprint XML definition of a Camel route that sends messages to an JBoss A-MQ 6.3 JMS queue. The procedure for creating a connection factory for JBoss A-MQ 6.3 broker is as follows.
You can download Red Hat Fuse 7.3.0 Quickstarts for Karaf from the Fuse Software Downloads page. Extract the contents of the quickstarts zip file to a local folder, for example a folder named quickstarts
.
7.2.3.1. Prerequisites
- You have installed Maven 3.3.1 or higher.
- You have Red Hat Fuse installed on your machine.
- You have JBoss A-MQ Broker 6.3 installed on your machine.
- You have downloaded and extracted the Fuse on Karaf quickstarts zip file from the customer portal.
7.2.3.2. Procedure
-
Navigate to
quickstarts/camel/camel-jms/src/main/resources/OSGI-INF/blueprint/
directory. Replace the following bean with id="jms" from the
camel-context.xml
file:<bean id="jms" class="org.apache.camel.component.jms.JmsComponent"> <property name="connectionFactory"> <reference interface="javax.jms.ConnectionFactory" /> </property> <property name="transactionManager" ref="transactionManager"/> </bean>
With the following section to instantiate the JBoss A-MQ 6.3 connection factory:
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent"> <property name="connectionFactory" ref="activemqConnectionFactory"/> <property name="transactionManager" ref="transactionManager"/> </bean> <bean id="activemqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616"/> <property name="userName" value="admin"/> <property name="password" value="admin"/> </bean>
The JBoss A-MQ 6.3 connection factory is configured to connect to a broker listening at
tcp://localhost:61616
. By default JBoss A-MQ uses the IP port value61616
. The connection factory is also configured to use the userName/password credentials, admin/admin. Make sure that this user is enable in your broker cofiguration (or you can customize these settings here to match your broker configuration).-
Save the
camel-context.xml
file. Build the
camel-jms
quickstart:$ cd quickstarts/camel/camel-jms $ mvn install
After the quickstart is successfully installed, navigate to
$FUSE_HOME/
directory and run the following command to start the Fuse on Apache Karaf server:$ ./bin/fuse
On the Fuse on Apache Karaf instance install
activemq-client
feature andcamel-jms
feature:karaf@root()> feature:install activemq-client karaf@root()> feature:install camel-jms
Install the
camel-jms
quickstart bundle:karaf@root()> install -s mvn:org.jboss.fuse.quickstarts/camel-jms/{$fuseversion}
Where replace
{$fuseversion}
with the actual version of the Maven artifact that you just built (consult the camel-jms quickstart README file).Start the
JBoss A-MQ 6.3
broker (you need an installation of JBoss A-MQ 6.3 for this). Open another terminal window and navigate to JBOSS_AMQ_63_INSTALLDIR directory:$ cd JBOSS_AMQ_63_INSTALLDIR $ ./bin/amq
-
As soon as the Camel routes have started, you can see a directory
work/jms/input
in your Fuse installation. Copy the files you find in this quickstart’ssrc/main/data directory
to the newly createdwork/jms/input
directory. Wait a few moments and you will find the same files organized by country under the
work/jms/output
directory:order1.xml, order2.xml and order4.xml in work/jms/output/others order3.xml and order5.xml in work/jms/output/us order6.xml in work/jms/output/fr
Use
log:display
to check out the business logging:Receiving order order1.xml Sending order order1.xml to another country Done processing order1.xml
7.2.4. Summary of handled properties
Properties from the Configuration Admin factory PID are passed to the relevant org.ops4j.pax.jms.service.ConnectionFactoryFactory
implementation.
ActiveMQ
org.ops4j.pax.jms.activemq.ActiveMQConnectionFactoryFactory
(JMS 1.1 only)Properties that are passed to the
org.apache.activemq.ActiveMQConnectionFactory.buildFromMap()
methodArtemis
org.ops4j.pax.jms.artemis.ArtemisConnectionFactoryFactory
If
protocol=amqp
, properties are passed to theorg.apache.qpid.jms.util.PropertyUtil.setProperties()
method to configure theorg.apache.qpid.jms.JmsConnectionFactory
instance.Otherwise,
org.apache.activemq.artemis.utils.uri.BeanSupport.setData()
is called for theorg.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory
instance.IBM MQ
org.ops4j.pax.jms.ibmmq.MQConnectionFactoryFactory
Bean properties of
com.ibm.mq.jms.MQConnectionFactory
orcom.ibm.mq.jms.MQXAConnectionFactory
are handled.
7.3. Using JMS console commands
Apache Karaf provides the jms
feature, which includes shell commands in the jms:*
scope. You already saw some examples of using these commands to check the manually configured connection factories. There are also commands that hide the need to create Configuration Admin configurations.
Starting with a fresh instance of Fuse, you can register a broker-specific connection factory. The following listing shows install of the jms
feature from Karaf and installation of pax-jms-artemis
from pax-jms
:
karaf@root()> feature:install jms pax-jms-artemis karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── karaf@root()> service:list javax.jms.ConnectionFactory # should be empty karaf@root()> service:list org.ops4j.pax.jms.service.ConnectionFactoryFactory [org.ops4j.pax.jms.service.ConnectionFactoryFactory] ---------------------------------------------------- service.bundleid = 250 service.id = 326 service.scope = singleton type = artemis Provided by : OPS4J Pax JMS Artemis Support (250)
The following listing shows how to create and check an Artemis connection factory:
karaf@root()> jms:create -t artemis -u admin -p admin --url tcp://localhost:61616 artemis karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/artemis karaf@root()> jms:info -u admin -p admin jms/artemis Property │ Value ─────────┼────────────────────────── product │ ActiveMQ version │ 2.4.0.amq-711002-redhat-1 karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis" karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1 Message ID │ Content │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp ────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼────────────────────────────┼────────────┼──────────┼─────────────┼─────────┼────────────────────────────── ID:7a944470-574f-11e8-918e-7ee9ecc029d4 │ Hello Artemis │ UTF-8 │ │ │ Persistent │ ActiveMQQueue[DEV.QUEUE.1] │ Never │ 4 │ false │ │ Mon May 14 10:19:10 CEST 2018 karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)' ---------------------------------------------------------------- Pid: org.ops4j.connectionfactory.9184db6f-cb5f-4fd7-b5d7-a217090473ad FactoryPid: org.ops4j.connectionfactory BundleLocation: mvn:org.ops4j.pax.jms/pax-jms-config/1.0.0 Properties: name = artemis osgi.jndi.service.name = jms/artemis password = admin service.factoryPid = org.ops4j.connectionfactory service.pid = org.ops4j.connectionfactory.9184db6f-cb5f-4fd7-b5d7-a217090473ad type = artemis url = tcp://localhost:61616 user = admin
As you can see, the org.ops4j.connectionfactory
factory PID is created for you. However it is not automatically stored in ${karaf.etc}
, which is possible with config:update
. It is not possible to specify other properties, but you can add them later.
7.4. Using encrypted configuration values
As with the pax-jdbc-config
bundle, you can use Jasypt to encrypt properties.
If there is any org.jasypt.encryption.StringEncryptor
service that is registered in OSGi with any alias
service property, you can reference it in a connection factory factory PID and use encrypted passwords. Following is an example:
felix.fileinstall.filename = */etc/org.ops4j.connectionfactory-artemis.cfg name = artemis type = artemis decryptor = my-jasypt-decryptor url = tcp://localhost:61616 user = fuse password = ENC(<encrypted-password>)
The service filter used to find the decryptor service is (&(objectClass=org.jasypt.encryption.StringEncryptor)(alias=<alias>))
, where <alias>
is the value of the decryptor
property from the connection factory configuration factory PID.
7.5. Using JMS connection pools
This section discusses JMS connection/session pooling options. There are fewer choices than there are for JDBC. The information is organized into the following topics:
To use XA recovery, you should use the pax-jms-pool-transx
or pax-jms-pool-narayana
connection pool module.
7.5.1. Introduction to using JMS connection pools
So far, you have registered a broker-specific connection factory. Because a connection factory itself is a factory for connection factories, the org.ops4j.pax.jms.service.ConnectionFactoryFactory
service may be treated as a meta factory. It should be able to produce two kinds of connection factories:
-
javax.jms.ConnectionFactory
-
javax.jms.XAConnectionFactory
The pax-jms-pool-*
bundles work smoothly with the org.ops4j.pax.jms.service.ConnectionFactoryFactory
service. These bundles provide implementations of org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
that can be used to create pooled connection factories by using a set of properties and the original org.ops4j.pax.jms.service.ConnectionFactoryFactory
in a kind of wrapper way. For example:
public interface PooledConnectionFactoryFactory { ConnectionFactory create(ConnectionFactoryFactory cff, Map<String, Object> props); }
The following table shows which bundles register pooled connection factory factories. In the table, o.o.p.j.p
represents org.ops4j.pax.jms.pool
.
Bundle | PooledConnectionFactoryFactory | Pool Key |
---|---|---|
|
|
|
|
|
|
|
|
|
The pax-jms-pool-narayana
factory is called PooledJms(XA)PooledConnectionFactoryFactory
because it is based on the pooled-jms
library. It adds integration with the Narayana transaction manager for XA recovery.
The above bundles install only connection factory factories. The bundles to not install the connection factories themselves. Consequently, something is needed that calls the javax.jms.ConnectionFactory org.ops4j.pax.jms.service.PooledConnectionFactoryFactory.create()
method.
7.5.2. Using the pax-jms-pool-pooledjms connection pool module
An understanding of how to use the pax-jms-pool-pooledjms
bundle helps you use not only the pax-jms-pool-pooledjms
bundle, but also the pax-jms-pool-narayna
bundle, which does almost everything as pax-jms-pool-pooledjms
.
The pax-jms-config bundle tracks the following:
-
org.ops4j.pax.jms.service.ConnectionFactoryFactory
services -
org.ops4j.connectionfactory
factory PIDs -
Instances of
org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
that are registered by one ofpax-jms-pool-*
bundles.
If a factory configuration contains a pool
property, the ultimate connection factory registered by the pax-jms-config
bundle is the broker-specific connection factory. If pool=pooledjms
then the connection factory is wrapped inside one of the following:
-
org.messaginghub.pooled.jms.JmsPoolConnectionFactory
(xa=false
) -
org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory
(xa=true
)
Besides the pool
property (and the Boolean xa
property, which selects one of non-xa/xa connection factories), the org.ops4j.connectionfactory
factory PID may contain properties that are prefixed with pool.
.
For the pooled-jms
library, these prefixed properties are used (after removing the prefix) to configure an instance of:
-
org.messaginghub.pooled.jms.JmsPoolConnectionFactory
, or -
org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory
The following listing is a realistic configuration of a pooled-jms
pool (org.ops4j.connectionfactory-artemis
factory PID) that is using a convenient syntax with jms.
-prefixed properties:
# configuration for pax-jms-config to choose and configure specific org.ops4j.pax.jms.service.ConnectionFactoryFactory name = jms/artemis connectionFactoryType = ConnectionFactory jms.url = tcp://localhost:61616 jms.user = fuse jms.password = fuse # org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory specific coniguration jms.callTimeout = 12000 # ... # hints for pax-jms-config to use selected org.ops4j.pax.jms.service.PooledConnectionFactoryFactory pool = pooledjms xa = false # pooled-jms specific configuration of org.messaginghub.pooled.jms.JmsPoolConnectionFactory pool.idleTimeout = 10 pool.maxConnections = 100 pool.blockIfSessionPoolIsFull = true # ...
In the above configuration, pool
and xa
keys are hints (service filter properties) to choose one of the registered org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
services. In the case of the pooled-jms
library it is:
karaf@root()> feature:install pax-jms-pool-pooledjms karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-pool-pooledjms OPS4J Pax JMS MessagingHub JMS Pool implementation (252) provides: ------------------------------------------------------------------ objectClass = [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory] pool = pooledjms service.bundleid = 252 service.id = 331 service.scope = singleton xa = false ----- objectClass = [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory] pool = pooledjms service.bundleid = 252 service.id = 335 service.scope = singleton xa = true
Following is a complete example of the steps for creating and configuring a connection pool:
Install the required features:
karaf@root()> feature:install -v pax-jms-pool-pooledjms pax-jms-artemis Adding features: pax-jms-pool-pooledjms/[1.0.0,1.0.0] ...
Install the
jms
feature:karaf@root()> feature:install jms karaf@root()> service:list org.ops4j.pax.jms.service.ConnectionFactoryFactory [org.ops4j.pax.jms.service.ConnectionFactoryFactory] ---------------------------------------------------- service.bundleid = 249 service.id = 327 service.scope = singleton type = artemis Provided by : OPS4J Pax JMS Artemis Support (249) karaf@root()> service:list org.ops4j.pax.jms.service.PooledConnectionFactoryFactory [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory] ---------------------------------------------------------- pool = pooledjms service.bundleid = 251 service.id = 328 service.scope = singleton xa = false Provided by : OPS4J Pax JMS MessagingHub JMS Pool implementation (251) [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory] ---------------------------------------------------------- pool = pooledjms service.bundleid = 251 service.id = 333 service.scope = singleton xa = true Provided by : OPS4J Pax JMS MessagingHub JMS Pool implementation (251)
Create a factory configuration:
karaf@root()> config:edit --factory --alias artemis org.ops4j.connectionfactory karaf@root()> config:property-set connectionFactoryType ConnectionFactory karaf@root()> config:property-set osgi.jndi.service.name jms/artemis karaf@root()> config:property-set type artemis karaf@root()> config:property-set protocol amqp # so we switch to org.apache.qpid.jms.JmsConnectionFactory karaf@root()> config:property-set jms.url amqp://localhost:61616 karaf@root()> config:property-set jms.username admin karaf@root()> config:property-set jms.password admin karaf@root()> config:property-set pool pooledjms karaf@root()> config:property-set xa false karaf@root()> config:property-set pool.idleTimeout 10 karaf@root()> config:property-set pool.maxConnections 123 karaf@root()> config:property-set pool.blockIfSessionPoolIsFull true karaf@root()> config:update
Check if
pax-jms-config
processed the configuration intojavax.jms.ConnectionFactory
service:karaf@root()> service:list javax.jms.ConnectionFactory [javax.jms.ConnectionFactory] ----------------------------- connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg jms.password = admin jms.url = amqp://localhost:61616 jms.username = admin osgi.jndi.service.name = jms/artemis pax.jms.managed = true pool.blockIfSessionPoolIsFull = true pool.idleTimeout = 10 pool.maxConnections = 123 protocol = amqp service.bundleid = 250 service.factoryPid = org.ops4j.connectionfactory service.id = 347 service.pid = org.ops4j.connectionfactory.fc1b9e85-91b4-421b-aa16-1151b0f836f9 service.scope = singleton type = artemis Provided by : OPS4J Pax JMS Config (250)
Use the connection factory:
karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/artemis karaf@root()> jms:info -u admin -p admin jms/artemis Property │ Value ─────────┼──────────────── product │ QpidJMS version │ 0.30.0.redhat-1 karaf@root()> jms:send -u admin -p admin jms/artemis DEV.QUEUE.1 "Hello Artemis" karaf@root()> jms:browse -u admin -p admin jms/artemis DEV.QUEUE.1 Message ID │ Content │ Charset │ Type │ Correlation ID │ Delivery Mode │ Destination │ Expiration │ Priority │ Redelivered │ ReplyTo │ Timestamp ────────────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼─────────────┼────────────┼──────────┼─────────────┼─────────┼────────────────────────────── ID:64842f99-5cb2-4850-9e88-f50506d49d20:1:1:1-1 │ Hello Artemis │ UTF-8 │ │ │ Persistent │ DEV.QUEUE.1 │ Never │ 4 │ false │ │ Mon May 14 12:47:13 CEST 2018
7.5.3. Using the pax-jms-pool-narayana connection pool module
The pax-jms-pool-narayna
module does almost everything as pax-jms-pool-pooledjms
. It installs the pooled-jms-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
, both for XA and non-XA scenarios. The only difference is that in an XA scenario, there is an additional integration point. The org.jboss.tm.XAResourceRecovery
OSGi service is registered to be picked up by com.arjuna.ats.arjuna.recovery.RecoveryManager
.
7.5.4. Using the pax-jms-pool-transx connection pool module
The pax-jms-pool-transx
module provides an implementation of org.ops4j.pax.jms.service.PooledConnectionFactoryFactory
services that is based on the pax-transx-jms
bundle. The pax-transx-jms
bundle creates javax.jms.ConnectionFactory
pools by using the org.ops4j.pax.transx.jms.ManagedConnectionFactoryBuilder
facility. This is a JCA (Java™ Connector Architecture) solution that is discussed in Section 8.3, “About the pax-transx project”.
7.6. Deploying connection factories as artifacts
This topic discusses real-world recommendations.
In the deployment method, javax.jms.ConnectionFactory
services are registered directly by application code. Usually, this code is inside a Blueprint container. Blueprint XML may be part of an ordinary OSGi bundle, installable by using mvn:
URI, and stored in a Maven repository (local or remote). It is easier to version-control such bundles as compared to Configuration Admin configurations.
The pax-jms-config
version 1.0.0 bundle adds a deployment method for connection factory configuration. An application developer registers the javax.jms.(XA)ConnectionFactory
service (usually by using Bluerpint XML) and specifies service properties. Then pax-jms-config
detects the registered, broker-specific connection factory and (using service properties) wraps the service inside a generic, non broker-specific, connection pool.
Following are three deployment methods that use Blueprint XML.
7.6.1. Manual deployment of connection factories
In this method, the pax-jms-config
bundle is not needed. Application code is responsible for registration of both broker-specific and generic connection pools.
<!-- Broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory --> <bean id="artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory"> <argument value="tcp://localhost:61616" /> <property name="callTimeout" value="2000" /> <property name="initialConnectAttempts" value="3" /> </bean> <!-- Fuse exports this service from fuse-pax-transx-tm-narayana bundle. --> <reference id="tm" interface="javax.transaction.TransactionManager" /> <!-- Non broker-specific, generic, pooling, enlisting javax.jms.ConnectionFactory --> <bean id="pool" class="org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory"> <property name="connectionFactory" ref="artemis" /> <property name="transactionManager" ref="tm" /> <property name="maxConnections" value="10" /> <property name="idleTimeout" value="10000" /> </bean> <!-- Expose connection factory for use by application code (such as Camel, Spring, ...) --> <service interface="javax.jms.ConnectionFactory" ref="pool"> <service-properties> <!-- Giving connection factory a name using one of these properties makes identification easier in jms:connectionfactories: --> <entry key="osgi.jndi.service.name" value="jms/artemis" /> <!--<entry key="name" value="jms/artemis" />--> <!-- Without any of the above, name will fall back to "service.id" --> </service-properties> </service>
Here are the shell commands that show how it should be used:
karaf@root()> feature:install artemis-core-client artemis-jms-client karaf@root()> install -s mvn:org.apache.commons/commons-pool2/2.5.0 Bundle ID: 244 karaf@root()> install -s mvn:org.messaginghub/pooled-jms/0.3.0 Bundle ID: 245 karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-manual.xml Bundle ID: 246 karaf@root()> bundle:services -p 246 Bundle 246 provides: -------------------- objectClass = [javax.jms.ConnectionFactory] osgi.jndi.service.name = jms/artemis osgi.service.blueprint.compname = pool service.bundleid = 246 service.id = 340 service.scope = bundle ----- objectClass = [org.osgi.service.blueprint.container.BlueprintContainer] osgi.blueprint.container.symbolicname = artemis-manual.xml osgi.blueprint.container.version = 0.0.0 service.bundleid = 246 service.id = 341 service.scope = singleton karaf@root()> feature:install jms karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/artemis karaf@root()> jms:info -u admin -p admin jms/artemis Property │ Value ─────────┼────────────────────────── product │ ActiveMQ version │ 2.4.0.amq-711002-redhat-1
As shown in the above listing, the Blueprint bundle exports the javax.jms.ConnectionFactory
service, which is a generic, non broker-specific, connection pool. The broker-specific javax.jms.XAConnectionFactory
is not registered as an OSGi service, because Blueprint XML does not have an explicit <service ref="artemis">
declaration.
7.6.2. Factory deployment of connection factories
This method shows the use of pax-jms-config
in a canonical way. This is a bit different than the method that was recommended for Fuse 6.x, where the requirement was to specify pooling configuration as service properties.
Here is the Blueprint XML example:
<!-- A broker-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory that can create (XA)ConnectionFactory using properties. It is registered by pax-jms-* bundles --> <reference id="connectionFactoryFactory" interface="org.ops4j.pax.jms.service.ConnectionFactoryFactory" filter="(type=artemis)" /> <!-- Non broker-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory that can create pooled connection factories with the help of org.ops4j.pax.jms.service.ConnectionFactoryFactory For example, pax-jms-pool-pooledjms bundle registers org.ops4j.pax.jms.service.PooledConnectionFactoryFactory with these properties: - pool = pooledjms - xa = true|false (both are registered) --> <reference id="pooledConnectionFactoryFactory" interface="org.ops4j.pax.jms.service.PooledConnectionFactoryFactory" filter="(&(pool=pooledjms)(xa=true))" /> <!-- When using XA connection factories, javax.transaction.TransactionManager service is not needed here. It is used internally by xa-aware pooledConnectionFactoryFactory. --> <!--<reference id="tm" interface="javax.transaction.TransactionManager" />--> <!-- Finally, use both factories to expose the pooled, xa-aware, connection factory. --> <bean id="pool" factory-ref="pooledConnectionFactoryFactory" factory-method="create"> <argument ref="connectionFactoryFactory" /> <argument> <props> <!-- Properties needed by artemis-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory --> <prop key="jms.url" value="tcp://localhost:61616" /> <prop key="jms.callTimeout" value="2000" /> <prop key="jms.initialConnectAttempts" value="3" /> <!-- Properties needed by pooled-jms-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory --> <prop key="pool.maxConnections" value="10" /> <prop key="pool.idleTimeout" value="10000" /> </props> </argument> </bean> <!-- Expose connection factory for use by application code (such as Camel, Spring, ...) --> <service interface="javax.jms.ConnectionFactory" ref="pool"> <service-properties> <!-- Giving connection factory a name using one of these properties makes identification easier in jms:connectionfactories: --> <entry key="osgi.jndi.service.name" value="jms/artemis" /> <!--<entry key="name" value="jms/artemis" />--> <!-- Without any of the above, name will fall back to "service.id" --> </service-properties> </service>
The previous example uses factory beans that create connection factories by using connection factory factories (…). There is no need for an explicit reference to the javax.transaction.TransactionManager
service, as this is tracked internally by the XA-aware PooledConnectionFactoryFactory
.
Here is how it looks in a Fuse/Karaf shell:
karaf@root()> feature:install jms pax-jms-artemis pax-jms-pool-pooledjms karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-pax-jms-factory-pooledjms.xml Bundle ID: 253 karaf@root()> bundle:services -p 253 Bundle 253 provides: -------------------- objectClass = [javax.jms.ConnectionFactory] osgi.jndi.service.name = jms/artemis osgi.service.blueprint.compname = pool service.bundleid = 253 service.id = 347 service.scope = bundle ----- objectClass = [org.osgi.service.blueprint.container.BlueprintContainer] osgi.blueprint.container.symbolicname = artemis-pax-jms-factory-pooledjms.xml osgi.blueprint.container.version = 0.0.0 service.bundleid = 253 service.id = 348 service.scope = singleton karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/artemis karaf@root()> jms:info -u admin -p admin jms/artemis Property │ Value ─────────┼────────────────────────── product │ ActiveMQ version │ 2.4.0.amq-711002-redhat-1
As shown in the above listing, the Blueprint bundle exports the javax.jms.ConnectionFactory
service, which is a generic, non broker-specific, connection pool. The broker-specific javax.jms.XAConnectionFactory
is not registered as an OSGi service because Blueprint XML does not have an explicit <service ref="artemis">
declaration.
7.6.3. Mixed deployment of connection factories
The pax-jms-config
1.0.0 bundle adds another way of wrapping broker-specific connection factories within pooling connection factories by using service properties. This method matches the way it used to work in Fuse 6.x.
Here is the Blueprint XML example:
<!-- Broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory --> <bean id="artemis" class="org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory"> <argument value="tcp://localhost:61616" /> <property name="callTimeout" value="2000" /> <property name="initialConnectAttempts" value="3" /> </bean> <!-- Expose broker-specific connection factory with service properties. No need to expose pooling, enlisting, non broker-specific javax.jms.XAConnectionFactory. It will be registered automatically by pax-jms-config with the same properties as this <service>, but with a higher service.ranking --> <service id="pool" ref="artemis" interface="javax.jms.XAConnectionFactory"> <service-properties> <!-- "pool" key is needed for pax-jms-config to wrap broker-specific connection factory inside connection pool --> <entry key="pool" value="pooledjms" /> <!-- <service>/@id attribute does not propagate, but name of the connection factory is required using one of: --> <entry key="osgi.jndi.service.name" value="jms/artemis" /> <!-- or: --> <!--<entry key="name" value="jms/artemis" />--> <!-- Other properties, that normally by e.g., pax-jms-pool-pooledjms --> <entry key="pool.maxConnections" value="10" /> <entry key="pool.idleTimeout" value="10000" /> </service-properties> </service>
In the above example, you can see the manual register of only the broker-specific connection factory. The pool=pooledjms
service property is a hint for the connection factory tracker that is managed by the pax-jms-config
bundle. Connection factory services with this service property are wrapped within a pooling connection factory, in this example, pax-jms-pool-pooledjms
.
Here is how it looks in a Fuse/Karaf shell:
karaf@root()> feature:install jms pax-jms-config pax-jms-artemis pax-jms-pool-pooledjms karaf@root()> install -s blueprint:file://$PQ_HOME/message-brokers/blueprints/artemis-pax-jms-discovery.xml Bundle ID: 254 karaf@root()> bundle:services -p 254 Bundle 254 provides: -------------------- objectClass = [javax.jms.XAConnectionFactory] osgi.jndi.service.name = jms/artemis osgi.service.blueprint.compname = artemis pool = pooledjms pool.idleTimeout = 10000 pool.maxConnections = 10 service.bundleid = 254 service.id = 349 service.scope = bundle ----- objectClass = [org.osgi.service.blueprint.container.BlueprintContainer] osgi.blueprint.container.symbolicname = artemis-pax-jms-discovery.xml osgi.blueprint.container.version = 0.0.0 service.bundleid = 254 service.id = 351 service.scope = singleton karaf@root()> service:list javax.jms.XAConnectionFactory [javax.jms.XAConnectionFactory] ------------------------------- osgi.jndi.service.name = jms/artemis osgi.service.blueprint.compname = artemis pool = pooledjms pool.idleTimeout = 10000 pool.maxConnections = 10 service.bundleid = 254 service.id = 349 service.scope = bundle Provided by : Bundle 254 Used by: OPS4J Pax JMS Config (251) karaf@root()> service:list javax.jms.ConnectionFactory [javax.jms.ConnectionFactory] ----------------------------- osgi.jndi.service.name = jms/artemis osgi.service.blueprint.compname = artemis pax.jms.managed = true pax.jms.service.id.ref = 349 pool.idleTimeout = 10000 pool.maxConnections = 10 service.bundleid = 251 service.id = 350 service.ranking = 1000 service.scope = singleton Provided by : OPS4J Pax JMS Config (251) karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/artemis karaf@root()> jms:info -u admin -p admin jms/artemis Property │ Value ─────────┼────────────────────────── product │ ActiveMQ version │ 2.4.0.amq-711002-redhat-1
In the previous example, jms:connectionfactories
shows only one service, because this command removes duplicate names. Two services were presented by jdbc:ds-list
in the mixed deployment of data sources.
javax.jms.XAConnectionFactory
is registered from the Blueprint bundle and it has the pool = pooledjms
property declared.
javax.jms.ConnectionFactory
is registered from the pax-jms-config
bundle and:
-
It does not have the
pool = pooledjms
property. It was removed when registering the wrapper connection factory. -
It has the
service.ranking = 1000
property, so it is always the preferred version when, for example, looking for a connection factory by name. -
It has the
pax.jms.managed = true
property, so it is not tried to be wrapped again. -
It has the
pax.jms.service.id.ref = 349
property, which indicates the original connection factory service that is wrapped inside the connection pool.