diff --git a/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerContainerParser.java b/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerContainerParser.java index 0340bcfe7c708adbf2705d023ed1d24d55297ed2..b3a226f156171d4e1a702cd62770f911345c7d84 100644 --- a/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerContainerParser.java +++ b/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerContainerParser.java @@ -54,6 +54,8 @@ class JmsListenerContainerParser extends AbstractListenerContainerParser { private static final String RECOVERY_INTERVAL_ATTRIBUTE = "recovery-interval"; + private static final String BACK_OFF_ATTRIBUTE = "back-off"; + protected PropertyValues parseProperties(Element containerEle, ParserContext parserContext) { final MutablePropertyValues properties = new MutablePropertyValues(); @@ -223,10 +225,18 @@ class JmsListenerContainerParser extends AbstractListenerContainerParser { } } - String recoveryInterval = containerEle.getAttribute(RECOVERY_INTERVAL_ATTRIBUTE); - if (StringUtils.hasText(recoveryInterval)) { + String backOffBeanName = containerEle.getAttribute(BACK_OFF_ATTRIBUTE); + if (StringUtils.hasText(backOffBeanName)) { if (!isSimpleContainer) { - propertyValues.add("recoveryInterval", recoveryInterval); + propertyValues.add("backOff", new RuntimeBeanReference(backOffBeanName)); + } + } + else { // No need to consider this if back-off is set + String recoveryInterval = containerEle.getAttribute(RECOVERY_INTERVAL_ATTRIBUTE); + if (StringUtils.hasText(recoveryInterval)) { + if (!isSimpleContainer) { + propertyValues.add("recoveryInterval", recoveryInterval); + } } } diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index f2ec8dc18e47d66152d4af59a76be4aab939a7d4..f724e593b551dfa9f63cf7d9c8e74eeca7e4f093 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -224,6 +224,8 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe * between recovery attempts. If the {@link BackOff} implementation * returns {@link BackOff#STOP}, this listener container will not further * attempt to recover. + *

The {@link #setRecoveryInterval(long) recovery interval} is ignored + * when this property is set. */ public void setBackOff(BackOff backOff) { this.backOff = backOff; @@ -231,9 +233,10 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe /** * Specify the interval between recovery attempts, in milliseconds. - * The default is 5000 ms, that is, 5 seconds. - *

This is a convenience method to create a {@link FixedBackOff} with - * the specified interval. + * The default is 5000 ms, that is, 5 seconds. This is a convenience method + * to create a {@link FixedBackOff} with the specified interval. + *

For more recovery options, consider specifying a {@link BackOff} + * instance instead. * @see #setBackOff(BackOff) * @see #handleListenerSetupFailure */ diff --git a/spring-jms/src/main/resources/org/springframework/jms/config/spring-jms-4.1.xsd b/spring-jms/src/main/resources/org/springframework/jms/config/spring-jms-4.1.xsd index 740da217f3e7996d4878227a54509714f4833f49..1987af77c4eb29c34c95b6a427bd69f9f14a6676 100644 --- a/spring-jms/src/main/resources/org/springframework/jms/config/spring-jms-4.1.xsd +++ b/spring-jms/src/main/resources/org/springframework/jms/config/spring-jms-4.1.xsd @@ -318,11 +318,29 @@ ]]> + + + + + + + + + + diff --git a/spring-jms/src/test/java/org/springframework/jms/config/JmsNamespaceHandlerTests.java b/spring-jms/src/test/java/org/springframework/jms/config/JmsNamespaceHandlerTests.java index 927b35f706916b56ff4980e7afcdb616ac2f43b4..66517166d9f880a7a41dda098813297539f3c0dc 100644 --- a/spring-jms/src/test/java/org/springframework/jms/config/JmsNamespaceHandlerTests.java +++ b/spring-jms/src/test/java/org/springframework/jms/config/JmsNamespaceHandlerTests.java @@ -161,6 +161,7 @@ public class JmsNamespaceHandlerTests { assertEquals("wrong concurrency", 3, container.getConcurrentConsumers()); assertEquals("wrong concurrency", 5, container.getMaxConcurrentConsumers()); assertEquals("wrong prefetch", 50, container.getMaxMessagesPerTask()); + assertSame(context.getBean("testBackOff"),new DirectFieldAccessor(container).getPropertyValue("backOff")); assertEquals("phase cannot be customized by the factory", Integer.MAX_VALUE, container.getPhase()); } @@ -216,12 +217,13 @@ public class JmsNamespaceHandlerTests { @Test public void testRecoveryInterval() { - long recoveryInterval1 = getRecoveryInterval("listener1"); - long recoveryInterval2 = getRecoveryInterval("listener2"); + Object testBackOff = context.getBean("testBackOff"); + BackOff backOff1 = getBackOff("listener1"); + BackOff backOff2 = getBackOff("listener2"); long recoveryInterval3 = getRecoveryInterval(DefaultMessageListenerContainer.class.getName() + "#0"); - assertEquals(1000L, recoveryInterval1); - assertEquals(1000L, recoveryInterval2); + assertSame(testBackOff, backOff1); + assertSame(testBackOff, backOff2); assertEquals(DefaultMessageListenerContainer.DEFAULT_RECOVERY_INTERVAL, recoveryInterval3); } @@ -300,9 +302,13 @@ public class JmsNamespaceHandlerTests { return (ErrorHandler) new DirectFieldAccessor(container).getPropertyValue("errorHandler"); } - private long getRecoveryInterval(String containerBeanName) { + private BackOff getBackOff(String containerBeanName) { DefaultMessageListenerContainer container = this.context.getBean(containerBeanName, DefaultMessageListenerContainer.class); - BackOff backOff = (BackOff) new DirectFieldAccessor(container).getPropertyValue("backOff"); + return (BackOff) new DirectFieldAccessor(container).getPropertyValue("backOff"); + } + + private long getRecoveryInterval(String containerBeanName) { + BackOff backOff = getBackOff(containerBeanName); assertEquals(FixedBackOff.class, backOff.getClass()); return ((FixedBackOff)backOff).getInterval(); } diff --git a/spring-jms/src/test/resources/org/springframework/jms/config/jmsNamespaceHandlerTests.xml b/spring-jms/src/test/resources/org/springframework/jms/config/jmsNamespaceHandlerTests.xml index 4145be1828092e0e7f043c70ad1fa0ca506f8ba9..2905a33d3ee490cbd37e68a345865a0a5918947c 100644 --- a/spring-jms/src/test/resources/org/springframework/jms/config/jmsNamespaceHandlerTests.xml +++ b/spring-jms/src/test/resources/org/springframework/jms/config/jmsNamespaceHandlerTests.xml @@ -9,7 +9,7 @@ connection-factory="testConnectionFactory" task-executor="testTaskExecutor" destination-resolver="testDestinationResolver" message-converter="testMessageConverter" transaction-manager="testTransactionManager" error-handler="testErrorHandler" - cache="connection" concurrency="3-5" prefetch="50" receive-timeout="100" recovery-interval="1000" phase="99"> + cache="connection" concurrency="3-5" prefetch="50" receive-timeout="100" back-off="testBackOff" phase="99"> @@ -43,7 +43,8 @@ connection-factory="testConnectionFactory" task-executor="testTaskExecutor" destination-resolver="testDestinationResolver" message-converter="testMessageConverter" transaction-manager="testTransactionManager" error-handler="testErrorHandler" - concurrency="3-5" prefetch="50" receive-timeout="100" recovery-interval="1000"/> + concurrency="3-5" prefetch="50" receive-timeout="100" + recovery-interval="1000" back-off="testBackOff"/> @@ -67,6 +68,10 @@ + + + + diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 6858658defab0779d5a6ab6497eda129b0b36303..1d17a8c95d3c05cd64679db046d84f4e7cfce227 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -40424,6 +40424,10 @@ This listener container strikes a good balance between low requirements on the J provider, advanced functionality such as transaction participation, and compatibility with Java EE environments. +This container also has recoverable capabilities when the broker goes down. By default, +a simple `BackOff` implementation retries every 5 seconds. It is possible to specify +a custom `BackOff` implementation for more fine-grained recovery options, see +`ExponentialBackOff` for an example. [[jms-tx]] @@ -41427,9 +41431,18 @@ choices and message redelivery scenarios. | The timeout to use for receive calls (in milliseconds). The default is `1000` ms (1 sec); `-1` indicates no timeout at all. +| back-off +| Specify the `BackOff` instance to use to compute the interval between recovery + attempts. If the `BackOff` implementation returns `BackOff#STOP`, the listener + container will not further attempt to recover. The `recovery-interval value is + is ignored when this property is set. The default is a `FixedBackOff` with an + interval of 5000 ms, that is 5 seconds. + | recovery-interval -| Specify the interval between recovery attempts, in milliseconds. The default is `5000` - ms, that is, 5 seconds. +| Specify the interval between recovery attempts, in milliseconds. Convenience + way to create a `FixedBackOff` with the specified interval. For more recovery + options, consider specifying a BackOff instance instead. The default is 5000 ms, + that is 5 seconds. | phase | The lifecycle phase within which this container should start and stop. The lower the