Issue
I am trying to get my head around the Spring CachingConnectionFactory
.
href="https://activemq.apache.org/jmstemplate-gotchas.html" rel="nofollow noreferrer">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?
Solution
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
).
Answered By - Gary Russell
Answer Checked By - Marilyn (JavaFixing Volunteer)