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 ofjavax.sql.DataSource
,javax.sql.XADataSource
,javax.sql.ConnectionPoolDataSource
andjava.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 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
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
andjavax.jms.XAConnectionFactory
withpool=<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 toorg.apache.qpid.jms.util.PropertyUtil.setProperties()
method to configureorg.apache.qpid.jms.JmsConnectionFactory
instance - otherwise,
org.apache.activemq.artemis.utils.uri.BeanSupport.setData()
is called fororg.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
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
, ororg.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 bycom.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