Java – JMS message redelivery on exception in JMS listener

javaspringspring-jms

Javadoc for org.springframework.jms.listener.AbstractMessageListenerContainer states, that if

"sessionAcknowledgeMode" set to "CLIENT_ACKNOWLEDGE": Automatic message acknowledgment after successful listener execution; no redelivery in case of exception thrown.

I guess, "no redelivery in case of exception thrown" means, that message would not be redelivered (so, my guess, it would be acknowledged), even if there's an exception thrown in the jms listener. But, well, exception thrown from listener means that call to it wasn't successful, and there should be redelivery due to no acknowledgement.

The question is:
What actually should happen with message acknowledgement in case of exception thrown in the jms listener?

What really happens, might be seen from this stacktrace:

at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:98)
at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:66)
at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:660)
at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:620)
at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:591)
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:308)
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:246)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1142)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1134)
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1031)

Line 5 of the stacktrace is of particular interest. Code there basically means, that (mostly) whatever exception thrown from the listener will bypass the ackowledgement which is done in org.springframework.jms.listener.AbstractMessageListenerContainer#commitIfNecessary.

That's ok, but what does "no redelivery in case of exception thrown" mean then?

Additional info:
spring-jms:4.1.2

<bean id="someListenerContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="concurrency" value="1-10"/>
    <property name="sessionAcknowledgeMode">
        <util:constant static-field="javax.jms.Session.CLIENT_ACKNOWLEDGE"/>
    </property>
</bean>

Best Answer

It depends on which listener container you use; when using AUTO ack mode, the SimpleMessageListenerContainer acks after the listener returns (i.e. a traditional JMS MessageListener). The DefaultMessageListenerContainer acks before the listener is invoked, so you need acknowledgeMode="transacted" to prevent message loss.

The javadocs in this area were a bit misleading and have been improved recently.

With CLIENT_ACKNOWLEDGE, you're on your own to do the acks. That doc just means you are at the whim of the broker. According to the JMS message javadoc:

Messages that have been received but not acknowledged may be redelivered

In my experience it is best to use auto ack with an SMLC and transactions with DMLC.

Related Topic