1

I am trying to get my head around the Spring CachingConnectionFactory.

ActiveMQ documentation recommends when using JmsTemplate that a Spring CachingConnectionFactory or an ActiveMQ PooledConnectionFactory is used as the connection factory implementation.

I understand this because using the normal ConnectionFactory a connection is created, a session started, and both are closed for EVERY call of the jmsTemplate.send() which is very wasteful.

So I am trying to implement a custom JmsTemplate bean with a CachingConnectionFactory for use where I may have many requests that are A) Persisted to DB B) Enqueued JMS.

@Configuration
public class JMSConfig {
    
    @Autowired
    private CachingConnectionFactory cachingConnectionFactory;

    @Bean
    @Qualifier("jmsTemplateCached")
    public JmsTemplate jmsTemplateCachingConnectionFactory() {
        cachingConnectionFactory.setSessionCacheSize(10);
        
        JmsTemplate jmsTemplate = new JmsTemplate();
        jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);
        jmsTemplate.setSessionAcknowledgeMode(JmsProperties.AcknowledgeMode.CLIENT.getMode());
        jmsTemplate.setSessionTransacted(true);
        jmsTemplate.setDeliveryPersistent(true);
        jmsTemplate.setConnectionFactory(cachingConnectionFactory);
        return jmsTemplate;
    }
}

My first question regards the Spring Docs For CachngConnecionFactory which say:

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.

But then in bold:

NOTE: 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.

Does this mean I only need to close sessions if I create a connection "manually" via the template or my CachingConnectionFactory bean? In other words like:

    Connection connection = jmsTemplateCached.getConnectionFactory().createConnection();

    Session sess = connection.createSession(true, JmsProperties.AcknowledgeMode.CLIENT.getMode());

    MessageProducer producer = sess.createProducer(activeMQQueue);

    try {
        producer.send(activeMQQueue, new ActiveMQTextMessage());
        sess.commit();
    } catch (JMSException e) {
        sess.rollback();
    } finally {
        sess.close();
    }

If I use the template like below, should I close or not close the session?

    @Autowired
    public JmsTemplate jmsTemplateCached;

    @Transactional
    public InboundResponse peristAndEnqueueForProcessing(InboundForm  inboundForm) throws IrresolvableException, JsonProcessingException, JMSException {

    //Removed for clarity, an entity has been persisted and is then to be enqueued via JMS.

        log.debug("Queue For Processing : {}", persistedRequest);

        String serialisedMessage = objectMapper.writeValueAsString(persistedRequest);
        ActiveMQTextMessage activeMQTextMessage = new ActiveMQTextMessage();
        activeMQTextMessage.setText(serialisedMessage);

        //Will throw JMS Exception on failure
        Session sessionUsed = jmsTemplateCached.execute((session, messageProducer) -> {
            messageProducer.send(activeMQQueue, activeMQTextMessage);
            session.commit();
            return session;
        });

        return response;
    }

Secondly, if the above jmsTemplate.execute() throws an exception, what happens to the session? Will it rollback after x time?

Jcov
  • 2,122
  • 2
  • 21
  • 32

1 Answers1

1

The JmsTemplate reliably closes its resources after each operation (returning the session to the cache), including execute().

That comment is related to user code using sessions directly; the close operation is intercepted and used to return the session to the cache, instead of actually closing it. You MUST call close, otherwise the session will be orphaned.

Yes, the transaction will roll back (immediately) if its sessionTransacted is true.

You should NOT call commit - the template will do that when execute exits normally (if it is sessionTransacted).

Gary Russell
  • 166,535
  • 14
  • 146
  • 179