JmsTemplate and Acknowledge
Hey Spring Team first just want to say Spring really ROCKS!
I am working on it with my first project and it really makes wiring up an application & testing easy...
But now a question (potential enhancement) for JmsTemplate.
Scenario:
I have an application that uses JMS to manage a message as it is processed by various agents. An agent within the application needs to read a message off Q1, do some processing, write the message to Q2, then acknowledge the message from Q1.
While I know that the resources for the JMS session are locked until the ack is sent (and the time here is dependent on the processing we do) this application needs to make sure that messages are not lost as they move from one Q to another. If the system were to have any issues/crash during processing we would lose the message since it was not on any of the Qs.
JmsTemplate:
I started looking at using Spring's JmsTemplate to handle our JMS needs. I noticed however that this class does not provide any public methods to acknowledge (or commit/rollback) a message even though the user of the class can call setSessionTransacted or setSessionAcknowledgeMode.
When I looked at the source code for JmsTemplate I noticed that the variable used by setSessionAcknowledgeMode (sessionAcknowledgeMode) is not used to determine when or if to call the Message.acknowledge method. Instead the doReceive method of JmsTemplate calls the Message.acknowledge after a successful consumer.receive() call.
In our scenario above the message would be acknowledged off Q1 before being processed or written to Q2 (even when setting CLIENT_ACKNOWLEDGE) and would leave the potential of losing the message in our given scenario.
I think that this topic in the Spring forums started to address this problem, seems that sessionAcknowledgeMode was going to be checked and the call to Message.acknowledge() was going to be called when set to CLIENT_ACKNOWLEDGE to bypass a problem calling acknowledge when setting mode to AUTO_ACKNOWLEDGE in WebSphereMQ.
http://forum.springframework.org/vie...ht=acknowledge
However I think that if sessionAcknowledgeMode is set to CLIENT_ACKNOWLEDGE then the JmsTemplate should not call message.acknowledge at all but provide the capability of the user of JmsTemplate to call a public method that does this.
What do you think?
Thanks,
Mike
consuming and publishing with Spring
I'd recommend you use Jencks with Spring
http://jencks.org/
You can then setup Jencks to do the inbound message consumption for you...
http://jencks.org/Message+Driven+POJOs
which has the added benefit of performing connection, session & thread pooling, handling concurrent processing and exception handling and it can work with regular JMS transactions or with full XA if you need it.
Then use the outbound pooling of Jencks...
http://jencks.org/Outbound+JMS
along with, if you wish, the JmsTemplate helper class. Then you can get inbound & outbound messaging all in a single JMS transaction done for you declaratively - or you can move to full XA if you need it (e.g. if you are using 2 different JMS providers or you want to include some JDBC operations in the XA).
use JmsTransactionManager
Hi,
I had a similar problem, but figured out that jmsTemplate.receive() was opening and closing the session for me.
once the session is closed, you can't message.acknowledge(), it's too late.
you have 2 options:
- write your own SessionCallback implementation (look in jmsTemplate for examples) and insert your code and your msg.acknowledge() before returning from your SessionCallback.doInJms() implementation
- use JmsTransactionManager
Code:
<!-- for a queueConnectionFactory with jms 1.0 -->
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager102">
<property name="connectionFactory">
<ref bean="jmsQueueConnectionFactory"/>
</property>
<property name="pubSubDomain">
<value>false</value>
</property>
</bean>
Code:
JmsTransactionManager transactionManager = (JmsTransactionManager ) appContext.getBean("jmsTransactionManager");
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
Message msg = jmsTemplate.receive(queueName);
if (msg != null) {
// do stuff...
msg.acknowledge(); //session is still open within the transaction
}
transactionManager.commit(status);
Personnaly I prefer the transactionManager approach
cheers,
Patrick