Java™ Message Service
Connections
While JDBC uses simple factory → connection pattern (even if there are three kinds of factories), JMS defines more concepts (consumers, producers, sessions, connections, destinations, contexts (JMS 2.0)). We’ll however focus on connections/contexts and the factories that may be used to obtain them.
The factory
There are 6 factory interfaces:
generic
javax.jms.ConnectionFactoryqueue-specific
javax.jms.QueueConnectionFactorytopic-specific
javax.jms.TopicConnectionFactoryXA
javax.jms.XAConnectionFactoryqueue-specific
javax.jms.XAQueueConnectionFactorytopic-specific
javax.jms.XATopicConnectionFactory
The libraries that contain the objects I’m going to describe are:
ActiveMQ 5:
mvn:org.apache.activemq/activemq-client/<version>ActiveMQ 6 (Artemis):
mvn:org.apache.activemq/artemis-jms-client/<version>ormvn:org.apache.qpid/qpid-jms-client/<version>IBM MQ 9: download
9.0.5.0-IBM-MQ-Install-Java-All.jarstarting from https://developer.ibm.com/messaging/mq-downloads/
If we check existing implementation (contained in driver JARs) we can find:
ActiveMQ 5:
org.apache.activemq.ActiveMQConnectionFactoryandorg.apache.activemq.ActiveMQXAConnectionFactoryActiveMQ 6 (Artemis):
org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactoryandorg.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactoryororg.apache.qpid.jms.JmsConnectionFactory(no XA inqpidlibrary)IBM MQ 9:
com.ibm.mq.jms.MQConnectionFactoryandcom.ibm.mq.jms.MQXAConnectionFactory
These implementations contain broker-specific logic to connect to given JMS broker.
In JDBC we can obtain connections directly from driver or using javax.sql.DataSource. With JMS there’s just:
// standard way
javax.jms.Connection connection = javax.jms.ConnectionFactory.createConnection();
// simplified way (JMS 2.0)
javax.jms.JMSContext jms = javax.jms.ConnectionFactory.createContext()(possibly with credential variants). JMSContext is just Connection + Session and we won’t write about it.
javax.jms.XAConnectionFactory is analogous to javax.sql.XADataSource - it allows to obtain javax.jms.XAConnection objects - most importantly to get javax.jms.XASession and javax.transaction.xa.XAResource in order to enlist the XA resource in active javax.transaction.Transaction object.
Broker specific and generic connection factories
We can separate JMS connection factory implementations into two categories:
broker specific implementations of
javax.jms.ConnectionFactory,javax.jms.XAConnectionFactoryand domain-specific (queue or topic) variantsgeneric
javax.jms.ConnectionFactoryimplementations that provide QoS functionality like connection/session pooling
Differently that with JDBC, in JMS it’s usually not that clear if pooling should be performed by non broker-specific connection factory.
Here are two generic connection factories:
org.springframework.jms.connection.CachingConnectionFactory(only JMS 1.1)org.messaginghub.pooled.jms.JmsPoolConnectionFactory(with XA and JCA versions, works with JMS 2.0)
As with JDBC, generic connection factory should be configured with broker-specific connection factory. It’s even easier, because there’s no driver manager way of obtaining connections, so there’s always a need to use broker-specific connection factory.
Note
org.messaginghub.pooled.jms.JmsPoolConnectionFactory uses DBCP2 internally
Code example
The recommended code pattern is:
create/obtain broker-specific
javax.jms.ConnectionFactoryorjavax.jms.XAConnectionFactoryinstance with broker-specific configuration (URL, credentials, …) that knows how to create connections/XA connections,create/obtain non broker-specific
javax.jms.ConnectionFactoryinstance (internally configured with the above, broker-specific connection factory) with non broker-specific configuration (connection pooling, transaction manager, …),use
javax.jms.ConnectionFactoryto getjavax.jms.Connectionand perform JMS operations.
Here’s a canonical example:
// broker-specific, non-pooling, non-enlisting javax.jms.XAConnectionFactory
ActiveMQXAConnectionFactory brokerCF
= new org.apache.activemq.artemis.jms.client.ActiveMQXAConnectionFactory("tcp://localhost:61616");
// broker-specific configuration
brokerCF.setCallTimeout(2000);
brokerCF.setInitialConnectAttempts(3);
// ...
// non broker-specific, pooling, enlisting javax.jms.ConnectionFactory
JmsPoolXAConnectionFactory pool = new org.messaginghub.pooled.jms.JmsPoolXAConnectionFactory();
// delegate to broker-specific XAConnectionFactory
pool.setConnectionFactory(brokerCF);
// delegate to JTA transaction manager
pool.setTransactionManager(transactionManager);
// non broker-specific configuration
pool.setMaxConnections(10);
pool.setIdleTimeout(10000);
// ...
// JMS code
javax.jms.ConnectionFactory jmsCF = pool;
userTransaction.begin();
try (Connection c = jmsCF.createConnection("username", "passw0rd")) {
c.start();
try (Session session = c.createSession(false, Session.SESSION_TRANSACTED)) {
ActiveMQQueue brokerQueue = new ActiveMQQueue("DEV.QUEUE.1");
Queue jmsQueue = brokerQueue;
try (MessageProducer producer = session.createProducer(jmsQueue)) {
TextMessage message = session.createTextMessage("Hello Artemis!");
producer.send(message);
}
}
}
userTransaction.commit();