7

We're trying to set a re-delivery policy for ActiveMQ using spring jms. We've set an exponential back-off for the re-deliveries, but it seems to be ignored - the intervals between the message re-deliveries are fixed instead of exponentially growing.

Does anyone know what might be the problem? This is our spring-jms configuration:

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
    p:brokerURL="${activemq_url}">
    <property name="redeliveryPolicy" ref="redeliveryPolicy" />
</bean>

<bean id="redeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
    <property name="queue" value="*" />
    <property name="initialRedeliveryDelay" value="10000" />
    <property name="redeliveryDelay" value="10000" />
    <property name="maximumRedeliveries" value="-1" />
    <property name="useExponentialBackOff" value="true" />
    <property name="backOffMultiplier" value="5" />
</bean>

<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory" 
    p:targetConnectionFactory-ref="connectionFactory" p:sessionCacheSize="10" 
    />

<!-- A JmsTemplate instance that uses the cached connection and destination -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="cachingConnectionFactory" />
    <property name="messageConverter" ref="messageConverter" />
    <property name="sessionTransacted" value="true" />
</bean>

<!-- The Spring message listener container configuration -->
<jms:listener-container container-type="default"
    destination-type="queue" connection-factory="connectionFactory"
    acknowledge="transacted" concurrency="1" cache="consumer">
    <jms:listener destination="testQueue" ref="testService"
        method="onMessage" />
</jms:listener-container>

Thanks!

EDIT: This is a log example, the re-deliveries happen every 5 seconds:

11 May 2014 18:52:00  WARN DefaultMessageListenerContainer - Execution of JMS message listener failed, and no ErrorHandler has been set.
javax.jms.JMSException: Sun May 11 18:52:00 IDT 2014
at ...
11 May 2014 18:52:05  WARN DefaultMessageListenerContainer - Execution of JMS message listener failed, and no ErrorHandler has been set.
javax.jms.JMSException: Sun May 11 18:52:05 IDT 2014
at ...
11 May 2014 18:52:10  WARN DefaultMessageListenerContainer - Execution of JMS message listener failed, and no ErrorHandler has been set.
javax.jms.JMSException: Sun May 11 18:52:10 IDT 2014
at ...
11 May 2014 18:52:15  WARN DefaultMessageListenerContainer - Execution of JMS message listener failed, and no ErrorHandler has been set.
javax.jms.JMSException: Sun May 11 18:52:15 IDT 2014
at ...
11 May 2014 18:52:20  WARN DefaultMessageListenerContainer - Execution of JMS message listener failed, and no ErrorHandler has been set.
javax.jms.JMSException: Sun May 11 18:52:20 IDT 2014
at ...
Ayelet
  • 1,713
  • 9
  • 29
  • 43

2 Answers2

2

So, I think I found the problem: When I was testing the policy before, I threw JMSException to get the messages to be re-delivered.

One I changed the exception that was thrown to Exception/RuntimeException, the exponential back off worked.

I'm not sure why JMSException causes the exponential back off policy to be ignored...Does anyone have any ideas?

Ayelet
  • 1,713
  • 9
  • 29
  • 43
  • I believe the JMS Exceptions are assumed to be related to the JMS connection and not the business case you have at hand. Thus on a JMS exception it should retry indefinitely whereas if there is another type of exception it should stop as per the RedeliveryPolicy. – Christoffer Soop Dec 03 '15 at 17:46
  • Probably because JMSException is a checked exception; `@Transactional` only rollbacks runtime exception by default, you need to configure that yourself. See also [@Transactional won't rollback](https://stackoverflow.com/questions/73897990/spring-boot-jdbc-transactional-wont-rollback). – Jacob van Lingen May 22 '23 at 09:37
0

To make @tyfyh's comment stand out a little more, let's expand it as an answer. By setting the cache level to CACHE_CONSUMER, it means that the JMS consumer (message receiver) is cached, allowing it to be reused for multiple message deliveries. This caching behavior can impact the redelivery behavior and handling of exceptions, and a such fix above problem.

You can define the DefaultJmsListenerContainerFactory yourself:

@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(PlatformTransactionManager txManager, ConnectionFactory connectionFactory) {
  var factory = new DefaultJmsListenerContainerFactory();
  factory.setTransactionManager(txManager);
  factory.setConnectionFactory(connectionFactory);
  factory.setCacheLevel(DefaultMessageListenerContainer.CACHE_CONSUMER);

  return factory;
}

Or use existing bean and just set the cache level:

@Configuration
public class JmsListenerConfig {

  private final DefaultJmsListenerContainerFactory factory;

  public JmsListenerConfig(DefaultJmsListenerContainerFactory factory) {
    this.factory = factory;
  }

  @PostConstruct
  public void customizeJmsListenerContainerFactory() {
    factory.setCacheLevel(DefaultMessageListenerContainer.CACHE_CONSUMER);
  }
}
Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78