Managing JMS transaction with Spring

Carlo picture Carlo · Oct 31, 2012 · Viewed 8.7k times · Source

I'm trying to manage JMS transaction with Spring and HornetQ.
This is the code I wrote:

public void receive() {
    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    Message msg = jmsTemplate.receive(queue);
    boolean success = false;
    if (msg != null) {
            try {
               success = handleMessage(msg);
               if (success) { 
                   msg.acknowledge(); // session is still open within the transaction
               }
            } catch (JMSException e) {
                transactionManager.rollback(status);
            }
            if (success)
                transactionManager.commit(status);
            else
                transactionManager.rollback(status):
    }
}

I'm doing a synchronous read from the queue, with timeout set to 0, since I don't wanna block on the read. Because of this I have to check if something was actually received.

This is an excerpt of my applicationContext.xml:

<bean id="inVMConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
        <value>java:/ConnectionFactory</value>
    </property>
</bean>

<bean id="cachedConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
    <property name="targetConnectionFactory" ref="inVMConnectionFactory" />
</bean>

<bean id="producer" class="it.ubiquity.gestoreprofilazione.onweb.OnWebProducer" scope="singleton">
    <property name="queue" ref="retryQueue" />
    <property name="connectionFactory" ref="cachedConnectionFactory" />
</bean>

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="cachedConnectionFactory" />
    <property name="sessionTransacted" value="true" />
    <property name="sessionAcknowledgeMode" value="#{T(javax.jms.Session).CLIENT_ACKNOWLEDGE}" />
    <property name="pubSubDomain" value="false" />
    <property name="receiveTimeout" value="#    {T(org.springframework.jms.core.JmsTemplate).RECEIVE_TIMEOUT_NO_WAIT}" />
</bean>

<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
    <property name="connectionFactory" ref="cachedConnectionFactory" />
</bean>

<bean id="consumer" class="it.ubiquity.gestoreprofilazione.onweb.OnWebConsumer" scope="singleton">
    <property name="queue" ref="retryQueue" />
    <property name="jmsTemplate" ref="jmsTemplate" />
    <property name="transactionManager" ref="jmsTransactionManager" />
</bean>

The problem I have is quite strange: the first time I receive a message, handleMessage fail, so I rollback the transaction. Then nothing more happens. If I check with the JMX console, I can see there's one message on the queue. Now, if I restart JBoss, the messages is receive again and again, as expected.
Maybe there's something wrong with my configuration, but why it works after a reboot?

  • HornetQ 2.2.10
  • JBoss 5.1.0
  • Spring 3.1.2

UPDATE
With debugging enabled I see the first time:

DEBUG [org.springframework.jms.connection.JmsTransactionManager] (baseScheduler-1) Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

and after the first rollback, on subsequent receive and rollback I see:

DEBUG [org.springframework.jms.connection.JmsTransactionManager] (baseScheduler-1) Participating in existing transaction

Instead, after restarting JBoss, I read that the transaction is actually rolled back:

DEBUG [org.springframework.jms.connection.JmsTransactionManager] (baseScheduler-1) Initiating transaction rollback 2012-11-05 09:54:14,436 DEBUG [org.springframework.jms.connection.JmsTransactionManager] (baseScheduler-1) Rolling back JMS transaction on Session

So, why the rollback it's not happening the first time, and as soon I restart the server it happens all the time? What am I doing wrong?

Answer

Carlo picture Carlo · Nov 5, 2012

Ok, finally I've managed to understand my mistake :) In the outer if's else I should have committed the transaction. That's why the mechanism works after restarting JBoss: there is no transaction hanging on.