Configuration

Configuration

Application code - whether it's using Blueprint, SCR or any other OSGi techniques - should not create instances of javax.jms.ConnectionFactory directly. The code should find the connection factories registered in OSGi service registry. The connection factories are registered using:

org.osgi.framework.BundleContext.registerService(javax.jms.ConnectionFactory.class, connectionFactoryObject, properties); org.osgi.framework.BundleContext.registerService(javax.jms.XAConnectionFactory.class, xaConnectionFactoryObject, properties);

And PAX-JMS helps with the registration.

There are generally two methods to register connection factories:

  • configuration method, where the connection factories are created on the basis of Configuration Admin configurations

  • deployment method, where the connection factories are registered directly - including the convenient method, where the application registers broker-specific, non-pooling, non-enlisting connection factory, while PAX-JMS wraps it with generic, pooling and enlisting connection factory

OSGi JMS Service

First, an analogy to JDBC.

OSGi way of handling JDBC data sources is related to two interfaces:

  • standard org.osgi.service.jdbc.DataSourceFactory (see Chapter 125 of OSGi Enterprise R6 specification) that's used to create instances of javax.sql.DataSource, javax.sql.XADataSource, javax.sql.ConnectionPoolDataSource and java.sql.Driver

  • proprietary PAX JDBC org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory that creates pooling data sources

For JMS we have these analogous interfaces:

  • proprietary org.ops4j.pax.jms.service.ConnectionFactoryFactory with the same purpose as standard OSGi JDBC org.osgi.service.jdbc.DataSourceFactory

  • proprietary org.ops4j.pax.jms.service.PooledConnectionFactoryFactory with the same purpose as proprietary PAX JDBC org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory

Dedicated, broker-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory implementations

There are bundles like:

  • mvn:org.ops4j.pax.jms/pax-jms-activemq/1.0.0

  • mvn:org.ops4j.pax.jms/pax-jms-artemis/1.0.0

  • mvn:org.ops4j.pax.jms/pax-jms-ibmmq/1.0.0

that register broker-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory services that can return JMS factories (javax.jms.ConnectionFactory and javax.jms.XAConnectionFactory). For example:

karaf@root()> feature:repo-add mvn:org.ops4j.pax.jms/pax-jms-features/1.0.0/xml/features Adding feature url mvn:org.ops4j.pax.jms/pax-jms-features/1.0.0/xml/features karaf@root()> feature:repo-add mvn:org.apache.activemq/artemis-features/2.5.0/xml/features Adding feature url mvn:org.apache.activemq/artemis-features/2.5.0/xml/features karaf@root()> feature:install pax-jms-config pax-jms-artemis karaf@root()> bundle:services -p org.ops4j.pax.jms.pax-jms-config OPS4J Pax JMS Config (72) provides: ----------------------------------- objectClass = [org.osgi.service.cm.ManagedServiceFactory] service.bundleid = 72 service.id = 88 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 (71) provides: -------------------------------------------- objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory] service.bundleid = 71 service.id = 86 service.scope = singleton type = artemis

pax-jms-config bundle registers managed service factory for org.ops4j.connectionfactory factory PIDs (described later).

org.ops4j.pax.jms.pax-jms-artemis registers org.ops4j.pax.jms.service.ConnectionFactoryFactory with type=artemis.

PAX-JMS configuration service

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 in order to invoke its methods:

public ConnectionFactory createConnectionFactory(Map<String, Object> properties); public XAConnectionFactory createXAConnectionFactory(Map<String, Object> properties);
  • tracks org.ops4j.connectionfactory factory PIDs in order to collect properties required by the above methods. If we create a factory configuration using any method available for Configuration Admin service, for example by creating ${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg file, we can perform the final step to expose actual broker-specific connection factory (the configuration method).

  • tracks instances of javax.jms.ConnectionFactory and javax.jms.XAConnectionFactory with pool=<pool-name> OSGi service property - to wrap broker-specific connection factories inside pooling connection factories (the deployment method)

Here’s the detailed, canonical step-by-step guide to register Artemis connection factory using configuration method:

1.Β Install Artemis features and PAX JMS features:

karaf@root()> feature:repo-add mvn:org.ops4j.pax.jms/pax-jms-features/1.0.0/xml/features Adding feature url mvn:org.ops4j.pax.jms/pax-jms-features/1.0.0/xml/features karaf@root()> feature:repo-add mvn:org.apache.activemq/artemis-features/2.5.0/xml/features Adding feature url mvn:org.apache.activemq/artemis-features/2.5.0/xml/features karaf@root()> feature:install pax-jms-config pax-jms-artemis

2. Create 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 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 pax karaf@root()> config:property-set jms.password pax karaf@root()> config:property-set jms.prefetchPolicy.queuePrefetch 1234 karaf@root()> config:update karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)' ---------------------------------------------------------------- Pid: org.ops4j.connectionfactory.f5ab1f99-cbfe-48fa-8b34-4e85084f82a8 FactoryPid: org.ops4j.connectionfactory BundleLocation: ? Properties: connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:/data/servers/apache-karaf-4.2.0/etc/org.ops4j.connectionfactory-5144692137911418633.cfg jms.password = pax jms.prefetchPolicy.queuePrefetch = 1234 jms.url = tcp://localhost:61616 jms.user = pax osgi.jndi.service.name = artemis service.factoryPid = org.ops4j.connectionfactory service.pid = org.ops4j.connectionfactory.f5ab1f99-cbfe-48fa-8b34-4e85084f82a8 type = artemis

3. Check if pax-jms-config processed the configuration into javax.jms.ConnectionFactory service:

karaf@root()> service:list javax.jms.ConnectionFactory [javax.jms.ConnectionFactory] ----------------------------- connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:/data/servers/apache-karaf-4.2.0/etc/org.ops4j.connectionfactory-5144692137911418633.cfg jms.password = pax jms.prefetchPolicy.queuePrefetch = 1234 jms.url = tcp://localhost:61616 jms.user = pax osgi.jndi.service.name = artemis pax.jms.managed = true service.bundleid = 72 service.factoryPid = org.ops4j.connectionfactory service.id = 93 service.pid = org.ops4j.connectionfactory.f5ab1f99-cbfe-48fa-8b34-4e85084f82a8 service.scope = singleton type = artemis Provided by : OPS4J Pax JMS Config (72)

Β 

If we specify additional property for Artemis configuration - protocol=amqp, QPID JMS library would be used instead of Artemis JMS client. amqp:// protocol has to be used then for jms.url property.

Now we have actual broker-specific (no pooling yet) connection factory. We can already inject it where needed. For example we can use Karaf commands from jms feature:

karaf@root()> feature:install -v jms Adding features: jms/[4.2.0,4.2.0] ... karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── artemis karaf@root()> jms:info -u pax -p pax artemis Property β”‚ Value ─────────┼───────── product β”‚ ActiveMQ version β”‚ 2.5.0 karaf@root()> jms:send -u pax -p pax artemis DEV.QUEUE.1 "Hello Artemis" karaf@root()> jms:browser -u pax -p pax artemis DEV.QUEUE.1 Message ID β”‚ Content β”‚ Charset β”‚ Type β”‚ Correlation ID β”‚ Delivery Mode β”‚ Destination β”‚ Expiration β”‚ Priority β”‚ Redelivered β”‚ ReplyTo β”‚ Timestamp ────────────────────────────────────────┼───────────────┼─────────┼──────┼────────────────┼───────────────┼────────────────────────────┼────────────┼──────────┼─────────────┼─────────┼────────────────────────────── ID:91ac54e1-5371-11e8-a61b-fe5400008fbb β”‚ Hello Artemis β”‚ UTF-8 β”‚ β”‚ β”‚ Persistent β”‚ ActiveMQQueue[DEV.QUEUE.1] β”‚ Never β”‚ 4 β”‚ false β”‚ β”‚ Wed May 09 12:13:07 CEST 2018

Let’s switch the protocol:

karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)' ---------------------------------------------------------------- Pid: org.ops4j.connectionfactory.f5ab1f99-cbfe-48fa-8b34-4e85084f82a8 FactoryPid: org.ops4j.connectionfactory BundleLocation: ? Properties: connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:/data/servers/apache-karaf-4.2.0/etc/org.ops4j.connectionfactory-5144692137911418633.cfg jms.password = pax jms.prefetchPolicy.queuePrefetch = 1234 jms.url = tcp://localhost:61616 jms.user = pax osgi.jndi.service.name = artemis service.factoryPid = org.ops4j.connectionfactory service.pid = org.ops4j.connectionfactory.f5ab1f99-cbfe-48fa-8b34-4e85084f82a8 type = artemis karaf@root()> config:edit org.ops4j.connectionfactory.f5ab1f99-cbfe-48fa-8b34-4e85084f82a8 karaf@root()> config:property-set protocol amqp karaf@root()> config:property-delete user karaf@root()> config:property-set username pax # 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 pax -p pax artemis Property β”‚ Value ─────────┼──────── product β”‚ QpidJMS version β”‚ 0.30.0 karaf@root()> jms:browse -u pax -p pax 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 β”‚ β”‚ Wed May 09 12:13:07 CEST 2018

If using PAX JMS with Artemis features for embedded broker (not client ones), consider using self implemented feature to avoid classloading problems.

Β 

For completeness, let’s see how we can connect to IBM MQ 9. Even if pax-jms-ibmmq installs relevant PAX JMS bundles, IBM MQ driver is not installed due to licensing reasons.

As mentioned inΒ Message Brokers we can download drivers from IBM MQ page.

karaf@root()> install 'file:///data/downloads/ibm.com/IBM%20MQ/MQSeriesJava-9.0.5-0.x86_64/java/lib/OSGi/com.ibm.msg.client.osgi.commonservices.j2se_9.0.5.0.jar' Bundle ID: 80 karaf@root()> install 'file:///data/downloads/ibm.com/IBM%20MQ/MQSeriesJava-9.0.5-0.x86_64/java/lib/OSGi/com.ibm.msg.client.osgi.jms_9.0.5.0.jar' Bundle ID: 81 karaf@root()> install 'file:///data/downloads/ibm.com/IBM%20MQ/MQSeriesJava-9.0.5-0.x86_64/java/lib/OSGi/com.ibm.msg.client.osgi.nls_9.0.5.0.jar' Bundle ID: 82 karaf@root()> install 'file:///data/downloads/ibm.com/IBM%20MQ/MQSeriesJava-9.0.5-0.x86_64/java/lib/OSGi/com.ibm.msg.client.osgi.wmq.nls_9.0.5.0.jar' # this is fragment bundle Bundle ID: 83 karaf@root()> install 'file:///data/downloads/ibm.com/IBM%20MQ/MQSeriesJava-9.0.5-0.x86_64/java/lib/OSGi/com.ibm.msg.client.osgi.wmq.prereq_9.0.5.0.jar' Bundle ID: 84 karaf@root()> install 'file:///data/downloads/ibm.com/IBM%20MQ/MQSeriesJava-9.0.5-0.x86_64/java/lib/OSGi/com.ibm.msg.client.osgi.wmq_9.0.5.0.jar' Bundle ID: 85 karaf@root()> resolve karaf@root()> start 80 81 82 84 85

These bundles could also be put into custom feature and installed using feature:install command.

And now:

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 (86) provides: ------------------------------------------- objectClass = [org.ops4j.pax.jms.service.ConnectionFactoryFactory] service.bundleid = 86 service.id = 103 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 mq9 karaf@root()> config:property-set connectionFactoryType ConnectionFactory # or XAConnectionFactory karaf@root()> config:property-set jms.queueManager PAXQM 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.109a6ced-1ace-4420-af45-daa886ddfd8f FactoryPid: org.ops4j.connectionfactory BundleLocation: ? Properties: connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:/data/servers/apache-karaf-4.2.0/etc/org.ops4j.connectionfactory-2202048893888780045.cfg jms.CCSID = 1208 jms.channel = DEV.APP.SVRCONN jms.hostName = localhost jms.port = 1414 jms.queueManager = PAXQM jms.transportType = 1 osgi.jndi.service.name = mq9 service.factoryPid = org.ops4j.connectionfactory service.pid = org.ops4j.connectionfactory.109a6ced-1ace-4420-af45-daa886ddfd8f type = ibmmq

Check if pax-jms-config processed the configuration into javax.jms.ConnectionFactory service:

karaf@root()> service:list javax.jms.ConnectionFactory [javax.jms.ConnectionFactory] ----------------------------- connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:/data/servers/apache-karaf-4.2.0/etc/org.ops4j.connectionfactory-2202048893888780045.cfg jms.CCSID = 1208 jms.channel = DEV.APP.SVRCONN jms.hostName = localhost jms.port = 1414 jms.queueManager = PAXQM jms.transportType = 1 osgi.jndi.service.name = mq9 pax.jms.managed = true service.bundleid = 72 service.factoryPid = org.ops4j.connectionfactory service.id = 106 service.pid = org.ops4j.connectionfactory.109a6ced-1ace-4420-af45-daa886ddfd8f service.scope = singleton type = ibmmq Provided by : OPS4J Pax JMS Config (72)

Test the connection:

karaf@root()> feature:install -v jms Adding features: jms/[4.2.0,4.2.0] ... karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── mq9 karaf@root()> jms:info -u app -p pax mq9 Property β”‚ Value ─────────┼──────────────────── product β”‚ IBM MQ JMS Provider version β”‚ 8.0.0.0 karaf@root()> jms:send -u app -p pax mq9 DEV.QUEUE.1 "Hello IBM MQ 9 from PAX-JMS" karaf@root()> jms:browse -u app -p pax mq9 DEV.QUEUE.1 Message ID β”‚ Content β”‚ Charset β”‚ Type β”‚ Correlation ID β”‚ Delivery Mode β”‚ Destination β”‚ Expiration β”‚ Priority β”‚ Redelivered β”‚ ReplyTo β”‚ Timestamp ────────────────────────────────────────────────────┼─────────────────────────────┼─────────┼──────┼────────────────┼───────────────┼──────────────────────┼────────────┼──────────┼─────────────┼─────────┼────────────────────────────── ID:414d5120504158514d2020202020202072cdf25a02006924 β”‚ Hello IBM MQ 9 from PAX-JMS β”‚ UTF-8 β”‚ β”‚ β”‚ Persistent β”‚ queue:///DEV.QUEUE.1 β”‚ Never β”‚ 4 β”‚ false β”‚ β”‚ Wed May 09 12:33:33 CEST 2018

We can check if the message was sent also from IBM MQ Explorer or from IBM MQ Web Console.

Summary of handled properties

Properties from configuration admin factory PID are passed to relevant org.ops4j.pax.jms.service.ConnectionFactoryFactory implementation.

ActiveMQ - org.ops4j.pax.jms.activemq.ActiveMQConnectionFactoryFactory

  • properties passed to org.apache.activemq.ActiveMQConnectionFactory.buildFromMap() method

Artemis - org.ops4j.pax.jms.artemis.ArtemisConnectionFactoryFactory

  • if protocol=amqp, properties are passed to org.apache.qpid.jms.util.PropertyUtil.setProperties() method to configure org.apache.qpid.jms.JmsConnectionFactory instance

  • otherwise, org.apache.activemq.artemis.utils.uri.BeanSupport.setData() is called for org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory instance

IBM MQ - org.ops4j.pax.jms.ibmmq.MQConnectionFactoryFactory

  • bean properties of com.ibm.mq.jms.MQConnectionFactory or com.ibm.mq.jms.MQXAConnectionFactory are handled

The properties that can't be set should be ignored - see

PAXJMS-15 - Getting issue details... STATUS

Using console commands

Apache Karaf provides jms feature that includes shell commands in the jms:* scope. We already tried some of them to check manually configured connection factories, but there are also commands that hide the need to create Configuration Admin configurations.

We could register broker-specific connection factory using (starting with fresh instance of Karaf):

Install jms feature from Karaf and pax-jms-artemis from pax-jms:

karaf@root()> feature:repo-add mvn:org.ops4j.pax.jms/pax-jms-features/1.0.0/xml/features Adding feature url mvn:org.ops4j.pax.jms/pax-jms-features/1.0.0/xml/features karaf@root()> feature:repo-add mvn:org.apache.activemq/artemis-features/2.5.0/xml/features Adding feature url mvn:org.apache.activemq/artemis-features/2.5.0/xml/features 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 = 75 service.id = 94 service.scope = singleton type = artemis Provided by : OPS4J Pax JMS Artemis Support (75)

Create and check Artemis connection factory:

karaf@root()> jms:create -t artemis -u pax -p pax --url tcp://localhost:61616 artemis karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/artemis karaf@root()> jms:info -u pax -p pax jms/artemis Property β”‚ Value ─────────┼───────── product β”‚ ActiveMQ version β”‚ 2.5.0 karaf@root()> config:list '(service.factoryPid=org.ops4j.connectionfactory)' ---------------------------------------------------------------- Pid: org.ops4j.connectionfactory.edd7ad54-0871-4d80-aa2b-bd9758835fb3 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 = pax service.factoryPid = org.ops4j.connectionfactory service.pid = org.ops4j.connectionfactory.edd7ad54-0871-4d80-aa2b-bd9758835fb3 type = artemis url = tcp://localhost:61616 user = pax

Β 

As we can see, org.ops4j.connectionfactory factory PID was created for us. However it’s not automatically stored in ${karaf.etc}, which is possible with config:update. There’s also no way to specify other properties (but we can add them later).

Using properties resolved from external storage

Properties can be stored outside the main configuration file and resolved by a marker placeholder. By default pax-jms does support FILE als build in marker to resolve content from an explicit file. We also support special use cases by implementing generic interfaceΒ org.ops4j.pax.jms.config.ConfigLoader and exporting as a service (e.g. via OSGi DS).

@Component public class CustomConfigLoader implements ConfigLoader { @Override public String getName() { return "CUSTOM"; } @Override public String resolve(String key) { return key; } }

Reserved keywords for name actually are FILE and ENC!

Β 

Usage:

felix.fileinstall.filename = ${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg name = artemis type = artemis url = tcp://localhost:61616 user = pax password = FILE(<filename>)

Using encrypted configuration values

We can use Jasypt to encrypt properties.

If there’s any org.jasypt.encryption.StringEncryptor service registered in OSGi with any alias service property, we can reference it in connection factory factory PID and use encrypted passwords. Here’s an example:

felix.fileinstall.filename = ${karaf.etc}/org.ops4j.connectionfactory-artemis.cfg name = artemis type = artemis decryptor = my-jasypt-decryptor url = tcp://localhost:61616 user = pax password = ENC(<encrypted-password>)

The service filter used to find decryptor service is (&(objectClass=org.jasypt.encryption.StringEncryptor)(alias=<alias>)), where <alias> is the value of decryptor property from connection factory configuration factory PID.

Using connection pools

It’s time to present JMS connection/session pooling options.

In order to use XA recovery, pax-jms-pool-transx or pax-jms-pool-narayana should be used.

So far we’ve registered broker-specific connection factory (because connection factory itself is a factory for connections, org.ops4j.pax.jms.service.ConnectionFactoryFactory may be treated as meta factory) that should be able to produce 2 kinds of connection factories:

  • javax.jms.ConnectionFactory

  • javax.jms.XAConnectionFactory

pax-jms-pool-* bundles work smoothly with the above described org.ops4j.pax.jms.service.ConnectionFactoryFactory services. These bundles provide implementations of org.ops4j.pax.jms.service.PooledConnectionFactoryFactory that can be used to create pooled connection factory using set of properties and original org.ops4j.pax.jms.service.ConnectionFactoryFactory (in a kind of wrapper way).

public interface PooledConnectionFactoryFactory { ConnectionFactory create(ConnectionFactoryFactory cff, Map<String, Object> props); }

Which bundles register pooled connection factory factories (o.o.p.j.p == org.ops4j.pax.jms.pool)?

Bundle

PooledConnectionFactoryFactory

pool key

pax-jms-pool-pooledjms

o.o.p.j.p.pooledjms.PooledJms(XA)PooledConnectionFactoryFactory

pooledjms

pax-jms-pool-narayana

o.o.p.j.p.narayana.PooledJms(XA)PooledConnectionFactoryFactory

narayana

pax-jms-pool-transx

o.o.p.j.p.transx.Transx(XA)PooledConnectionFactoryFactory

transx

pax-jms-pool-narayana factory is called PooledJms(XA)PooledConnectionFactoryFactory because it is based on pooled-jms library - just adding integration with Narayana Transaction Manager in terms of XA Recovery.

The above bundles only install connection factory factories. Not the connection factory themselves. So again we need something that’ll actually call org.ops4j.pax.jms.service.PooledConnectionFactoryFactory.create() method.

pax-jms-pool-pooledjms

We’ll describe the integration with this pool for explanatory purposes. This pool uses https://github.com/messaginghub/pooled-jms project.

pax-jms-config bundle is tracking instances of org.ops4j.pax.jms.service.PooledConnectionFactoryFactory registered by one of pax-jms-pool-* bundles.

If factory configuration contains pool property, the ultimate connection factory registered by pax-jms-config bundle will be the broker-specific connection factory, but wrapped inside one of (if pool=pooledjms):

  • org.messaginghub.pooled.jms.JmsPoolConnectionFactory (xa=false)

  • org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory (xa=true)

Besides pool property (and boolean xa property, which selects one of non-xa/xa connection factories), org.ops4j.connectionfactory factory PID may contain properties prefixed with pool..

For pooled-jms these prefixed properties are used (after removing the prefix) to configure instance of:

  • org.messaginghub.pooled.jms.JmsPoolConnectionFactory, or

  • org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory

Here’s quite realistic configuration of pooled-jms pool (org.ops4j.connectionfactory-artemis factory PID) using convenient syntax with jms.-prefixed properties:

# configuration for pax-jms-config to choose and configure specific org.ops4j.pax.jms.service.ConnectionFactoryFactory name = artemis connectionFactoryType = ConnectionFactory jms.url = tcp://localhost:61616 jms.user = pax jms.password = pax # 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 registered org.ops4j.pax.jms.service.PooledConnectionFactoryFactory services. In case of pooled-jms it’s:

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 (77) provides: ----------------------------------------------------------------- objectClass = [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory] pool = pooledjms service.bundleid = 77 service.id = 87 service.scope = singleton xa = false ---- objectClass = [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory] pool = pooledjms service.bundleid = 77 service.id = 97 service.scope = singleton xa = true

Example

For completeness, here’s full example with connection pool configuration:

Install required features:

karaf@root()> feature:repo-add mvn:org.ops4j.pax.jms/pax-jms-features/1.0.0/xml/features Adding feature url mvn:org.ops4j.pax.jms/pax-jms-features/1.0.0/xml/features karaf@root()> feature:repo-add mvn:org.apache.activemq/artemis-features/2.5.0/xml/features Adding feature url mvn:org.apache.activemq/artemis-features/2.5.0/xml/features karaf@root()> feature:install -v pax-jms-pool-pooledjms pax-jms-artemis Adding features: pax-jms-artemis/[1.0.0,1.0.0],pax-jms-pool-pooledjms/[1.0.0,1.0.0] ...

Install 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 = 73 service.id = 87 service.scope = singleton type = artemis Provided by : OPS4J Pax JMS Artemis Support (73) karaf@root()> service:list org.ops4j.pax.jms.service.PooledConnectionFactoryFactory [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory] ---------------------------------------------------------- pool = pooledjms service.bundleid = 75 service.id = 97 service.scope = singleton xa = false Provided by : OPS4J Pax JMS MessagingHub JMS Pool implementation (75) [org.ops4j.pax.jms.service.PooledConnectionFactoryFactory] ---------------------------------------------------------- pool = pooledjms service.bundleid = 75 service.id = 102 service.scope = singleton xa = true Provided by : OPS4J Pax JMS MessagingHub JMS Pool implementation (75)

Create factory configuration:

karaf@root()> config:edit --factory --alias artemis org.ops4j.connectionfactory karaf@root()> config:property-set connectionFactoryType ConnectionFactory karaf@root()> config:property-set name 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 pax karaf@root()> config:property-set jms.password pax 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 into javax.jms.ConnectionFactory service:

karaf@root()> service:list javax.jms.ConnectionFactory [javax.jms.ConnectionFactory] ----------------------------- connectionFactoryType = ConnectionFactory felix.fileinstall.filename = file:/data/servers/apache-karaf-4.2.0/etc/org.ops4j.connectionfactory-5033358905176596086.cfg jms.password = pax jms.url = amqp://localhost:61616 jms.username = pax name = artemis osgi.jndi.service.name = artemis pax.jms.managed = true pool.blockIfSessionPoolIsFull = true pool.idleTimeout = 10 pool.maxConnections = 123 protocol = amqp service.bundleid = 74 service.factoryPid = org.ops4j.connectionfactory service.id = 104 service.pid = org.ops4j.connectionfactory.732ee8f2-cb21-4e57-b27d-f65d9d0d6296 service.scope = singleton type = artemis Provided by : OPS4J Pax JMS Config (74)

Use the connection factory

karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── artemis karaf@root()> jms:info -u pax -p pax artemis Property β”‚ Value ─────────┼──────── product β”‚ QpidJMS version β”‚ 0.30.0 karaf@root()> jms:send -u pax -p pax artemis DEV.QUEUE.1 "Hello Artemis from PAX-JMS" karaf@root()> jms:browse -u pax -p pax artemis DEV.QUEUE.1 Message ID β”‚ Content β”‚ Charset β”‚ Type β”‚ Correlation ID β”‚ Delivery Mode β”‚ Destination β”‚ Expiration β”‚ Priority β”‚ Redelivered β”‚ ReplyTo β”‚ Timestamp ────────────────────────────────────────────────┼────────────────────────────┼─────────┼──────┼────────────────┼───────────────┼─────────────┼────────────┼──────────┼─────────────┼─────────┼────────────────────────────── ID:39bd5de4-5ee0-45ef-887f-7ff00c53fa6a:2:1:1-1 β”‚ Hello Artemis from PAX-JMS β”‚ UTF-8 β”‚ β”‚ β”‚ Persistent β”‚ DEV.QUEUE.1 β”‚ Never β”‚ 4 β”‚ false β”‚ β”‚ Wed May 09 13:18:27 CEST 2018

Β 

pax-jms-pool-narayana

For clarification, pax-jms-pool-narayna does almost everything as pax-jms-pool-pooledjms - installs pooled-jms-specific org.ops4j.pax.jms.service.PooledConnectionFactoryFactory - both for XA and non-XA scenarios. The only difference is that in XA scenario we have additional integration point:

  • org.jboss.tm.XAResourceRecovery OSGi service is registered to be picked up by com.arjuna.ats.arjuna.recovery.RecoveryManager

pax-jms-pool-transx

The implementation of org.ops4j.pax.jms.service.PooledConnectionFactoryFactory services provided by this bundle is based on pax-transx-jms bundle, which creates javax.jms.ConnectionFactory pools using org.ops4j.pax.transx.jms.ManagedConnectionFactoryBuilder facility. This is JCA (Javaβ„’ Connector Architecture) based solution.

Deploying connection factories as artifacts

In deployment method, javax.jms.ConnectionFactory services are registered directly by application code - usually inside Blueprint container. Blueprint XML may be part of ordinary OSGi bundle, installable using mvn: URI and stored in Maven repository (local or remote). It’s much easier to version-control such bundles comparing to Configuration Admin configurations.

pax-jms-config version 1.0.0 adds a deployment method for connection factory configuration. Application developer registers javax.jms.(XA)ConnectionFactory service (usually using Bluerpint XML) and specifies service properties. Then pax-jms-config detects such registered broker-specific connection factory and (using service properties) wraps the service inside generic, non broker-specific connection pool.

For completeness, I’ll present three deployment methods using Blueprint XML.

Manual deployment of connection factories

In this method, we don’t need pax-jms-config at all. Application code is responsible for registration of both broker-specific and generic connection factory.

<!-- 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" /> <argument value="username" /> <argument value="password" /> <property name="callTimeout" value="2000" /> <property name="initialConnectAttempts" value="3" /> </bean> <!-- Service exported from pax-transx-tm-narayana bundle - or from any bundle that registers JTA Transaction Manager --> <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 to use by application code (like 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 fallback 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 aries-blueprint karaf@root()> install -s mvn:org.apache.commons/commons-pool2/2.5.0 Bundle ID: 69 karaf@root()> install -s mvn:org.messaginghub/pooled-jms/0.3.0 Bundle ID: 70 karaf@root()> install -s blueprint:file:///path/to/artemis-manual.xml Bundle ID: 89 karaf@root()> bundle:services -p 89 Bundle 89 provides: ------------------- objectClass = [javax.jms.ConnectionFactory] osgi.jndi.service.name = jms/artemis osgi.service.blueprint.compname = pool service.bundleid = 89 service.id = 126 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 = 89 service.id = 127 service.scope = singleton karaf@root()> feature:install jms karaf@root()> jms:connectionfactories JMS Connection Factory ────────────────────── jms/artemis karaf@root()> jms:info -u pax -p pax jms/artemis Property β”‚ Value ─────────┼───────── product β”‚ ActiveMQ version β”‚ 2.5.0

As shown in the above listing, blueprint bundle exports javax.jms.ConnectionFactory service which is generic, non broker-specific connection pool. The broker-specific javax.jms.XAConnectionFactory is not registered as OSGi service, because Blueprint XML doesn’t have explicit <service ref="artemis"> declaration.

Factory deployment of connection factories

In this method, we use pax-jms-config in a canonical way - following the way used by PAX JMS itself.

Here’s the Blueprint XML example:

<!-- A broker-specific org.ops4j.pax.jms.service.ConnectionFactoryFactory that can create (XA)ConnectionFactory using properties. It's 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's used internally by xa-aware pooledConnectionFactoryFactory --> <!--<reference id="tm" interface="javax.transaction.TransactionManager" />--> <!-- Finally we can use both factories to expose 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 to use by application code (like 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 fallback to "service.id" --> </service-properties> </service>

In the above example, we use factory beans that create connection factories using connection factory factories (…​). We don’t need explicit reference to javax.transaction.TransactionManager service, as this is tracked internally by XA-aware PooledConnectionFactoryFactory.

Blueprint bundle exports javax.jms.ConnectionFactory service which is generic, non broker-specific connection pool. The broker-specific javax.jms.XAConnectionFactory is not registered as OSGi service, because Blueprint XML doesn’t have explicit <service ref="artemis"> declaration.

Mixed deployment of connection factories

pax-jms-config 1.0.0 adds another way of wrapping broker-specific connection factories within pooling connection factories using service properties.

Here’s 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" /> <argument value="username" /> <argument value="password" /> <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'll be registered automatically by pax-jms-config with the same properties as this <service>, but with 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 doesn't 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, we manually register only broker-specific connection factory. pool=pooledjms service property is a hint for connection factory tracker managed by pax-jms-config bundle. Connection Factory services with this service property will be wrapped within pooling connection factory (in this example - pax-jms-pool-pooledjms).

This time, jms:connectionfactories shows only one service, but that’s becuase this command removes duplicate names.

javax.jms.XAConnectionFactory is registered from the Blueprint bundle and have pool=pooledjms property declared.

javax.jms.ConnectionFactory is registered from pax-jms-config bundle and:

  • doesn’t have pool=pooledjms property (it was removed when registering wrapper connection factory)

  • has service.ranking=1000 property, so it’s always preferred version when e.g., looking for connection factory by name

  • has pax.jms.managed=true property, so it’s not tried to be wrapped again

  • has pax.jms.service.id.ref=<ID> property, so we know what’s the original connection factory service that’s wrapped inside connection pool