Add back-off attribute to JMS namespace
This commit adds a "back-off" attribute to the jms:listener-container element so that a BackOff instance can be provided for users of the XML namespace. Issue: SPR-11746
This commit is contained in:
parent
6a0483128a
commit
49040a2925
|
@ -54,6 +54,8 @@ class JmsListenerContainerParser extends AbstractListenerContainerParser {
|
||||||
|
|
||||||
private static final String RECOVERY_INTERVAL_ATTRIBUTE = "recovery-interval";
|
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) {
|
protected PropertyValues parseProperties(Element containerEle, ParserContext parserContext) {
|
||||||
final MutablePropertyValues properties = new MutablePropertyValues();
|
final MutablePropertyValues properties = new MutablePropertyValues();
|
||||||
|
@ -223,10 +225,18 @@ class JmsListenerContainerParser extends AbstractListenerContainerParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String recoveryInterval = containerEle.getAttribute(RECOVERY_INTERVAL_ATTRIBUTE);
|
String backOffBeanName = containerEle.getAttribute(BACK_OFF_ATTRIBUTE);
|
||||||
if (StringUtils.hasText(recoveryInterval)) {
|
if (StringUtils.hasText(backOffBeanName)) {
|
||||||
if (!isSimpleContainer) {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,6 +224,8 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
|
||||||
* between recovery attempts. If the {@link BackOff} implementation
|
* between recovery attempts. If the {@link BackOff} implementation
|
||||||
* returns {@link BackOff#STOP}, this listener container will not further
|
* returns {@link BackOff#STOP}, this listener container will not further
|
||||||
* attempt to recover.
|
* attempt to recover.
|
||||||
|
* <p>The {@link #setRecoveryInterval(long) recovery interval} is ignored
|
||||||
|
* when this property is set.
|
||||||
*/
|
*/
|
||||||
public void setBackOff(BackOff backOff) {
|
public void setBackOff(BackOff backOff) {
|
||||||
this.backOff = backOff;
|
this.backOff = backOff;
|
||||||
|
@ -231,9 +233,10 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify the interval between recovery attempts, in <b>milliseconds</b>.
|
* Specify the interval between recovery attempts, in <b>milliseconds</b>.
|
||||||
* The default is 5000 ms, that is, 5 seconds.
|
* The default is 5000 ms, that is, 5 seconds. This is a convenience method
|
||||||
* <p>This is a convenience method to create a {@link FixedBackOff} with
|
* to create a {@link FixedBackOff} with the specified interval.
|
||||||
* the specified interval.
|
* <p>For more recovery options, consider specifying a {@link BackOff}
|
||||||
|
* instance instead.
|
||||||
* @see #setBackOff(BackOff)
|
* @see #setBackOff(BackOff)
|
||||||
* @see #handleListenerSetupFailure
|
* @see #handleListenerSetupFailure
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -318,11 +318,29 @@
|
||||||
]]></xsd:documentation>
|
]]></xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="back-off" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
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
|
||||||
|
ignored when this property is set. The default is a FixedBackOff with an
|
||||||
|
interval of 5000 ms, that is 5 seconds.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation kind="ref">
|
||||||
|
<tool:expected-type type="org.springframework.util.BackOff"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
<xsd:attribute name="recovery-interval" type="xsd:string">
|
<xsd:attribute name="recovery-interval" type="xsd:string">
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
<xsd:documentation><![CDATA[
|
<xsd:documentation><![CDATA[
|
||||||
Specify the interval between recovery attempts, in milliseconds.
|
Specify the interval between recovery attempts, in milliseconds. Convenience
|
||||||
The default is 5000 ms, that is, 5 seconds.
|
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.
|
||||||
]]></xsd:documentation>
|
]]></xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
|
|
|
@ -161,6 +161,7 @@ public class JmsNamespaceHandlerTests {
|
||||||
assertEquals("wrong concurrency", 3, container.getConcurrentConsumers());
|
assertEquals("wrong concurrency", 3, container.getConcurrentConsumers());
|
||||||
assertEquals("wrong concurrency", 5, container.getMaxConcurrentConsumers());
|
assertEquals("wrong concurrency", 5, container.getMaxConcurrentConsumers());
|
||||||
assertEquals("wrong prefetch", 50, container.getMaxMessagesPerTask());
|
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());
|
assertEquals("phase cannot be customized by the factory", Integer.MAX_VALUE, container.getPhase());
|
||||||
}
|
}
|
||||||
|
@ -216,12 +217,13 @@ public class JmsNamespaceHandlerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRecoveryInterval() {
|
public void testRecoveryInterval() {
|
||||||
long recoveryInterval1 = getRecoveryInterval("listener1");
|
Object testBackOff = context.getBean("testBackOff");
|
||||||
long recoveryInterval2 = getRecoveryInterval("listener2");
|
BackOff backOff1 = getBackOff("listener1");
|
||||||
|
BackOff backOff2 = getBackOff("listener2");
|
||||||
long recoveryInterval3 = getRecoveryInterval(DefaultMessageListenerContainer.class.getName() + "#0");
|
long recoveryInterval3 = getRecoveryInterval(DefaultMessageListenerContainer.class.getName() + "#0");
|
||||||
|
|
||||||
assertEquals(1000L, recoveryInterval1);
|
assertSame(testBackOff, backOff1);
|
||||||
assertEquals(1000L, recoveryInterval2);
|
assertSame(testBackOff, backOff2);
|
||||||
assertEquals(DefaultMessageListenerContainer.DEFAULT_RECOVERY_INTERVAL, recoveryInterval3);
|
assertEquals(DefaultMessageListenerContainer.DEFAULT_RECOVERY_INTERVAL, recoveryInterval3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,9 +302,13 @@ public class JmsNamespaceHandlerTests {
|
||||||
return (ErrorHandler) new DirectFieldAccessor(container).getPropertyValue("errorHandler");
|
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);
|
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());
|
assertEquals(FixedBackOff.class, backOff.getClass());
|
||||||
return ((FixedBackOff)backOff).getInterval();
|
return ((FixedBackOff)backOff).getInterval();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
connection-factory="testConnectionFactory" task-executor="testTaskExecutor"
|
connection-factory="testConnectionFactory" task-executor="testTaskExecutor"
|
||||||
destination-resolver="testDestinationResolver" message-converter="testMessageConverter"
|
destination-resolver="testDestinationResolver" message-converter="testMessageConverter"
|
||||||
transaction-manager="testTransactionManager" error-handler="testErrorHandler"
|
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">
|
||||||
<jms:listener id="listener1" destination="testDestination" ref="testBean1" method="setName"/>
|
<jms:listener id="listener1" destination="testDestination" ref="testBean1" method="setName"/>
|
||||||
<jms:listener id="listener2" destination="testDestination" ref="testBean2" method="setName" response-destination="responseDestination"/>
|
<jms:listener id="listener2" destination="testDestination" ref="testBean2" method="setName" response-destination="responseDestination"/>
|
||||||
</jms:listener-container>
|
</jms:listener-container>
|
||||||
|
@ -43,7 +43,8 @@
|
||||||
connection-factory="testConnectionFactory" task-executor="testTaskExecutor"
|
connection-factory="testConnectionFactory" task-executor="testTaskExecutor"
|
||||||
destination-resolver="testDestinationResolver" message-converter="testMessageConverter"
|
destination-resolver="testDestinationResolver" message-converter="testMessageConverter"
|
||||||
transaction-manager="testTransactionManager" error-handler="testErrorHandler"
|
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"/>
|
||||||
|
|
||||||
<!-- the default ConnectionFactory -->
|
<!-- the default ConnectionFactory -->
|
||||||
<bean id="connectionFactory" class="org.springframework.jms.StubConnectionFactory"/>
|
<bean id="connectionFactory" class="org.springframework.jms.StubConnectionFactory"/>
|
||||||
|
@ -67,6 +68,10 @@
|
||||||
|
|
||||||
<bean id="testErrorHandler" class="org.springframework.jms.config.JmsNamespaceHandlerTests$TestErrorHandler"/>
|
<bean id="testErrorHandler" class="org.springframework.jms.config.JmsNamespaceHandlerTests$TestErrorHandler"/>
|
||||||
|
|
||||||
|
<bean id="testBackOff" class="org.springframework.util.FixedBackOff">
|
||||||
|
<property name="interval" value="1000"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="testBean1" class="org.springframework.tests.sample.beans.TestBean"/>
|
<bean id="testBean1" class="org.springframework.tests.sample.beans.TestBean"/>
|
||||||
|
|
||||||
<bean id="testBean2" class="org.springframework.tests.sample.beans.TestBean"/>
|
<bean id="testBean2" class="org.springframework.tests.sample.beans.TestBean"/>
|
||||||
|
|
|
@ -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
|
provider, advanced functionality such as transaction participation, and compatibility
|
||||||
with Java EE environments.
|
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]]
|
[[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
|
| The timeout to use for receive calls (in milliseconds). The default is `1000` ms (1
|
||||||
sec); `-1` indicates no timeout at all.
|
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
|
| recovery-interval
|
||||||
| Specify the interval between recovery attempts, in milliseconds. The default is `5000`
|
| Specify the interval between recovery attempts, in milliseconds. Convenience
|
||||||
ms, that is, 5 seconds.
|
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
|
| phase
|
||||||
| The lifecycle phase within which this container should start and stop. The lower the
|
| The lifecycle phase within which this container should start and stop. The lower the
|
||||||
|
|
Loading…
Reference in New Issue