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 0340bcfe7c..b3a226f156 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 f2ec8dc18e..f724e593b5 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 740da217f3..1987af77c4 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 927b35f706..66517166d9 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 4145be1828..2905a33d3e 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 6858658def..1d17a8c95d 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