Using CachingConnectionFactory with Spring JmsTemplate: What problem does it solve?


From Spring framework API docs for org.springframework.jms.core.JmsTemplate,

“…The ConnectionFactory used with this template should return pooled Connections (or a single shared Connection) as well as pooled Sessions and MessageProducers. Otherwise, performance of ad-hoc JMS operations is going to suffer.

The simplest option is to use the Spring-provided SingleConnectionFactory as a decorator for your target ConnectionFactory, reusing a single JMS Connection in a thread-safe fashion; this is often good enough for the purpose of sending messages via this template.

In a J2EE environment, make sure that the ConnectionFactory is obtained from the application’s environment naming context via JNDI; application servers typically expose pooled, transaction-aware factories there.”

JmsTemplate code uses several anti-patterns like creating a new connection, session, producer/consumer just to send/receive a message, then closing them again. It’s designed for use in EJBs using the EJB containers’ JMS pooling abstraction. So, without pooling, this’s probably one of the worst ways of working with JMS.

Now, lets come to the solution for JMS MessageProducer. We’ll see about using MessageListener in place of MessageConsumer in the next post.

If you’ve written code this way for MessageProducer based on JmsTemplate (see below – Problematic code), it can easily be fixed by the mentioned approach using a CachedConnectionFactory:

I’m assuming you’re using ActiveMQ as Message Broker, but this applies for any broker.

Problematic Code


...
<!-- A connection to ActiveMQ -->
<bean id="amqConnectionFactory"
    class="org.apache.activemq.ActiveMQConnectionFactory"
    p:brokerURL='tcp://localhost:61616" />

<!-- A destination in ActiveMQ -->
<bean id="destination"
    class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="FOO.TEST" />
</bean>

<!-- A JmsTemplate instance that uses the uncached connection and destination -->
<bean id="producerTemplate"
    class="org.springframework.jms.core.JmsTemplate"
    p:connectionFactory-ref="amqConnectionFactory"
    p:defaultDestination-ref="destination" />
...

Fix: Using CachingConnectionFactory


...
<!-- A connection to ActiveMQ -->
<bean id="amqConnectionFactory"
    class="org.apache.activemq.ActiveMQConnectionFactory"
    p:brokerURL='tcp://localhost:61616" />

<!-- A cached connection to wrap the ActiveMQ connection -->
<bean id="cachedConnectionFactory"
    class="org.springframework.jms.connection.CachingConnectionFactory"
    p:targetConnectionFactory-ref="amqConnectionFactory"
    p:sessionCacheSize="10" />

<!-- A destination in ActiveMQ -->
<bean id="destination"
    class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="FOO.TEST" />
</bean>

<!-- A JmsTemplate instance that uses the cached connection and destination -->
<bean id="producerTemplate"
    class="org.springframework.jms.core.JmsTemplate"
    p:connectionFactory-ref="cachedConnectionFactory"
    p:defaultDestination-ref="destination" />
...

From the API docs for org.springframework.jms.connection.CachingConnectionFactory,

This is a SingleConnectionFactory subclass that adds Session caching as well MessageProducer caching. This ConnectionFactory also switches the “reconnectOnException” property to “true” by default, allowing for automatic recovery of the underlying Connection.

By default, only one single Session will be cached, with further requested Sessions being created and disposed on demand. Consider raising the “sessionCacheSize” value in case of a high-concurrency environment.

This ConnectionFactory requires explicit closing of all Sessions obtained from its shared Connection. This is the usual recommendation for native JMS access code anyway. However, with this ConnectionFactory, its use is mandatory in order to actually allow for Session reuse.

Note also that MessageConsumers obtained from a cached Session won’t get closed until the Session will eventually be removed from the pool. This may lead to semantic side effects in some cases. For a durable subscriber, the logical Session.close() call will also close the subscription. Re-registering a durable consumer for the same subscription on the same Session handle is not supported; close and reobtain a cached Session first.

About these ads

One comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 27 other followers

%d bloggers like this: